summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FreeFileSync/Build/Changelog.txt21
-rw-r--r--FreeFileSync/Build/Help/html/RealtimeSync.html2
-rw-r--r--FreeFileSync/Build/Help/html/Schedule a Batch Job.html4
-rw-r--r--FreeFileSync/Build/Languages/lithuanian.lng98
-rw-r--r--FreeFileSync/Build/Languages/outdated/dutch.lng (renamed from FreeFileSync/Build/Languages/dutch.lng)162
-rw-r--r--FreeFileSync/Build/Languages/outdated/ukrainian.lng (renamed from FreeFileSync/Build/Languages/ukrainian.lng)162
-rw-r--r--FreeFileSync/Build/Languages/polish.lng66
-rw-r--r--FreeFileSync/Build/Languages/turkish.lng66
-rw-r--r--FreeFileSync/Source/RealtimeSync/application.cpp4
-rw-r--r--FreeFileSync/Source/application.cpp4
-rw-r--r--FreeFileSync/Source/lib/dir_lock.cpp2
-rw-r--r--FreeFileSync/Source/lib/icon_buffer.cpp10
-rw-r--r--FreeFileSync/Source/lib/osx_file_icon.h36
-rw-r--r--FreeFileSync/Source/lib/osx_file_icon.mm179
-rw-r--r--FreeFileSync/Source/lib/process_xml.h2
-rw-r--r--FreeFileSync/Source/lib/shadow.cpp128
-rw-r--r--FreeFileSync/Source/lib/shadow.h36
-rw-r--r--FreeFileSync/Source/synchronization.cpp174
-rw-r--r--FreeFileSync/Source/ui/batch_status_handler.cpp4
-rw-r--r--FreeFileSync/Source/ui/custom_grid.cpp87
-rw-r--r--FreeFileSync/Source/ui/dir_name.cpp2
-rw-r--r--FreeFileSync/Source/ui/gui_status_handler.cpp4
-rw-r--r--FreeFileSync/Source/ui/main_dlg.cpp22
-rw-r--r--FreeFileSync/Source/ui/main_dlg.h2
-rw-r--r--FreeFileSync/Source/ui/osx_dock.h17
-rw-r--r--FreeFileSync/Source/ui/osx_dock.mm24
-rw-r--r--FreeFileSync/Source/ui/progress_indicator.cpp42
-rw-r--r--FreeFileSync/Source/ui/taskbar.cpp18
-rw-r--r--FreeFileSync/Source/ui/tray_icon.cpp2
-rw-r--r--FreeFileSync/Source/ui/tree_view.cpp2
-rw-r--r--FreeFileSync/Source/version/version.h2
-rw-r--r--wx+/graph.cpp34
-rw-r--r--wx+/grid.cpp22
-rw-r--r--wx+/grid.h8
-rw-r--r--zen/basic_math.h8
-rw-r--r--zen/dll.h121
-rw-r--r--zen/file_error.h4
-rw-r--r--zen/i18n.h4
-rw-r--r--zen/osx_string.h82
-rw-r--r--zen/osx_throw_exception.h56
-rw-r--r--zen/privilege.cpp144
-rw-r--r--zen/privilege.h17
-rw-r--r--zen/symlink_target.h14
-rw-r--r--zen/win_ver.h86
44 files changed, 1456 insertions, 528 deletions
diff --git a/FreeFileSync/Build/Changelog.txt b/FreeFileSync/Build/Changelog.txt
index a2b21734..5b631823 100644
--- a/FreeFileSync/Build/Changelog.txt
+++ b/FreeFileSync/Build/Changelog.txt
@@ -1,6 +1,21 @@
-FreeFileSync 6.9
-----------------
-Use FreeFileSync taskbar icon when available (Windows 7)
+FreeFileSync 6.10
+-----------------
+Fixed crash when accessing recycle bin in compatibility mode (Windows 7, 8)
+Draw middle grid selection irrespective of focus column
+Don't show parts of progress graph if nothing to sync
+Break on missing directories before evaluating warnings
+Ignore leading/trailing whitespace in search panel
+Disable search panel during comparison
+Disable shortkeys during comparison
+Log folder pair only if files are synced
+Fixed number separator formatting for english locale
+Copying locked files now inactive by default
+Show all affected folders when warning about a shared sub folder
+
+
+FreeFileSync 6.9 [2014-09-01]
+-----------------------------
+Reuse FreeFileSync taskbar link when available (Windows 7)
Limit number of retries when creating temporary files
Fixed bitmap rendering issue for high-contrast color schemes
Revised and fixed unclear GUI texts
diff --git a/FreeFileSync/Build/Help/html/RealtimeSync.html b/FreeFileSync/Build/Help/html/RealtimeSync.html
index ca6b764b..6b2e2feb 100644
--- a/FreeFileSync/Build/Help/html/RealtimeSync.html
+++ b/FreeFileSync/Build/Help/html/RealtimeSync.html
@@ -118,7 +118,7 @@ on the stick. RealtimeSync will also trigger each time files are modified in <FO
<h3>Limitations:</h3>
<UL>
<LI>If multiple changes happen at the same time, only the name of the first file is written to variable <b>%changed_file%</b>.
- <LI>While RealtimeSync is executing the command line, monitoring is inactive and changes occurring during this time are lost.
+ <LI>While RealtimeSync is executing the command line, monitoring is inactive and changes occurring during this time are not detected.
</UL>
</BODY>
diff --git a/FreeFileSync/Build/Help/html/Schedule a Batch Job.html b/FreeFileSync/Build/Help/html/Schedule a Batch Job.html
index 46c1221b..efc239e8 100644
--- a/FreeFileSync/Build/Help/html/Schedule a Batch Job.html
+++ b/FreeFileSync/Build/Help/html/Schedule a Batch Job.html
@@ -34,6 +34,10 @@
<LI>If you don't want error or warning messages to interrupt synchronization, set <B>Handle errors</B> to either <B>Ignore</B> or <B>Stop</B>.
<br>&nbsp;
+ <LI>If log files are required, enable <B>Save log</B> and enter a folder path. Additionally FreeFileSync always stores the result of the last
+ synchronization in file <FONT FACE="Courier New, monospace">LastSyncs.log</FONT> (up to a user-defined size, see <A HREF="Expert%20Settings.html">Expert Settings</A>).
+ <br>&nbsp;
+
<LI>Set up the FreeFileSync batch job in your operating system's scheduler:
<BR>&nbsp;
diff --git a/FreeFileSync/Build/Languages/lithuanian.lng b/FreeFileSync/Build/Languages/lithuanian.lng
index 69ac63f8..d8e0573f 100644
--- a/FreeFileSync/Build/Languages/lithuanian.lng
+++ b/FreeFileSync/Build/Languages/lithuanian.lng
@@ -8,19 +8,19 @@
</header>
<source>Both sides have changed since last synchronization.</source>
-<target>Abi pusės buvo pakeistos nuo paskutinio sinchronizavimo.</target>
+<target>Abi pusės buvo pakeistos nuo paskutinio suvienodinimo.</target>
<source>Cannot determine sync-direction:</source>
-<target>Nepavyksta nustatyti sinchronizavimo krypties:</target>
+<target>Nepavyksta nustatyti suvienodinimo krypties:</target>
<source>No change since last synchronization.</source>
-<target>Nėra pakitimo nuo pakutinio sinchronizavimo.</target>
+<target>Nėra pakitimo nuo pakutinio suvienodinimo.</target>
<source>The database entry is not in sync considering current settings.</source>
-<target>Duomenų bazės įrašas nesinchronizuotas remiantis šiais nustatymais.</target>
+<target>Duomenų bazės įrašas nesuvienodintas remiantis šiais nustatymais.</target>
<source>Setting default synchronization directions: Old files will be overwritten with newer files.</source>
-<target>Nustatomos numatytos sinchronizavimo kryptys: Seni failai bus perrašyti naujesniais failais.</target>
+<target>Nustatomos numatytos suvienodinimo kryptys: Seni failai bus perrašyti naujesniais failais.</target>
<source>Checking recycle bin availability for folder %x...</source>
<target>Tikrinamas šiukšliadėžės prieinamumas aplankui %x...</target>
@@ -98,7 +98,7 @@
<target>Nepavyksta rasti šių aplankų:</target>
<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source>
-<target>Jei žinote, kad kiekvienas katalogas yra tuščias, šią klaidą galite ignoruoti. Katalogai bus automatiškai sukurti sinchronizacijos metu.</target>
+<target>Jei žinote, kad kiekvienas katalogas yra tuščias, šią klaidą galite ignoruoti. Katalogai bus automatiškai sukurti suvienodinimo metu.</target>
<source>A folder input field is empty.</source>
<target>Aplanko įvesties laukas yra tuščias.</target>
@@ -107,7 +107,7 @@
<target>Atitinkamas aplankas bus laikomas tuščiu.</target>
<source>The following folder paths are dependent from each other:</source>
-<target></target>
+<target>Sekančios aplankų nuorodos yra priklausomos viena nuo kitos</target>
<source>File %x has an invalid date.</source>
<target>Failas %x turi netinkamą datą.</target>
@@ -131,7 +131,7 @@
<target>Ieškoma simbolinės nuorodos %x</target>
<source>Comparing content of files %x</source>
-<target>Sulyginamas failų turinys %x</target>
+<target>Palyginamas failų turinys %x</target>
<source>Generating file list...</source>
<target>Sukuriamas failų sąrašas...</target>
@@ -218,11 +218,14 @@
<source>%x GB</source>
<target>%x GB</target>
+<source>Cannot load file %x.</source>
+<target>Nepavyksta įkelti failo %x.</target>
+
<source>Database file %x is incompatible.</source>
<target>Duomenų bazė %x yra netinkama.</target>
<source>Initial synchronization:</source>
-<target>Pirminis sinchronizavimas:</target>
+<target>Pirminis suvienodinimas:</target>
<source>Database file %x does not yet exist.</source>
<target>Duomenų bazės failo %x dar nėra.</target>
@@ -322,9 +325,6 @@
<source>Please use FreeFileSync 64-bit version to create shadow copies on this system.</source>
<target>Prašome naudoti 64-bit FreeFileSync versiją, kad sukurti šėšėlines kopijas šioje sistemoje.</target>
-<source>Cannot load file %x.</source>
-<target>Nepavyksta įkelti failo %x.</target>
-
<source>Cannot determine volume name for %x.</source>
<target>Vietos vardo %x nustatyti nepavyko</target>
@@ -347,7 +347,7 @@
<target>&Išeiti</target>
<source>&File</source>
-<target></target>
+<target>&Failas</target>
<source>&View help</source>
<target>&Rodyti pagalbą</target>
@@ -501,26 +501,26 @@ Komanda inicijuojama jei:
<source>Data verification error: %x and %y have different content.</source>
<target>Duomenų patikros klaida: %x ir %y turinys yra skirtingas.</target>
-<source>Cannot find folder %x.</source>
-<target></target>
-
<source>Target folder %x already existing.</source>
<target>Tikslo aplankas %x jau yra.</target>
+<source>Cannot find folder %x.</source>
+<target>Negalima rasti aplanko %x.</target>
+
<source>Target folder input field must not be empty.</source>
<target>Tikslo aplanko įvesties laukas negali būti tuščias.</target>
-<source>Please enter a target folder for versioning.</source>
-<target>Prašome nurodyti aplanką kitoms versijoms</target>
-
<source>Source folder %x not found.</source>
<target>Šaltinio aplankas %x nerastas.</target>
+<source>Please enter a target folder for versioning.</source>
+<target>Prašome nurodyti aplanką kitoms versijoms</target>
+
<source>The following items have unresolved conflicts and will not be synchronized:</source>
-<target>Šie elementai turi neišspręstų konfliktų ir nebus sinchronizuoti:</target>
+<target>Šie elementai turi neišspręstų konfliktų ir nebus suvienodinti:</target>
<source>The following folders are significantly different. Make sure you are matching the correct folders for synchronization.</source>
-<target>Šie katalogai yra labai skirtingi. Įsitikinkite, kad sinchronizacijai pasirinkote teisingus katalogus.</target>
+<target>Šie katalogai yra labai skirtingi. Įsitikinkite, kad suvienodinimui pasirinkote teisingus katalogus.</target>
<source>Not enough free disk space available in:</source>
<target>Nepakanka laisvos disko vietos:</target>
@@ -532,7 +532,7 @@ Komanda inicijuojama jei:
<target>Pasiekiama:</target>
<source>Multiple folder pairs write to a common subfolder. Please review your configuration.</source>
-<target></target>
+<target>Aplankai grupuojami į vieną poaplankį. Prašome patikrinti jūsų pasirinkimus</target>
<source>Synchronizing folder pair:</source>
<target>Suvienodinama aplankų pora:</target>
@@ -635,10 +635,10 @@ Komanda inicijuojama jei:
<target>Pavadinimas</target>
<source>Relative folder</source>
-<target></target>
+<target>Aplanai</target>
<source>Base folder</source>
-<target>Bazinis aplankas</target>
+<target>Pasirinktas aplankas</target>
<source>Size</source>
<target>Dydis</target>
@@ -656,7 +656,7 @@ Komanda inicijuojama jei:
<target>Veiksmas</target>
<source>Drag && drop</source>
-<target>Vilkti ir numesti</target>
+<target>Vilkti && numesti</target>
<source>Local comparison settings</source>
<target>Vietiniai palyginimo parametrai</target>
@@ -677,7 +677,7 @@ Komanda inicijuojama jei:
<target>Pašalinti vietinius parametrus</target>
<source>Clear local filter</source>
-<target></target>
+<target>Ištrinti vietos filtrą</target>
<source>Copy</source>
<target>Kopijuoti</target>
@@ -698,10 +698,10 @@ Komanda inicijuojama jei:
<target>Išsaugoti kaip &užduočių paketą...</target>
<source>Start &comparison</source>
-<target></target>
+<target>Pradėti &palyginimą</target>
<source>Start &synchronization</source>
-<target></target>
+<target>Pradėti &suvienodinimą</target>
<source>&Options</source>
<target>&Parinktys</target>
@@ -713,7 +713,7 @@ Komanda inicijuojama jei:
<target>&Ieškoti...</target>
<source>&Reset layout</source>
-<target></target>
+<target>&Išnaujo nustatyti išdėstymą</target>
<source>&Export file list...</source>
<target>&Eksportuoti failų sąrašą...</target>
@@ -734,7 +734,7 @@ Komanda inicijuojama jei:
<target>Atšaukti</target>
<source>Compare</source>
-<target>Sulyginti</target>
+<target>Palyginti</target>
<source>Synchronize</source>
<target>Suvienodinti</target>
@@ -758,7 +758,7 @@ Komanda inicijuojama jei:
<target>Atitikti atveją</target>
<source>New</source>
-<target></target>
+<target>Naujas</target>
<source>Open...</source>
<target>Atversti...</target>
@@ -803,13 +803,13 @@ Komanda inicijuojama jei:
<target>Sutapatinti vienodus failus lyginant failų turinį.</target>
<source>Ignore time shift (in hours)</source>
-<target></target>
+<target>Ignoruoti laiko pasikeitimą (valandomis)</target>
<source>Consider file times with specified offset as equal</source>
-<target></target>
+<target>Failai su tam tikra paklaida skaitomi kaip lygūs</target>
<source>Handle daylight saving time</source>
-<target></target>
+<target>Naudoti vasaros laiką</target>
<source>Symbolic links:</source>
<target>Simbolinės nuorodos:</target>
@@ -842,7 +842,7 @@ Komanda inicijuojama jei:
<target>Didžiausias:</target>
<source>C&lear</source>
-<target></target>
+<target>&Išvalyti</target>
<source>Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair.</source>
<target>Pasirinkti filtro taisykles norint išskirti pasirinktus failus iš Suvienodinimo. Įveskite failo kelią, kuris atitinka aplanką.</target>
@@ -868,7 +868,7 @@ Komanda inicijuojama jei:
<target>Ištrinti failus:</target>
<source>&Permanent</source>
-<target></target>
+<target>&Pastovus</target>
<source>Delete or overwrite files permanently</source>
<target>Trinti ar perrašyti failus visam laikui</target>
@@ -880,7 +880,7 @@ Komanda inicijuojama jei:
<target>Padaryti šiukšlių dėžėje esančių ištrintų ar perrašytų failų atsarginę kopiją</target>
<source>&Versioning</source>
-<target></target>
+<target>&Atsarginės versijos kūrimas</target>
<source>Move files to a user-defined folder</source>
<target>Perkelti failus į vartotojo nustatytą katalogą</target>
@@ -895,7 +895,7 @@ Komanda inicijuojama jei:
<target>Slėpti visus klaidų ir perspėjimų pranešimus</target>
<source>&Pop-up</source>
-<target></target>
+<target>&Iškylančiame</target>
<source>Show pop-up on errors or warnings</source>
<target>Rodyti pranešimą esant klaidoms ar perspėjimams</target>
@@ -931,7 +931,7 @@ Komanda inicijuojama jei:
<target>Nuleisti į apačią dešinėje</target>
<source>Bytes copied:</source>
-<target></target>
+<target>Nukopijuota baitų:</target>
<source>Close</source>
<target>Uždaryti</target>
@@ -946,7 +946,7 @@ Komanda inicijuojama jei:
<target>Sukurti paleidimo failą suvienodinimui be priežiūros. Norint jį paleisti reikia paspausti and failo du kartus pele arba nustatyti su užduočių planuotoju: %x</target>
<source>&Stop</source>
-<target></target>
+<target>&Sustabdyti</target>
<source>Stop synchronization at first error</source>
<target>Stabdyti suvienodinimą, pirmai klaidai įvykus</target>
@@ -1015,10 +1015,10 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<target>Apibūdinimas</target>
<source>Show hidden dialogs again</source>
-<target></target>
+<target>Vėl rodyti paslėptus dialogo langus</target>
<source>Show all permanently hidden dialogs and warning messages again</source>
-<target></target>
+<target>Vėl rodyti visus paslėptus dialogo langus ir įspėjamuosius pranešimus</target>
<source>&Default</source>
<target>&Numatyta</target>
@@ -1081,10 +1081,10 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<target>Pagrindinė įrankinė</target>
<source>Start comparison</source>
-<target></target>
+<target>Pradėti palyginimą</target>
<source>Comparison settings</source>
-<target>Sulyginimo nustatymai</target>
+<target>Palyginimo nustatymai</target>
<source>Synchronization settings</source>
<target>Suvienodinimo nustatymai</target>
@@ -1187,7 +1187,7 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<target>Paskutinė sesija</target>
<source>Folder Comparison and Synchronization</source>
-<target>Aplankų palyginimas ir suvienodinimas</target>
+<target>Aplankų Palyginimas ir Suvienodinimas</target>
<source>Configuration saved</source>
<target>Nustatymai išsaugoti</target>
@@ -1211,7 +1211,7 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<target>Suvienodinimo parametrai</target>
<source>Clear filter</source>
-<target></target>
+<target>Panaikinti filtrą</target>
<source>Show files that exist on left side only</source>
<target>Rodyti failus, kurie egzistuoja tik kairėje pusėje</target>
@@ -1298,7 +1298,7 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<target>Tikrinama...</target>
<source>Comparing content...</source>
-<target>Sulyginamas turinys...</target>
+<target>Palyginimo turinys...</target>
<source>Info</source>
<target>Informacija</target>
@@ -1372,10 +1372,10 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<target>- Kitos pusės atitikmuo %item_folder%</target>
<source>Show hidden dialogs and warning messages again?</source>
-<target></target>
+<target>Ar vėl rodyti visus paslėptus dialogo langus ir įspėjamuosius pranešimus</target>
<source>&Show</source>
-<target></target>
+<target>&Rodyti</target>
<source>Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database.</source>
<target>Nustatyti ir skatinti pokyčius apbiejose pusėse. Trinimai, perkėlimai ir konfliktai yra aptinkami automatiškai naudojant duomenų bazę.</target>
diff --git a/FreeFileSync/Build/Languages/dutch.lng b/FreeFileSync/Build/Languages/outdated/dutch.lng
index 8ef337e1..d9802c29 100644
--- a/FreeFileSync/Build/Languages/dutch.lng
+++ b/FreeFileSync/Build/Languages/outdated/dutch.lng
@@ -7,6 +7,81 @@
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
+<source>&Show</source>
+<target></target>
+
+<source>Show hidden dialogs and warning messages again?</source>
+<target></target>
+
+<source>Clear filter</source>
+<target></target>
+
+<source>Start comparison</source>
+<target></target>
+
+<source>Show all permanently hidden dialogs and warning messages again</source>
+<target></target>
+
+<source>Show hidden dialogs again</source>
+<target></target>
+
+<source>&Stop</source>
+<target></target>
+
+<source>Bytes copied:</source>
+<target></target>
+
+<source>&Pop-up</source>
+<target></target>
+
+<source>&Versioning</source>
+<target></target>
+
+<source>&Permanent</source>
+<target></target>
+
+<source>C&lear</source>
+<target></target>
+
+<source>Handle daylight saving time</source>
+<target></target>
+
+<source>Consider file times with specified offset as equal</source>
+<target></target>
+
+<source>Ignore time shift (in hours)</source>
+<target></target>
+
+<source>New</source>
+<target></target>
+
+<source>&Reset layout</source>
+<target></target>
+
+<source>Start &synchronization</source>
+<target></target>
+
+<source>Start &comparison</source>
+<target></target>
+
+<source>Clear local filter</source>
+<target></target>
+
+<source>Relative folder</source>
+<target></target>
+
+<source>Multiple folder pairs write to a common subfolder. Please review your configuration.</source>
+<target></target>
+
+<source>Cannot find folder %x.</source>
+<target></target>
+
+<source>&File</source>
+<target></target>
+
+<source>The following folder paths are dependent from each other:</source>
+<target></target>
+
<source>Both sides have changed since last synchronization.</source>
<target>Beide zijdes zijn veranderd sinds de laatste synchronisatie.</target>
@@ -106,9 +181,6 @@
<source>The corresponding folder will be considered as empty.</source>
<target>De overeenkomstige map zal als leeg worden beschouwd.</target>
-<source>The following folder paths are dependent from each other:</source>
-<target></target>
-
<source>File %x has an invalid date.</source>
<target>Bestand %x heeft een ongeldige datum.</target>
@@ -217,6 +289,9 @@
<source>%x GB</source>
<target>%x GB</target>
+<source>Cannot load file %x.</source>
+<target>Kan bestand %x niet laden.</target>
+
<source>Database file %x is incompatible.</source>
<target>Databasebestand %x is niet compatibel.</target>
@@ -319,9 +394,6 @@
<source>Please use FreeFileSync 64-bit version to create shadow copies on this system.</source>
<target>Gebruik alstublieft de FreeFileSync 64-bit versie om schaduwkopieën te maken op dit systeem.</target>
-<source>Cannot load file %x.</source>
-<target>Kan bestand %x niet laden.</target>
-
<source>Cannot determine volume name for %x.</source>
<target>Kan volumenaam voor %x niet bepalen.</target>
@@ -343,9 +415,6 @@
<source>&Quit</source>
<target>&Afsluiten</target>
-<source>&File</source>
-<target></target>
-
<source>&View help</source>
<target>Help &bekijken</target>
@@ -498,21 +567,18 @@ De opdracht word geactiveerd als:
<source>Data verification error: %x and %y have different content.</source>
<target>Data verificatie fout: %x en %y hebben een verschillende inhoud.</target>
-<source>Cannot find folder %x.</source>
-<target></target>
-
<source>Target folder %x already existing.</source>
<target>Doelmap %x bestaat al.</target>
<source>Target folder input field must not be empty.</source>
<target>Doelmap mag niet leeg zijn.</target>
-<source>Please enter a target folder for versioning.</source>
-<target>Selecteer alstublieft een doelmap voor versiebeheer.</target>
-
<source>Source folder %x not found.</source>
<target>Bronmap %x niet gevonden.</target>
+<source>Please enter a target folder for versioning.</source>
+<target>Selecteer alstublieft een doelmap voor versiebeheer.</target>
+
<source>The following items have unresolved conflicts and will not be synchronized:</source>
<target>De volgende items hebben onopgeloste conflicten en zullen niet worden gesynchroniseerd:</target>
@@ -528,9 +594,6 @@ De opdracht word geactiveerd als:
<source>Available:</source>
<target>Beschikbaar:</target>
-<source>Multiple folder pairs write to a common subfolder. Please review your configuration.</source>
-<target></target>
-
<source>Synchronizing folder pair:</source>
<target>Bezig met synchroniseren van folder paar:</target>
@@ -630,9 +693,6 @@ De opdracht word geactiveerd als:
<source>Name</source>
<target>Naam</target>
-<source>Relative folder</source>
-<target></target>
-
<source>Base folder</source>
<target>Hoofdmap</target>
@@ -672,9 +732,6 @@ De opdracht word geactiveerd als:
<source>Remove local settings</source>
<target>Verwijder lokale instellingen</target>
-<source>Clear local filter</source>
-<target></target>
-
<source>Copy</source>
<target>Kopiëren</target>
@@ -693,12 +750,6 @@ De opdracht word geactiveerd als:
<source>Save as &batch job...</source>
<target>Opslaan als &batch opdracht...</target>
-<source>Start &comparison</source>
-<target></target>
-
-<source>Start &synchronization</source>
-<target></target>
-
<source>&Options</source>
<target>&Opties</target>
@@ -708,9 +759,6 @@ De opdracht word geactiveerd als:
<source>&Find...</source>
<target>&Zoek...</target>
-<source>&Reset layout</source>
-<target></target>
-
<source>&Export file list...</source>
<target>&Exporteer bestandslijst...</target>
@@ -753,9 +801,6 @@ De opdracht word geactiveerd als:
<source>Match case</source>
<target>Hoofdlettergevoelig</target>
-<source>New</source>
-<target></target>
-
<source>Open...</source>
<target>Open...</target>
@@ -798,15 +843,6 @@ De opdracht word geactiveerd als:
<source>Identify equal files by comparing the file content.</source>
<target>Identificeer gelijke bestanden door de bestandsinhoud te vergelijken.</target>
-<source>Ignore time shift (in hours)</source>
-<target></target>
-
-<source>Consider file times with specified offset as equal</source>
-<target></target>
-
-<source>Handle daylight saving time</source>
-<target></target>
-
<source>Symbolic links:</source>
<target>Snelkoppelingen:</target>
@@ -837,9 +873,6 @@ De opdracht word geactiveerd als:
<source>Maximum:</source>
<target>Maximum:</target>
-<source>C&lear</source>
-<target></target>
-
<source>Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair.</source>
<target>Selecteer filter regels om bepaalde bestanden uit te sluiten van synchronisatie. Geef bestandspaden in die relatief zijn aan hun corresponderende folderparen.</target>
@@ -863,9 +896,6 @@ De opdracht word geactiveerd als:
<source>Delete files:</source>
<target>Verwijder bestanden:</target>
-<source>&Permanent</source>
-<target></target>
-
<source>Delete or overwrite files permanently</source>
<target>Bestanden definitief verwijderen of overschrijven</target>
@@ -875,9 +905,6 @@ De opdracht word geactiveerd als:
<source>Back up deleted and overwritten files in the recycle bin</source>
<target>Maak een backup van verwijderde en overschreven bestanden in de prullenbak</target>
-<source>&Versioning</source>
-<target></target>
-
<source>Move files to a user-defined folder</source>
<target>Verplaats bestanden naar een gebruikers gedefineerde map</target>
@@ -890,9 +917,6 @@ De opdracht word geactiveerd als:
<source>Hide all error and warning messages</source>
<target>Verberg alle fout- en waarschuwingsberichten</target>
-<source>&Pop-up</source>
-<target></target>
-
<source>Show pop-up on errors or warnings</source>
<target>Laat pop-up zien bij foutmeldingen of waarschuwingen</target>
@@ -926,9 +950,6 @@ De opdracht word geactiveerd als:
<source>Minimize to notification area</source>
<target>Minimaliseer naar systeemvak</target>
-<source>Bytes copied:</source>
-<target></target>
-
<source>Close</source>
<target>Sluiten</target>
@@ -941,9 +962,6 @@ De opdracht word geactiveerd als:
<source>Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x</source>
<target>Maak een batchbestand voor onbeheerde synchronisatie. Om te starten dubbelklikt u dit bestand of rooster in een taakplanner: %x</target>
-<source>&Stop</source>
-<target></target>
-
<source>Stop synchronization at first error</source>
<target>Stop synchronisatie bij eerste foutmelding</target>
@@ -1010,12 +1028,6 @@ Dit garandeert een consistente staat, zelfs in het geval van een serieuze fout.
<source>Description</source>
<target>Omschrijving</target>
-<source>Show hidden dialogs again</source>
-<target></target>
-
-<source>Show all permanently hidden dialogs and warning messages again</source>
-<target></target>
-
<source>&Default</source>
<target>&Standaard</target>
@@ -1076,9 +1088,6 @@ Dit garandeert een consistente staat, zelfs in het geval van een serieuze fout.
<source>Main Bar</source>
<target>Hoofdbalk</target>
-<source>Start comparison</source>
-<target></target>
-
<source>Comparison settings</source>
<target>Vergelijksinstellingen</target>
@@ -1202,9 +1211,6 @@ Dit garandeert een consistente staat, zelfs in het geval van een serieuze fout.
<source>Synchronization Settings</source>
<target>Synchronisatie instellingen</target>
-<source>Clear filter</source>
-<target></target>
-
<source>Show files that exist on left side only</source>
<target>Toon bestanden die alleen aan de linkerzijde bestaan</target>
@@ -1361,12 +1367,6 @@ Dit garandeert een consistente staat, zelfs in het geval van een serieuze fout.
<source>- Other side's counterpart to %item_folder%</source>
<target>- Tegenhanger van de andere kant naar %item_folder%</target>
-<source>Show hidden dialogs and warning messages again?</source>
-<target></target>
-
-<source>&Show</source>
-<target></target>
-
<source>Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database.</source>
<target>Identificeer en pas veranderingen toe aan beide kanten. Verwijderingen, verplaatsingen en conflicten worden automatisch gevonden met behulp van een database.</target>
diff --git a/FreeFileSync/Build/Languages/ukrainian.lng b/FreeFileSync/Build/Languages/outdated/ukrainian.lng
index a2a7ea0f..e8314f07 100644
--- a/FreeFileSync/Build/Languages/ukrainian.lng
+++ b/FreeFileSync/Build/Languages/outdated/ukrainian.lng
@@ -7,6 +7,81 @@
<plural_definition>n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2</plural_definition>
</header>
+<source>&Show</source>
+<target></target>
+
+<source>Show hidden dialogs and warning messages again?</source>
+<target></target>
+
+<source>Clear filter</source>
+<target></target>
+
+<source>Start comparison</source>
+<target></target>
+
+<source>Show all permanently hidden dialogs and warning messages again</source>
+<target></target>
+
+<source>Show hidden dialogs again</source>
+<target></target>
+
+<source>&Stop</source>
+<target></target>
+
+<source>Bytes copied:</source>
+<target></target>
+
+<source>&Pop-up</source>
+<target></target>
+
+<source>&Versioning</source>
+<target></target>
+
+<source>&Permanent</source>
+<target></target>
+
+<source>C&lear</source>
+<target></target>
+
+<source>Handle daylight saving time</source>
+<target></target>
+
+<source>Consider file times with specified offset as equal</source>
+<target></target>
+
+<source>Ignore time shift (in hours)</source>
+<target></target>
+
+<source>New</source>
+<target></target>
+
+<source>&Reset layout</source>
+<target></target>
+
+<source>Start &synchronization</source>
+<target></target>
+
+<source>Start &comparison</source>
+<target></target>
+
+<source>Clear local filter</source>
+<target></target>
+
+<source>Relative folder</source>
+<target></target>
+
+<source>Multiple folder pairs write to a common subfolder. Please review your configuration.</source>
+<target></target>
+
+<source>Cannot find folder %x.</source>
+<target></target>
+
+<source>&File</source>
+<target></target>
+
+<source>The following folder paths are dependent from each other:</source>
+<target></target>
+
<source>Both sides have changed since last synchronization.</source>
<target>З моменту останньої синхронізації з обох сторін відбулися зміни.</target>
@@ -109,9 +184,6 @@
<source>The corresponding folder will be considered as empty.</source>
<target>Відповідна папка буде вважатися порожньою.</target>
-<source>The following folder paths are dependent from each other:</source>
-<target></target>
-
<source>File %x has an invalid date.</source>
<target>Файл %x має неіснуючу дату.</target>
@@ -221,6 +293,9 @@
<source>%x GB</source>
<target>%x ГБ</target>
+<source>Cannot load file %x.</source>
+<target>Не вдається завантажити файл %x.</target>
+
<source>Database file %x is incompatible.</source>
<target>Несумісний файл бази даних %x.</target>
@@ -325,9 +400,6 @@
<source>Please use FreeFileSync 64-bit version to create shadow copies on this system.</source>
<target>Будь ласка, використовуйте 64-розрядну версію FreeFileSync для створення тіньових копій у цій системі.</target>
-<source>Cannot load file %x.</source>
-<target>Не вдається завантажити файл %x.</target>
-
<source>Cannot determine volume name for %x.</source>
<target>Не вдалося встановити ім'я тому для %x.</target>
@@ -349,9 +421,6 @@
<source>&Quit</source>
<target>&Вихід</target>
-<source>&File</source>
-<target></target>
-
<source>&View help</source>
<target>&Перегляд довідки</target>
@@ -504,21 +573,18 @@ The command is triggered if:
<source>Data verification error: %x and %y have different content.</source>
<target>Помилка перевірки даних: %x та %y мають різний вміст.</target>
-<source>Cannot find folder %x.</source>
-<target></target>
-
<source>Target folder %x already existing.</source>
<target>Цільова папка %x вже існує.</target>
<source>Target folder input field must not be empty.</source>
<target>Поле цільової папки не повинно бути порожнім.</target>
-<source>Please enter a target folder for versioning.</source>
-<target>Будь ласка, введіть цільову папку для версій.</target>
-
<source>Source folder %x not found.</source>
<target>Вихідний каталог %x не знайдено.</target>
+<source>Please enter a target folder for versioning.</source>
+<target>Будь ласка, введіть цільову папку для версій.</target>
+
<source>The following items have unresolved conflicts and will not be synchronized:</source>
<target>Наступні елементи мають невирішені конфлікти і не будуть синхронізовані:</target>
@@ -534,9 +600,6 @@ The command is triggered if:
<source>Available:</source>
<target>Доступно:</target>
-<source>Multiple folder pairs write to a common subfolder. Please review your configuration.</source>
-<target></target>
-
<source>Synchronizing folder pair:</source>
<target>Синхронізація пари папок:</target>
@@ -637,9 +700,6 @@ The command is triggered if:
<source>Name</source>
<target>Назва</target>
-<source>Relative folder</source>
-<target></target>
-
<source>Base folder</source>
<target>Базова папка</target>
@@ -679,9 +739,6 @@ The command is triggered if:
<source>Remove local settings</source>
<target>Вилучити локальні налаштування</target>
-<source>Clear local filter</source>
-<target></target>
-
<source>Copy</source>
<target>Копіювати</target>
@@ -700,12 +757,6 @@ The command is triggered if:
<source>Save as &batch job...</source>
<target>Зберегти як &пакетне завдання</target>
-<source>Start &comparison</source>
-<target></target>
-
-<source>Start &synchronization</source>
-<target></target>
-
<source>&Options</source>
<target>&Опції</target>
@@ -715,9 +766,6 @@ The command is triggered if:
<source>&Find...</source>
<target>&Знайти...</target>
-<source>&Reset layout</source>
-<target></target>
-
<source>&Export file list...</source>
<target>&Експортувати список файлів...</target>
@@ -760,9 +808,6 @@ The command is triggered if:
<source>Match case</source>
<target>Враховувати регістр</target>
-<source>New</source>
-<target></target>
-
<source>Open...</source>
<target>Відкрити...</target>
@@ -805,15 +850,6 @@ The command is triggered if:
<source>Identify equal files by comparing the file content.</source>
<target>Визначити однакові файли порівнюючи їх вміст.</target>
-<source>Ignore time shift (in hours)</source>
-<target></target>
-
-<source>Consider file times with specified offset as equal</source>
-<target></target>
-
-<source>Handle daylight saving time</source>
-<target></target>
-
<source>Symbolic links:</source>
<target>Символьні посилання:</target>
@@ -844,9 +880,6 @@ The command is triggered if:
<source>Maximum:</source>
<target>Максимум:</target>
-<source>C&lear</source>
-<target></target>
-
<source>Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair.</source>
<target>Виберіть правила фільтрації для виключення деяких файлів із синхронізації. Введіть шляхи файлів відносно відповідної пари папок.</target>
@@ -870,9 +903,6 @@ The command is triggered if:
<source>Delete files:</source>
<target>Вилучити файли:</target>
-<source>&Permanent</source>
-<target></target>
-
<source>Delete or overwrite files permanently</source>
<target>Вилучати чи перезаписати файли назавжди</target>
@@ -882,9 +912,6 @@ The command is triggered if:
<source>Back up deleted and overwritten files in the recycle bin</source>
<target>Резервно зберегти вилучені та перезаисані файли в Корзині</target>
-<source>&Versioning</source>
-<target></target>
-
<source>Move files to a user-defined folder</source>
<target>Перемістити файли у визначену користувачем папку</target>
@@ -897,9 +924,6 @@ The command is triggered if:
<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>
@@ -933,9 +957,6 @@ The command is triggered if:
<source>Minimize to notification area</source>
<target>Згорнути в область повідомлень</target>
-<source>Bytes copied:</source>
-<target></target>
-
<source>Close</source>
<target>Замкнути</target>
@@ -948,9 +969,6 @@ The command is triggered if:
<source>Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x</source>
<target>Створити пакетний файл для автоматичної синхронізації. Щоб розпочати двічі клацніть цей файл або заплануйте в планувальнику завдань: %x</target>
-<source>&Stop</source>
-<target></target>
-
<source>Stop synchronization at first error</source>
<target>Зупинити синхронізацію при першій помилці</target>
@@ -1017,12 +1035,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Description</source>
<target>Опис</target>
-<source>Show hidden dialogs again</source>
-<target></target>
-
-<source>Show all permanently hidden dialogs and warning messages again</source>
-<target></target>
-
<source>&Default</source>
<target>&За замовчуванням</target>
@@ -1083,9 +1095,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Main Bar</source>
<target>Головна панель</target>
-<source>Start comparison</source>
-<target></target>
-
<source>Comparison settings</source>
<target>Налаштування порівнювання</target>
@@ -1213,9 +1222,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Synchronization Settings</source>
<target>Налаштування Синхронізації</target>
-<source>Clear filter</source>
-<target></target>
-
<source>Show files that exist on left side only</source>
<target>Показати файли, які є тільки ліворуч</target>
@@ -1374,12 +1380,6 @@ This guarantees a consistent state even in case of a serious error.
<source>- Other side's counterpart to %item_folder%</source>
<target>- Елемент з протилежної сторони до %item_folder%</target>
-<source>Show hidden dialogs and warning messages again?</source>
-<target></target>
-
-<source>&Show</source>
-<target></target>
-
<source>Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database.</source>
<target>Виявити та поширити зміни на обидві сторони. Видалення, перейменування та конфлікти визначаються автоматично використовуючи базу даних.</target>
diff --git a/FreeFileSync/Build/Languages/polish.lng b/FreeFileSync/Build/Languages/polish.lng
index 306d3f05..6aac661e 100644
--- a/FreeFileSync/Build/Languages/polish.lng
+++ b/FreeFileSync/Build/Languages/polish.lng
@@ -107,7 +107,7 @@
<target>Katalog będzie oznaczony jako pusty.</target>
<source>The following folder paths are dependent from each other:</source>
-<target></target>
+<target>Następujące ścieżki katalogów są od siebie zależne:</target>
<source>File %x has an invalid date.</source>
<target>Plik %x ma nieprawidłową datę.</target>
@@ -218,6 +218,9 @@
<source>%x GB</source>
<target>%x GB</target>
+<source>Cannot load file %x.</source>
+<target>Nie można wczytać pliku %x.</target>
+
<source>Database file %x is incompatible.</source>
<target>Plik bazy danych %x nie jest kompatybilny.</target>
@@ -322,9 +325,6 @@
<source>Please use FreeFileSync 64-bit version to create shadow copies on this system.</source>
<target>Usługa shadow copy jest aktywna tylko w wersji 64 bitowej programu FreeFileSync.</target>
-<source>Cannot load file %x.</source>
-<target>Nie można wczytać pliku %x.</target>
-
<source>Cannot determine volume name for %x.</source>
<target>Nie można określić nazwy dysku dla %x.</target>
@@ -347,7 +347,7 @@
<target>Zam&knij</target>
<source>&File</source>
-<target></target>
+<target>&Plik</target>
<source>&View help</source>
<target>&Pomoc</target>
@@ -501,21 +501,21 @@ Komenda jest wykonywana gdy:
<source>Data verification error: %x and %y have different content.</source>
<target>Nastąpił błąd weryfikacji: %x oraz %y różnią się zawartością.</target>
-<source>Cannot find folder %x.</source>
-<target></target>
-
<source>Target folder %x already existing.</source>
<target>Katalog docelowy %x już istnieje.</target>
+<source>Cannot find folder %x.</source>
+<target>Nie można znaleźć katalogu %x.</target>
+
<source>Target folder input field must not be empty.</source>
<target>Pole katalog docelowy nie może być puste.</target>
-<source>Please enter a target folder for versioning.</source>
-<target>Określ katalog do wersjonowania.</target>
-
<source>Source folder %x not found.</source>
<target>Nie znaleziono katalogu docelowego %x.</target>
+<source>Please enter a target folder for versioning.</source>
+<target>Określ katalog do wersjonowania.</target>
+
<source>The following items have unresolved conflicts and will not be synchronized:</source>
<target>Te elementy znajdują się w konflikcie, którego nie można rozwiązać. Pliki nie zostaną zsynchronizowane:</target>
@@ -532,7 +532,7 @@ Komenda jest wykonywana gdy:
<target>Dostępne:</target>
<source>Multiple folder pairs write to a common subfolder. Please review your configuration.</source>
-<target></target>
+<target>Wiele par katalogów zapisuje do wspólnego podkatalogu. Przejrzyj konfigurację.</target>
<source>Synchronizing folder pair:</source>
<target>Synchronizacja katalgów:</target>
@@ -635,7 +635,7 @@ Komenda jest wykonywana gdy:
<target>Nazwa</target>
<source>Relative folder</source>
-<target></target>
+<target>Relatywny katalog</target>
<source>Base folder</source>
<target>Katalog bazowy</target>
@@ -677,7 +677,7 @@ Komenda jest wykonywana gdy:
<target>Usuń lokalne ustawienia</target>
<source>Clear local filter</source>
-<target></target>
+<target>Wyczyść lokalny filtr</target>
<source>Copy</source>
<target>Kopiuj</target>
@@ -698,10 +698,10 @@ Komenda jest wykonywana gdy:
<target>Zapisz w trybie &wsadowym...</target>
<source>Start &comparison</source>
-<target></target>
+<target>Rozpo&cznij porównywanie</target>
<source>Start &synchronization</source>
-<target></target>
+<target>Rozpocznij &synchronizację</target>
<source>&Options</source>
<target>&Opcje</target>
@@ -713,7 +713,7 @@ Komenda jest wykonywana gdy:
<target>&Szukaj...</target>
<source>&Reset layout</source>
-<target></target>
+<target>&Resetuj układ okien</target>
<source>&Export file list...</source>
<target>&Eksportuj listę plików...</target>
@@ -758,7 +758,7 @@ Komenda jest wykonywana gdy:
<target>Uwzględnij wielkość liter</target>
<source>New</source>
-<target></target>
+<target>Nowy</target>
<source>Open...</source>
<target>Otwórz...</target>
@@ -803,13 +803,13 @@ Komenda jest wykonywana gdy:
<target>Określ różnice w plikach na podstawie ich zawartości.</target>
<source>Ignore time shift (in hours)</source>
-<target></target>
+<target>Ignoruj różnice w czasie (w godzinach)</target>
<source>Consider file times with specified offset as equal</source>
-<target></target>
+<target>Traktuje czasy dwóch plików jako równe w określonych granicach</target>
<source>Handle daylight saving time</source>
-<target></target>
+<target>Uwzględniaj przesunięcie czasu</target>
<source>Symbolic links:</source>
<target>Dowiązania symboliczne:</target>
@@ -842,7 +842,7 @@ Komenda jest wykonywana gdy:
<target>Maksymalny:</target>
<source>C&lear</source>
-<target></target>
+<target>&Wyczyść</target>
<source>Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair.</source>
<target>Określ reguły filtrowania w celu wykluczenia niektórych plików z synchronizacji. Ścieżki plików muszą być relatywne do podanych par katalogów.</target>
@@ -868,7 +868,7 @@ Komenda jest wykonywana gdy:
<target>Usuwanie plików:</target>
<source>&Permanent</source>
-<target></target>
+<target>&Permanentne</target>
<source>Delete or overwrite files permanently</source>
<target>Usuń lub nadpisz pliki na stałe</target>
@@ -880,7 +880,7 @@ Komenda jest wykonywana gdy:
<target>Przechowuj pliki, które zostały usunięte lub nadpisane w koszu.</target>
<source>&Versioning</source>
-<target></target>
+<target>&Wersjonowanie</target>
<source>Move files to a user-defined folder</source>
<target>Przenieś pliki do katalogu zdefiniowanego przez użytkownika</target>
@@ -895,7 +895,7 @@ Komenda jest wykonywana gdy:
<target>Ukryj wszystkie informacje błędach i ostrzeżeniach</target>
<source>&Pop-up</source>
-<target></target>
+<target>&Pop-up</target>
<source>Show pop-up on errors or warnings</source>
<target>Pokazuj okna pop-up dla błędów i ostrzeżeń</target>
@@ -931,7 +931,7 @@ Komenda jest wykonywana gdy:
<target>Minimalizuj do obszaru powiadomień</target>
<source>Bytes copied:</source>
-<target></target>
+<target>Skopiowano bajtów:</target>
<source>Close</source>
<target>Zamknij</target>
@@ -946,7 +946,7 @@ Komenda jest wykonywana gdy:
<target>Utwórz plik wsadowy do zautomatyzowania procesu synchronizacji. Aby rozpocząć, klknij dwa razy plik lub zaplanuj zadanie w harmonogramie zadań: %x</target>
<source>&Stop</source>
-<target></target>
+<target>&Stop</target>
<source>Stop synchronization at first error</source>
<target>Przerwij synchronizację przy pierwszym błędzie</target>
@@ -1015,10 +1015,10 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>Opis</target>
<source>Show hidden dialogs again</source>
-<target></target>
+<target>Przywróć ukryte dialogi</target>
<source>Show all permanently hidden dialogs and warning messages again</source>
-<target></target>
+<target>Przywróć wszystkie, stale ukryte dialogi i powiadomienia.</target>
<source>&Default</source>
<target>&Domyślne</target>
@@ -1081,7 +1081,7 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>Główny pasek</target>
<source>Start comparison</source>
-<target></target>
+<target>Rozpocznij porównywanie</target>
<source>Comparison settings</source>
<target>Ustawienia porównywania</target>
@@ -1211,7 +1211,7 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>Ustawienia synchronizacji</target>
<source>Clear filter</source>
-<target></target>
+<target>Wyczyść filtr</target>
<source>Show files that exist on left side only</source>
<target>Pokaż pliki istniejące tylko po lewej stronie</target>
@@ -1372,10 +1372,10 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>- Odpowiednik %item_folder% po drugiej stronie</target>
<source>Show hidden dialogs and warning messages again?</source>
-<target></target>
+<target>Przywrócić ukryte dialogi i powiadomienia?</target>
<source>&Show</source>
-<target></target>
+<target>&Przywróć</target>
<source>Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database.</source>
<target>Wyszukaj oraz zastosuj zmiany po obu stronach. Wszystkie operacje na plikach takie jak usunięcia, zmiany oraz konflikty wykrywane są automatycznie przy użyciu bazy danych.</target>
diff --git a/FreeFileSync/Build/Languages/turkish.lng b/FreeFileSync/Build/Languages/turkish.lng
index 076ebc56..3dc44c4c 100644
--- a/FreeFileSync/Build/Languages/turkish.lng
+++ b/FreeFileSync/Build/Languages/turkish.lng
@@ -107,7 +107,7 @@
<target>Karşıdaki klasör boş olarak kabul edilecek.</target>
<source>The following folder paths are dependent from each other:</source>
-<target></target>
+<target>Aşağıdaki klasör yolları birbirine bağlı:</target>
<source>File %x has an invalid date.</source>
<target>%x dosyasının tarihi geçersiz.</target>
@@ -217,6 +217,9 @@
<source>%x GB</source>
<target>%x GB</target>
+<source>Cannot load file %x.</source>
+<target>%x dosyası yüklenemedi.</target>
+
<source>Database file %x is incompatible.</source>
<target>%x veritabanı dosyası uyumsuz</target>
@@ -319,9 +322,6 @@
<source>Please use FreeFileSync 64-bit version to create shadow copies on this system.</source>
<target>Bu sistem üzerinde gölge kopyalar oluşturmak için FreeFileSync 64-bit sürümünü kullanın.</target>
-<source>Cannot load file %x.</source>
-<target>%x dosyası yüklenemedi.</target>
-
<source>Cannot determine volume name for %x.</source>
<target>%x için birim adı belirlenemedi.</target>
@@ -344,7 +344,7 @@
<target>Çı&kın</target>
<source>&File</source>
-<target></target>
+<target>&Dosya</target>
<source>&View help</source>
<target>&Yardıma bakın</target>
@@ -498,21 +498,21 @@ Komut şu durumlarda yürütülür:
<source>Data verification error: %x and %y have different content.</source>
<target>Veri doğrulama hatası: %x ve %y farklı içeriklere sahip.</target>
-<source>Cannot find folder %x.</source>
-<target></target>
-
<source>Target folder %x already existing.</source>
<target>%x hedef klasörü zaten var.</target>
+<source>Cannot find folder %x.</source>
+<target>%x klasörü bulunamadı.</target>
+
<source>Target folder input field must not be empty.</source>
<target>Hedef klasör giriş alanı boş olmamalı.</target>
-<source>Please enter a target folder for versioning.</source>
-<target>Sürüm izlemesinde kullanılacak bir hedef klasör yazın.</target>
-
<source>Source folder %x not found.</source>
<target>%x kaynak klasörü bulunamadı.</target>
+<source>Please enter a target folder for versioning.</source>
+<target>Sürüm izlemesinde kullanılacak bir hedef klasör yazın.</target>
+
<source>The following items have unresolved conflicts and will not be synchronized:</source>
<target>Uyuşmazlığı çözülmemiş şu ögeler eşleştirilmeyecek:</target>
@@ -529,7 +529,7 @@ Komut şu durumlarda yürütülür:
<target>Kullanılabilir:</target>
<source>Multiple folder pairs write to a common subfolder. Please review your configuration.</source>
-<target></target>
+<target>Birden çok klasör çifti aynı alt klasöre yazıyor. Lütfen ayarlarınızı gözden geçirin.</target>
<source>Synchronizing folder pair:</source>
<target>Eşleştirilen klasör çifti:</target>
@@ -631,7 +631,7 @@ Komut şu durumlarda yürütülür:
<target>Ad</target>
<source>Relative folder</source>
-<target></target>
+<target>Bağıl klasör</target>
<source>Base folder</source>
<target>Başlangıç klasörü</target>
@@ -673,7 +673,7 @@ Komut şu durumlarda yürütülür:
<target>Yerel ayarları silin</target>
<source>Clear local filter</source>
-<target></target>
+<target>Yerel süzgeci temizleyin</target>
<source>Copy</source>
<target>Kopyalayın</target>
@@ -694,10 +694,10 @@ Komut şu durumlarda yürütülür:
<target>&Toplu iş olarak kaydedin...</target>
<source>Start &comparison</source>
-<target></target>
+<target>&Karşılaştırmayı başlatın</target>
<source>Start &synchronization</source>
-<target></target>
+<target>&Eşleştirmeyi başlatın</target>
<source>&Options</source>
<target>A&yarlar</target>
@@ -709,7 +709,7 @@ Komut şu durumlarda yürütülür:
<target>A&rayın...</target>
<source>&Reset layout</source>
-<target></target>
+<target>Düzeni sıfı&rlayın</target>
<source>&Export file list...</source>
<target>Dosya list&esini verin...</target>
@@ -754,7 +754,7 @@ Komut şu durumlarda yürütülür:
<target>Büyük-küçük harf uysun</target>
<source>New</source>
-<target></target>
+<target>Ekleyin</target>
<source>Open...</source>
<target>Açın...</target>
@@ -799,13 +799,13 @@ Komut şu durumlarda yürütülür:
<target>Dosyaların aynı olup olmadığı, içeriklerine göre belirlensin.</target>
<source>Ignore time shift (in hours)</source>
-<target></target>
+<target>Yoksayılacak zaman kayması (saat olarak)</target>
<source>Consider file times with specified offset as equal</source>
-<target></target>
+<target>Belirtilen saat kadar zaman farkı yoksayılır</target>
<source>Handle daylight saving time</source>
-<target></target>
+<target>Yaz saati hesaba katılsın</target>
<source>Symbolic links:</source>
<target>Sembolik bağlantılar:</target>
@@ -838,7 +838,7 @@ Komut şu durumlarda yürütülür:
<target>En büyük:</target>
<source>C&lear</source>
-<target></target>
+<target>&Temizleyin</target>
<source>Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair.</source>
<target>Eşleştirmeye katılmayacak dosyaların süzülme kurallarını belirleyin. Dosya yollarını bulundukları klasör çiftine göre yazın.</target>
@@ -864,7 +864,7 @@ Komut şu durumlarda yürütülür:
<target>Dosya silme işlemi:</target>
<source>&Permanent</source>
-<target></target>
+<target>&Kalıcı</target>
<source>Delete or overwrite files permanently</source>
<target>Dosyalar kalıcı olarak silinir ya da üzerine yazılır</target>
@@ -876,7 +876,7 @@ Komut şu durumlarda yürütülür:
<target>Silinen ya da üzerine yazılan dosyalar geri dönüşüm kutusuna gönderilir</target>
<source>&Versioning</source>
-<target></target>
+<target>&Sürümleme</target>
<source>Move files to a user-defined folder</source>
<target>Dosyalar kullanıcı tarafından belirtilen bir klasöre taşınır</target>
@@ -891,7 +891,7 @@ Komut şu durumlarda yürütülür:
<target>Hiçbir hata ve uyarı iletisi görüntülenmez</target>
<source>&Pop-up</source>
-<target></target>
+<target>Açılan &Pencere</target>
<source>Show pop-up on errors or warnings</source>
<target>Hata ya da uyarılar açılır pencerede görüntülenir</target>
@@ -927,7 +927,7 @@ Komut şu durumlarda yürütülür:
<target>Bildirim alanına küçültün</target>
<source>Bytes copied:</source>
-<target></target>
+<target>Kopyalanan bayt:</target>
<source>Close</source>
<target>Kapatın</target>
@@ -942,7 +942,7 @@ Komut şu durumlarda yürütülür:
<target>Hiç bir soru sorulmadan eşleştirme yapılması için bir toplu iş dosyası oluşturun. İşlemi başlatmak için bu dosyaya çift tıklayın ya da bir görev zamanlayıcıya şu şekilde ekleyin: %x</target>
<source>&Stop</source>
-<target></target>
+<target>&Durdurun</target>
<source>Stop synchronization at first error</source>
<target>Oluşacak ilk hatada eşleştirme durdurulur</target>
@@ -1011,10 +1011,10 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>Açıklama</target>
<source>Show hidden dialogs again</source>
-<target></target>
+<target>Gizlenen iletiler yeniden görüntülensin</target>
<source>Show all permanently hidden dialogs and warning messages again</source>
-<target></target>
+<target>Gizlenen tüm ileti ve uyarılar yeniden görüntülensin</target>
<source>&Default</source>
<target>&Varsayılan</target>
@@ -1077,7 +1077,7 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>Ana Çubuk</target>
<source>Start comparison</source>
-<target></target>
+<target>Karşılaştırmayı başlatın</target>
<source>Comparison settings</source>
<target>Karşılaştırma ayarları</target>
@@ -1203,7 +1203,7 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>Eşleştirme Ayarları</target>
<source>Clear filter</source>
-<target></target>
+<target>Süzgeci temizleyin</target>
<source>Show files that exist on left side only</source>
<target>Yalnız sol tarafta bulunan dosyaları görüntüler ya da gizler</target>
@@ -1362,10 +1362,10 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>%item_folder% ögesinin diğer taraftaki karşılığı</target>
<source>Show hidden dialogs and warning messages again?</source>
-<target></target>
+<target>Gizlenen tüm ileti ve uyarılar yeniden görüntülensin mi?</target>
<source>&Show</source>
-<target></target>
+<target>&Görüntülensin</target>
<source>Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database.</source>
<target>İki tarafta da değişikliklere bakılır ve karşılıklı kopyalanır. Silinmiş, taşınmış ve çakışan ögeler, veritabanı kullanılarak kendiliğinden algılanır.</target>
diff --git a/FreeFileSync/Source/RealtimeSync/application.cpp b/FreeFileSync/Source/RealtimeSync/application.cpp
index 20195baf..068a7cef 100644
--- a/FreeFileSync/Source/RealtimeSync/application.cpp
+++ b/FreeFileSync/Source/RealtimeSync/application.cpp
@@ -70,8 +70,8 @@ bool Application::OnInit()
//SEM_FAILCRITICALERRORS at startup. This is to prevent error mode dialogs from hanging the application."
::SetErrorMode(SEM_FAILCRITICALERRORS);
- setAppUserModeId(L"RealtimeSync", L"SourceForge.RealtimeSync"); //noexcept
- //consider: RealtimeSync.exe, RealtimeSync_Win32.exe, RealtimeSync_x64.exe
+ setAppUserModeId(L"RealtimeSync", L"SourceForge.RealtimeSync"); //noexcept
+ //consider: RealtimeSync.exe, RealtimeSync_Win32.exe, RealtimeSync_x64.exe
wxToolTip::SetMaxWidth(-1); //disable tooltip wrapping -> Windows only
diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp
index 6a3a095d..9056d962 100644
--- a/FreeFileSync/Source/application.cpp
+++ b/FreeFileSync/Source/application.cpp
@@ -155,8 +155,8 @@ bool Application::OnInit()
//SEM_FAILCRITICALERRORS at startup. This is to prevent error mode dialogs from hanging the application."
::SetErrorMode(SEM_FAILCRITICALERRORS);
- setAppUserModeId(L"FreeFileSync", L"SourceForge.FreeFileSync"); //noexcept
- //consider: FreeFileSync.exe, FreeFileSync_Win32.exe, FreeFileSync_x64.exe
+ setAppUserModeId(L"FreeFileSync", L"SourceForge.FreeFileSync"); //noexcept
+ //consider: FreeFileSync.exe, FreeFileSync_Win32.exe, FreeFileSync_x64.exe
wxToolTip::SetMaxWidth(-1); //disable tooltip wrapping -> Windows only
diff --git a/FreeFileSync/Source/lib/dir_lock.cpp b/FreeFileSync/Source/lib/dir_lock.cpp
index 821976f5..1d22f455 100644
--- a/FreeFileSync/Source/lib/dir_lock.cpp
+++ b/FreeFileSync/Source/lib/dir_lock.cpp
@@ -496,7 +496,7 @@ void waitOnDirLock(const Zstring& lockfilepath, DirLockCallback* callback) //thr
if (dist(lastLifeSign, now) / TICKS_PER_SEC > EMIT_LIFE_SIGN_INTERVAL)
{
const int remainingSeconds = std::max<int>(0, DETECT_ABANDONED_INTERVAL - dist(lastLifeSign, getTicks()) / TICKS_PER_SEC);
- const std::wstring remSecMsg = replaceCpy(_P("1 sec", "%x sec", remainingSeconds), L"%x", numberTo<std::wstring>(remainingSeconds));
+ const std::wstring remSecMsg = replaceCpy(_P("1 sec", "%x sec", remainingSeconds), L"%x", toGuiString(remainingSeconds));
callback->reportStatus(infoMsg + L" | " + _("Detecting abandoned lock...") + L' ' + remSecMsg);
}
else
diff --git a/FreeFileSync/Source/lib/icon_buffer.cpp b/FreeFileSync/Source/lib/icon_buffer.cpp
index c9f60ad9..73fe47d1 100644
--- a/FreeFileSync/Source/lib/icon_buffer.cpp
+++ b/FreeFileSync/Source/lib/icon_buffer.cpp
@@ -619,11 +619,11 @@ void WorkerThread::operator()() //thread entry
//Prerequisites, see thumbnail.h
//1. Initialize COM
- if (FAILED(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)))
- {
- assert(false);
- return;
- }
+ if (FAILED(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)))
+ {
+ assert(false);
+ return;
+ }
ZEN_ON_SCOPE_EXIT(::CoUninitialize());
//2. Initialize system image list
diff --git a/FreeFileSync/Source/lib/osx_file_icon.h b/FreeFileSync/Source/lib/osx_file_icon.h
new file mode 100644
index 00000000..5edfd740
--- /dev/null
+++ b/FreeFileSync/Source/lib/osx_file_icon.h
@@ -0,0 +1,36 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef OSX_FILE_ICON_8427508422345342
+#define OSX_FILE_ICON_8427508422345342
+
+#include <vector>
+#include <zen/sys_error.h>
+
+namespace osx
+{
+struct ImageData
+{
+ ImageData(int w, int h) : width(w), height(h), rgb(w* h * 3), alpha(w* h) {}
+ ImageData(ImageData&& tmp) : width(tmp.width), height(tmp.height) { rgb.swap(tmp.rgb); alpha.swap(tmp.alpha); }
+
+ const int width;
+ const int height;
+ std::vector<unsigned char> rgb; //rgb-byte order for use with wxImage
+ std::vector<unsigned char> alpha;
+
+private:
+ ImageData(const ImageData&);
+ ImageData& operator=(const ImageData&);
+};
+
+ImageData getThumbnail(const char* filename, int requestedSize); //throw SysError
+ImageData getFileIcon (const char* filename, int requestedSize); //throw SysError
+ImageData getDefaultFileIcon (int requestedSize); //throw SysError
+ImageData getDefaultFolderIcon(int requestedSize); //throw SysError
+}
+
+#endif //OSX_FILE_ICON_8427508422345342
diff --git a/FreeFileSync/Source/lib/osx_file_icon.mm b/FreeFileSync/Source/lib/osx_file_icon.mm
new file mode 100644
index 00000000..e6b384d3
--- /dev/null
+++ b/FreeFileSync/Source/lib/osx_file_icon.mm
@@ -0,0 +1,179 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#include "osx_file_icon.h"
+#include <zen/osx_throw_exception.h>
+#include <zen/scope_guard.h>
+#include <zen/basic_math.h>
+
+namespace
+{
+osx::ImageData extractBytes(NSImage* nsImg, int requestedSize) //throw SysError; NSException?
+{
+ /*
+ wxBitmap(NSImage*) is not good enough: it calls "[NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]]"
+ => inefficient: TIFFRepresentation converts all contained images of an NSImage
+ => lacking: imageRepWithData extracts the first contained image only!
+ => wxBitmap(NSImage*) is wxCocoa only, deprecated!
+ => wxWidgets generally is not thread-safe so care must be taken to use wxBitmap from main thread only! (e.g. race-condition on non-atomic ref-count!!!)
+
+ -> we need only a single bitmap at a specific size => extract raw bytes for use with wxImage in a thread-safe way!
+ */
+
+ //we choose the Core Graphics solution; for the equivalent App-Kit way see: http://www.cocoabuilder.com/archive/cocoa/193131-is-lockfocus-main-thread-specific.html#193191
+
+ ZEN_OSX_ASSERT(requestedSize > 0);
+ NSRect rectProposed = NSMakeRect(0, 0, requestedSize, requestedSize); //this is merely a hint!
+
+ CGImageRef imgRef = [nsImg CGImageForProposedRect:&rectProposed context:nil hints:nil];
+ ZEN_OSX_ASSERT(imgRef != NULL); //can this fail? not documented; ownership?? not documented!
+
+ const size_t width = ::CGImageGetWidth (imgRef);
+ const size_t height = ::CGImageGetHeight(imgRef);
+
+ ZEN_OSX_ASSERT(width > 0 && height > 0 && requestedSize > 0);
+
+ int trgWidth = width;
+ int trgHeight = height;
+
+ const int maxExtent = std::max(width, height); //don't stretch small images, but shrink large ones instead!
+ if (requestedSize < maxExtent)
+ {
+ trgWidth = width * requestedSize / maxExtent;
+ trgHeight = height * requestedSize / maxExtent;
+ }
+
+ CGColorSpaceRef colorSpace = ::CGColorSpaceCreateDeviceRGB();
+ ZEN_OSX_ASSERT(colorSpace != NULL); //may fail
+ ZEN_ON_SCOPE_EXIT(::CGColorSpaceRelease(colorSpace));
+
+ std::vector<unsigned char> buf(trgWidth* trgHeight * 4); //32-bit ARGB, little endian byte order -> already initialized with 0 = fully transparent
+
+ //supported color spaces: https://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB
+ CGContextRef ctxRef = ::CGBitmapContextCreate(&buf[0], //void *data,
+ trgWidth, //size_t width,
+ trgHeight, //size_t height,
+ 8, //size_t bitsPerComponent,
+ 4 * trgWidth, //size_t bytesPerRow,
+ colorSpace, //CGColorSpaceRef colorspace,
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little); //CGBitmapInfo bitmapInfo
+ ZEN_OSX_ASSERT(ctxRef != NULL);
+ ZEN_ON_SCOPE_EXIT(::CGContextRelease(ctxRef));
+
+ ::CGContextDrawImage(ctxRef, CGRectMake(0, 0, trgWidth, trgHeight), imgRef); //can this fail? not documented
+
+ //::CGContextFlush(ctxRef); //"If you pass [...] a bitmap context, this function does nothing."
+
+ osx::ImageData imgOut(trgWidth, trgHeight);
+
+ auto it = buf.begin();
+ auto itOutRgb = imgOut.rgb.begin();
+ auto itOutAlpha = imgOut.alpha.begin();
+ for (int i = 0; i < trgWidth * trgHeight; ++i)
+ {
+ const unsigned char b = *it++;
+ const unsigned char g = *it++;
+ const unsigned char r = *it++;
+ const unsigned char a = *it++;
+
+ //unsigned arithmetics caveat!
+ auto demultiplex = [&](unsigned char c) { return static_cast<unsigned char>(numeric::clampCpy(a == 0 ? 0 : (c * 255 + a - 1) / a, 0, 255)); }; //=ceil(c * 255 / a)
+
+ *itOutRgb++ = demultiplex(r);
+ *itOutRgb++ = demultiplex(g);
+ *itOutRgb++ = demultiplex(b);
+ *itOutAlpha++ = a;
+ }
+
+ return imgOut;
+}
+}
+
+
+osx::ImageData osx::getThumbnail(const char* filename, int requestedSize) //throw SysError
+{
+ @try
+ {
+ @autoreleasepool
+ {
+ NSString* nsFile = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding];
+ ZEN_OSX_ASSERT(nsFile != nil); //throw SysError; can this fail? not documented
+ //stringWithCString returns string which is already set to autorelease!
+
+ NSImage* nsImg = [[[NSImage alloc] initWithContentsOfFile:nsFile] autorelease];
+ ZEN_OSX_ASSERT(nsImg != nil); //may fail
+
+ return extractBytes(nsImg, requestedSize); //throw SysError
+ }
+ }
+ @catch(NSException* e)
+ {
+ throwSysError(e); //throw SysError
+ }
+}
+
+
+osx::ImageData osx::getFileIcon(const char* filename, int requestedSize) //throw SysError
+{
+ @try
+ {
+ @autoreleasepool
+ {
+ NSString* nsFile = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding];
+ ZEN_OSX_ASSERT(nsFile != nil); //throw SysError; can this fail? not documented
+ //stringWithCString returns string which is already set to autorelease!
+
+ NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFile:nsFile];
+ ZEN_OSX_ASSERT(nsImg != nil); //can this fail? not documented
+
+ return extractBytes(nsImg, requestedSize); //throw SysError
+ }
+ }
+ @catch (NSException* e)
+ {
+ throwSysError(e); //throw SysError
+ }
+}
+
+
+osx::ImageData osx::getDefaultFileIcon(int requestedSize) //throw SysError
+{
+ @try
+ {
+ @autoreleasepool
+ {
+ NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericDocumentIcon)];
+ //NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:@"dat"];
+ ZEN_OSX_ASSERT(nsImg != nil); //can this fail? not documented
+
+ return extractBytes(nsImg, requestedSize); //throw SysError
+ }
+ }
+ @catch (NSException* e)
+ {
+ throwSysError(e); //throw SysError
+ }
+}
+
+
+osx::ImageData osx::getDefaultFolderIcon(int requestedSize) //throw SysError
+{
+ @try
+ {
+ @autoreleasepool
+ {
+ NSImage* nsImg = [NSImage imageNamed:NSImageNameFolder];
+ //NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)];
+ ZEN_OSX_ASSERT(nsImg != nil); //may fail
+
+ return extractBytes(nsImg, requestedSize); //throw SysError
+ }
+ }
+ @catch (NSException* e)
+ {
+ throwSysError(e); //throw SysError
+ }
+}
diff --git a/FreeFileSync/Source/lib/process_xml.h b/FreeFileSync/Source/lib/process_xml.h
index 394b3f65..6d834922 100644
--- a/FreeFileSync/Source/lib/process_xml.h
+++ b/FreeFileSync/Source/lib/process_xml.h
@@ -135,7 +135,7 @@ struct XmlGlobalSettings
XmlGlobalSettings() :
programLanguage(zen::retrieveSystemLanguage()),
failsafeFileCopy(true),
- copyLockedFiles(true),
+ copyLockedFiles(false), //safer default: avoid copies of partially written files
copyFilePermissions(false),
automaticRetryCount(0),
automaticRetryDelay(5),
diff --git a/FreeFileSync/Source/lib/shadow.cpp b/FreeFileSync/Source/lib/shadow.cpp
new file mode 100644
index 00000000..be9ba1f3
--- /dev/null
+++ b/FreeFileSync/Source/lib/shadow.cpp
@@ -0,0 +1,128 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#include "shadow.h"
+#include <stdexcept>
+#include <zen/win.h> //includes "windows.h"
+#include <zen/dll.h>
+#include <zen/win_ver.h>
+#include <zen/assert_static.h>
+#include <zen/long_path_prefix.h>
+#include <zen/symlink_target.h>
+#include "../dll/ShadowCopy/shadow.h"
+#include <zen/scope_guard.h>
+
+using namespace zen;
+using namespace shadow;
+
+
+namespace
+{
+bool runningWOW64() //test if process is running under WOW64 (reference http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx)
+{
+ typedef BOOL (WINAPI* IsWow64ProcessFun)(HANDLE hProcess, PBOOL Wow64Process);
+
+ const SysDllFun<IsWow64ProcessFun> isWow64Process(L"kernel32.dll", "IsWow64Process");
+ if (isWow64Process)
+ {
+ BOOL isWow64 = FALSE;
+ if (isWow64Process(::GetCurrentProcess(), &isWow64))
+ return isWow64 != FALSE;
+ }
+ return false;
+}
+}
+
+//#############################################################################################################
+
+class ShadowCopy::ShadowVolume
+{
+public:
+ ShadowVolume(const Zstring& volumeNamePf) : //throw FileError
+ createShadowCopy (getDllName(), funName_createShadowCopy),
+ releaseShadowCopy (getDllName(), funName_releaseShadowCopy),
+ getShadowVolume (getDllName(), funName_getShadowVolume),
+ getLastErrorMessage(getDllName(), funName_getLastErrorMessage),
+ backupHandle(nullptr)
+ {
+ //VSS does not support running under WOW64 except for Windows XP and Windows Server 2003
+ //reference: http://msdn.microsoft.com/en-us/library/aa384627(VS.85).aspx
+ if (runningWOW64())
+ throw FileError(_("Cannot access the Volume Shadow Copy Service."),
+ _("Please use FreeFileSync 64-bit version to create shadow copies on this system."));
+
+ //check if shadow copy dll was loaded correctly
+ if (!createShadowCopy || !releaseShadowCopy || !getShadowVolume || !getLastErrorMessage)
+ throw FileError(_("Cannot access the Volume Shadow Copy Service."),
+ replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName())));
+
+ //---------------------------------------------------------------------------------------------------------
+ //start volume shadow copy service:
+ backupHandle = createShadowCopy(volumeNamePf.c_str());
+ if (!backupHandle)
+ throw FileError(_("Cannot access the Volume Shadow Copy Service."), getLastErrorMessage() + std::wstring(L" Volume: ") + fmtFileName(volumeNamePf));
+
+ shadowVolPf = appendSeparator(getShadowVolume(backupHandle)); //shadowVolName NEVER has a trailing backslash
+ }
+
+ ~ShadowVolume() { releaseShadowCopy(backupHandle); } //fast! no performance optimization necessary
+
+ Zstring geNamePf() const { return shadowVolPf; } //with trailing path separator
+
+private:
+ ShadowVolume (const ShadowVolume&) = delete;
+ ShadowVolume& operator=(const ShadowVolume&) = delete;
+
+ const DllFun<FunType_createShadowCopy > createShadowCopy;
+ const DllFun<FunType_releaseShadowCopy > releaseShadowCopy;
+ const DllFun<FunType_getShadowVolume > getShadowVolume;
+ const DllFun<FunType_getLastErrorMessage> getLastErrorMessage;
+
+ Zstring shadowVolPf;
+ ShadowHandle backupHandle;
+};
+
+//#############################################################################################################
+
+Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function<void(const Zstring& volumeNamePf)>& onBeforeMakeVolumeCopy)
+{
+ Zstring filepathFinal = inputFile;
+
+ //try to resolve symlinks and junctions:
+ //1. symlinks: we need to retrieve the target path, else we would just return a symlink on a VSS volume while the target outside were still locked!
+ //2. junctions: C:\Users\<username> is a junction that may link to e.g. D:\Users\<username>, so GetVolumePathName() returns "D:\" => "Volume name %x not part of file name %y."
+ if (vistaOrLater())
+ filepathFinal = getResolvedFilePath(inputFile); //throw FileError; requires Vista or later!
+ //-> returns paths with \\?\ prefix! => make sure to avoid duplicate shadow copies for volume paths with/without prefix
+
+ DWORD bufferSize = 10000;
+ std::vector<wchar_t> volBuffer(bufferSize);
+ if (!::GetVolumePathName(filepathFinal.c_str(), //__in LPCTSTR lpszFileName,
+ &volBuffer[0], //__out LPTSTR lpszVolumePathName,
+ bufferSize)) //__in DWORD cchBufferLength
+ throwFileError(replaceCpy(_("Cannot determine volume name for %x."), L"%x", fmtFileName(filepathFinal)), L"GetVolumePathName", ::GetLastError());
+
+ const Zstring volumeNamePf = appendSeparator(&volBuffer[0]); //msdn: if buffer is 1 char too short, GetVolumePathName() may skip last separator without error!
+
+ //input file is always absolute! directory formatting takes care of this! Therefore volume name can always be found.
+ const size_t pos = filepathFinal.find(volumeNamePf); //filepathFinal needs NOT to begin with volumeNamePf: consider \\?\ prefix!
+ if (pos == Zstring::npos)
+ throw FileError(replaceCpy(replaceCpy(_("Volume name %x is not part of file path %y."),
+ L"%x", fmtFileName(volumeNamePf)),
+ L"%y", fmtFileName(filepathFinal)));
+
+ //get or create instance of shadow volume
+ auto it = shadowVol.find(volumeNamePf);
+ if (it == shadowVol.end())
+ {
+ onBeforeMakeVolumeCopy(volumeNamePf); //notify client before (potentially) blocking some time
+ auto newEntry = std::make_shared<ShadowVolume>(volumeNamePf); //throw FileError
+ it = shadowVol.insert(std::make_pair(volumeNamePf, newEntry)).first;
+ }
+
+ //return filepath alias on shadow copy volume
+ return it->second->geNamePf() + Zstring(filepathFinal.c_str() + pos + volumeNamePf.length());
+}
diff --git a/FreeFileSync/Source/lib/shadow.h b/FreeFileSync/Source/lib/shadow.h
new file mode 100644
index 00000000..4f2dbf32
--- /dev/null
+++ b/FreeFileSync/Source/lib/shadow.h
@@ -0,0 +1,36 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef SHADOW_H_INCLUDED
+#define SHADOW_H_INCLUDED
+
+#include <map>
+#include <memory>
+#include <functional>
+#include <zen/zstring.h>
+#include <zen/file_error.h>
+
+namespace shadow
+{
+class ShadowCopy //take and buffer Windows Volume Shadow Copy snapshots as needed
+{
+public:
+ ShadowCopy() {}
+
+ //return filepath on shadow copy volume - follows symlinks!
+ Zstring makeShadowCopy(const Zstring& inputFile, const std::function<void(const Zstring& volumeNamePf)>& onBeforeMakeVolumeCopy); //throw FileError
+
+private:
+ ShadowCopy (const ShadowCopy&) = delete;
+ ShadowCopy& operator=(const ShadowCopy&) = delete;
+
+ class ShadowVolume;
+ typedef std::map<Zstring, std::shared_ptr<ShadowVolume>, LessFilename> VolNameShadowMap;
+ VolNameShadowMap shadowVol;
+};
+}
+
+#endif //SHADOW_H_INCLUDED
diff --git a/FreeFileSync/Source/synchronization.cpp b/FreeFileSync/Source/synchronization.cpp
index af37ffc8..2c3af007 100644
--- a/FreeFileSync/Source/synchronization.cpp
+++ b/FreeFileSync/Source/synchronization.cpp
@@ -1898,19 +1898,6 @@ void SynchronizeFolderPair::verifyFileCopy(const Zstring& source, const Zstring&
//###########################################################################################
-/*
-struct LessDependentDirectory : public std::binary_function<Zstring, Zstring, bool>
-{
--> a *very* bad idea: this is NOT a strict weak ordering! No transitivity of equivalence!
-
- bool operator()(const Zstring& lhs, const Zstring& rhs) const
- {
- return LessFilename()(Zstring(lhs.c_str(), std::min(lhs.length(), rhs.length())),
- Zstring(rhs.c_str(), std::min(lhs.length(), rhs.length())));
- }
-};
-*/
-
template <SelectedSide side> //create base directories first (if not yet existing) -> no symlink or attribute copying!
bool createBaseDirectory(BaseDirPair& baseDirObj, ProcessCallback& callback) //nothrow; return false if fatal error occurred
{
@@ -1918,18 +1905,7 @@ bool createBaseDirectory(BaseDirPair& baseDirObj, ProcessCallback& callback) //n
if (dirpath.empty())
return true;
- if (baseDirObj.isExisting<side>()) //atomicity: do NOT check directory existence again!
- {
- //just convenience: exit sync right here instead of showing tons of error messages during file copy
- zen::Opt<std::wstring> errMsg = tryReportingError([&]
- {
- if (!dirExistsUpdating(dirpath, false, callback))
- throw FileError(replaceCpy(_("Cannot find folder %x."), L"%x", fmtFileName(dirpath))); //should be logged as a "fatal error" if ignored by the user...
- }, callback); //may throw in error-callback!
-
- return !errMsg;
- }
- else //create target directory: user presumably ignored error "dir existing" in order to have it created automatically
+ if (!baseDirObj.isExisting<side>()) //create target directory: user presumably ignored error "dir existing" in order to have it created automatically
{
bool temporaryNetworkDrop = false;
zen::Opt<std::wstring> errMsg = tryReportingError([&]
@@ -1942,7 +1918,7 @@ bool createBaseDirectory(BaseDirPair& baseDirObj, ProcessCallback& callback) //n
}
catch (const ErrorTargetExisting&)
{
- //TEMPORARY network drop: base directory not found during comparison, but reappears during synchronization
+ //TEMPORARY network drop! base directory not found during comparison, but reappears during synchronization
//=> sync-directions are based on false assumptions! Abort.
callback.reportFatalError(replaceCpy(_("Target folder %x already existing."), L"%x", fmtFileName(dirpath)));
temporaryNetworkDrop = true;
@@ -1955,7 +1931,16 @@ bool createBaseDirectory(BaseDirPair& baseDirObj, ProcessCallback& callback) //n
}, callback); //may throw in error-callback!
return !errMsg && !temporaryNetworkDrop;
}
+
+ return true;
}
+
+struct ReadWriteCount
+{
+ ReadWriteCount() : reads(), writes() {}
+ size_t reads;
+ size_t writes;
+};
}
@@ -2011,6 +1996,19 @@ void zen::synchronize(const TimeComp& timeStamp,
//-------------------execute basic checks all at once before starting sync--------------------------------------
+ auto dirNotFoundAnymore = [&](const Zstring& baseDirPf, bool wasExisting)
+ {
+ if (wasExisting)
+ if (Opt<std::wstring> errMsg = tryReportingError([&]
+ {
+ if (!dirExistsUpdating(baseDirPf, false, callback))
+ throw FileError(replaceCpy(_("Cannot find folder %x."), L"%x", fmtFileName(baseDirPf))); //should be logged as a "fatal error" if ignored by the user...
+ }, callback)) //may throw in error-callback!
+ return true;
+
+ return false;
+ };
+
auto dependentDir = [](const Zstring& lhs, const Zstring& rhs) //note: this is NOT an equivalence relation!
{
return EqualFilename()(Zstring(lhs.c_str(), std::min(lhs.length(), rhs.length())),
@@ -2018,20 +2016,24 @@ void zen::synchronize(const TimeComp& timeStamp,
};
//aggregate information
- std::map<Zstring, std::pair<size_t, size_t>, LessFilename> dirReadWriteCount; //count read/write accesses
+ std::map<Zstring, ReadWriteCount, LessFilename> dirReadWriteCount; //count read/write accesses
+ for (auto j = begin(folderCmp); j != end(folderCmp); ++j)
+ {
+ dirReadWriteCount[j->getBaseDirPf<LEFT_SIDE >()]; //create all entries first!
+ dirReadWriteCount[j->getBaseDirPf<RIGHT_SIDE>()]; //=> counting accesses is complex for later inserts!
+ }
+
auto incReadCount = [&](const Zstring& baseDir)
{
- dirReadWriteCount[baseDir]; //create entry
for (auto& item : dirReadWriteCount)
if (dependentDir(baseDir, item.first))
- ++item.second.first;
+ ++item.second.reads;
};
auto incWriteCount = [&](const Zstring& baseDir)
{
- dirReadWriteCount[baseDir]; //create entry
for (auto& item : dirReadWriteCount)
if (dependentDir(baseDir, item.first))
- ++item.second.second;
+ ++item.second.writes;
};
std::vector<std::pair<Zstring, Zstring>> significantDiffPairs;
@@ -2047,16 +2049,18 @@ void zen::synchronize(const TimeComp& timeStamp,
for (auto j = begin(folderCmp); j != end(folderCmp); ++j)
{
const size_t folderIndex = j - begin(folderCmp);
+ const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex];
//exclude some pathological case (leftdir, rightdir are empty)
if (EqualFilename()(j->getBaseDirPf<LEFT_SIDE>(), j->getBaseDirPf<RIGHT_SIDE>()))
+ {
+ skipFolderPair[folderIndex] = true;
continue;
+ }
- const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex];
-
+ //aggregate basic information
const SyncStatistics folderPairStat(*j);
- //aggregate basic information
const bool writeLeft = folderPairStat.getCreate<LEFT_SIDE>() +
folderPairStat.getUpdate<LEFT_SIDE>() +
folderPairStat.getDelete<LEFT_SIDE>() > 0;
@@ -2073,15 +2077,6 @@ void zen::synchronize(const TimeComp& timeStamp,
continue;
}
- //check empty input fields: this only makes sense if empty field is source (and no DB files need to be created)
- if ((j->getBaseDirPf<LEFT_SIDE >().empty() && (writeLeft || folderPairCfg.saveSyncDB_)) ||
- (j->getBaseDirPf<RIGHT_SIDE>().empty() && (writeRight || folderPairCfg.saveSyncDB_)))
- {
- callback.reportFatalError(_("Target folder input field must not be empty."));
- skipFolderPair[folderIndex] = true;
- continue;
- }
-
//aggregate information of folders used by multiple pairs in read/write access
if (!dependentDir(j->getBaseDirPf<LEFT_SIDE>(), j->getBaseDirPf<RIGHT_SIDE>())) //true in general
{
@@ -2107,45 +2102,60 @@ void zen::synchronize(const TimeComp& timeStamp,
incWriteCount(j->getBaseDirPf<LEFT_SIDE>());
}
+ //check empty input fields: this only makes sense if empty field is source (and no DB files need to be created)
+ if ((j->getBaseDirPf<LEFT_SIDE >().empty() && (writeLeft || folderPairCfg.saveSyncDB_)) ||
+ (j->getBaseDirPf<RIGHT_SIDE>().empty() && (writeRight || folderPairCfg.saveSyncDB_)))
+ {
+ callback.reportFatalError(_("Target folder input field must not be empty."));
+ skipFolderPair[folderIndex] = true;
+ continue;
+ }
- if (folderPairStat.getUpdate() + folderPairStat.getDelete() > 0 &&
- folderPairCfg.handleDeletion == zen::DELETE_TO_VERSIONING)
+ //check for network drops after comparison
+ // - convenience: exit sync right here instead of showing tons of errors during file copy
+ // - early failure! there's no point in evaluating subsequent warnings
+ if (dirNotFoundAnymore(j->getBaseDirPf<LEFT_SIDE >(), j->isExisting<LEFT_SIDE >()) ||
+ dirNotFoundAnymore(j->getBaseDirPf<RIGHT_SIDE>(), j->isExisting<RIGHT_SIDE>()))
{
- //check if user-defined directory for deletion was specified
- if (folderPairCfg.versioningFolder.empty()) //already trimmed by getFormattedDirectoryPath()
- {
- //should never arrive here: already checked in SyncCfgDialog
- callback.reportFatalError(_("Please enter a target folder for versioning."));
- skipFolderPair[folderIndex] = true;
- continue;
- }
+ skipFolderPair[folderIndex] = true;
+ continue;
}
//the following scenario is covered by base directory creation below in case source directory exists (accessible or not), but latter doesn't cover source created after comparison, but before sync!!!
- auto checkSourceMissing = [&](const Zstring& baseDirPf, bool wasExisting) -> bool //avoid race-condition: we need to evaluate existence status from time of comparison!
+ auto sourceDirNotFound = [&](const Zstring& baseDirPf, bool wasExisting) -> bool //avoid race-condition: we need to evaluate existence status from time of comparison!
{
if (!baseDirPf.empty())
- {
//PERMANENT network drop: avoid data loss when source directory is not found AND user chose to ignore errors (else we wouldn't arrive here)
- if (folderPairStat.getCreate() +
- folderPairStat.getUpdate() == 0 &&
+ if (folderPairStat.getCreate() + folderPairStat.getUpdate() == 0 &&
folderPairStat.getDelete() > 0) //deletions only... (respect filtered items!)
//folderPairStat.getConflict() == 0 && -> there COULD be conflicts for <automatic> if directory existence check fails, but loading sync.ffs_db succeeds
- //https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3531351&group_id=234430 -> fixed, but still better not consider conflicts
- {
+ //https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3531351&group_id=234430 -> fixed, but still better not consider conflicts!
if (!wasExisting) //avoid race-condition: we need to evaluate existence status from time of comparison!
{
callback.reportFatalError(replaceCpy(_("Source folder %x not found."), L"%x", fmtFileName(baseDirPf)));
- skipFolderPair[folderIndex] = true;
- return false;
+ return true;
}
- }
- }
- return true;
+ return false;
};
- if (!checkSourceMissing(j->getBaseDirPf<LEFT_SIDE >(), j->isExisting<LEFT_SIDE >()) ||
- !checkSourceMissing(j->getBaseDirPf<RIGHT_SIDE>(), j->isExisting<RIGHT_SIDE>()))
+ if (sourceDirNotFound(j->getBaseDirPf<LEFT_SIDE >(), j->isExisting<LEFT_SIDE >()) ||
+ sourceDirNotFound(j->getBaseDirPf<RIGHT_SIDE>(), j->isExisting<RIGHT_SIDE>()))
+ {
+ skipFolderPair[folderIndex] = true;
continue;
+ }
+
+ //check if user-defined directory for deletion was specified
+ if (folderPairCfg.handleDeletion == zen::DELETE_TO_VERSIONING &&
+ folderPairStat.getUpdate() + folderPairStat.getDelete() > 0)
+ {
+ if (folderPairCfg.versioningFolder.empty()) //already trimmed by getFormattedDirectoryPath()
+ {
+ //should not arrive here: already checked in SyncCfgDialog
+ callback.reportFatalError(_("Please enter a target folder for versioning."));
+ skipFolderPair[folderIndex] = true;
+ continue;
+ }
+ }
//check if more than 50% of total number of files/dirs are to be created/overwritten/deleted
if (significantDifferenceDetected(folderPairStat))
@@ -2172,7 +2182,8 @@ void zen::synchronize(const TimeComp& timeStamp,
//windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong
auto checkRecycler = [&](const Zstring& baseDirPf)
{
- if (!baseDirPf.empty()) //should be
+ assert(!baseDirPf.empty());
+ if (!baseDirPf.empty())
if (baseDirHasRecycler.find(baseDirPf) == baseDirHasRecycler.end()) //perf: avoid duplicate checks!
{
callback.reportStatus(replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", fmtFileName(baseDirPf), false));
@@ -2255,12 +2266,8 @@ void zen::synchronize(const TimeComp& timeStamp,
{
std::vector<Zstring> conflictDirs;
for (const auto& item : dirReadWriteCount)
- {
- const std::pair<size_t, size_t>& countRef = item.second; //# read/write accesses
-
- if (countRef.first + countRef.second >= 2 && countRef.second >= 1) //race condition := multiple accesses of which at least one is a write
+ if (item.second.reads + item.second.writes >= 2 && item.second.writes >= 1) //race condition := multiple accesses of which at least one is a write
conflictDirs.push_back(item.first);
- }
if (!conflictDirs.empty())
{
@@ -2286,8 +2293,10 @@ void zen::synchronize(const TimeComp& timeStamp,
//loop through all directory pairs
for (auto j = begin(folderCmp); j != end(folderCmp); ++j)
{
- //exclude pathological cases (e.g. leftdir, rightdir are empty)
- if (EqualFilename()(j->getBaseDirPf<LEFT_SIDE>(), j->getBaseDirPf<RIGHT_SIDE>()))
+ const size_t folderIndex = j - begin(folderCmp);
+ const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex];
+
+ if (skipFolderPair[folderIndex]) //folder pairs may be skipped after fatal errors were found
continue;
//------------------------------------------------------------------------------------------
@@ -2296,16 +2305,15 @@ void zen::synchronize(const TimeComp& timeStamp,
L" " + j->getBaseDirPf<RIGHT_SIDE>());
//------------------------------------------------------------------------------------------
- const size_t folderIndex = j - begin(folderCmp);
- const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex];
-
- if (skipFolderPair[folderIndex]) //folder pairs may be skipped after fatal errors were found
+ //checking a second time: (a long time may have passed since the intro checks!)
+ if (dirNotFoundAnymore(j->getBaseDirPf<LEFT_SIDE >(), j->isExisting<LEFT_SIDE >()) ||
+ dirNotFoundAnymore(j->getBaseDirPf<RIGHT_SIDE>(), j->isExisting<RIGHT_SIDE>()))
continue;
//create base directories first (if not yet existing) -> no symlink or attribute copying!
if (!createBaseDirectory<LEFT_SIDE >(*j, callback) ||
!createBaseDirectory<RIGHT_SIDE>(*j, callback))
- continue; //skip this folder pair
+ continue;
//------------------------------------------------------------------------------------------
//execute synchronization recursively
@@ -2338,7 +2346,7 @@ void zen::synchronize(const TimeComp& timeStamp,
if (folderPairCfg.handleDeletion == DELETE_TO_RECYCLER)
{
auto it = baseDirHasRecycler.find(baseDirPf);
- if (it != baseDirHasRecycler.end())
+ if (it != baseDirHasRecycler.end()) //buffer filled during intro checks (but only if deletions are expected)
if (!it->second)
return DELETE_PERMANENTLY; //Windows' ::SHFileOperation() will do this anyway, but we have a better and faster deletion routine (e.g. on networks)
}
@@ -2370,8 +2378,8 @@ void zen::synchronize(const TimeComp& timeStamp,
syncFP.startSync(*j);
//(try to gracefully) cleanup temporary Recycle bin folders and versioning -> will be done in ~DeletionHandling anyway...
- tryReportingError([&] { delHandlerL.tryCleanup(); }, callback); //show error dialog if necessary
- tryReportingError([&] { delHandlerR.tryCleanup(); }, callback); //
+ tryReportingError([&] { delHandlerL.tryCleanup(); /*throw FileError*/}, callback); //show error dialog if necessary
+ tryReportingError([&] { delHandlerR.tryCleanup(); /*throw FileError*/}, callback); //
//(try to gracefully) write database file
if (folderPairCfg.saveSyncDB_)
@@ -2379,7 +2387,7 @@ void zen::synchronize(const TimeComp& timeStamp,
callback.reportStatus(_("Generating database..."));
callback.forceUiRefresh();
- tryReportingError([&] { zen::saveLastSynchronousState(*j); }, callback); //throw FileError
+ tryReportingError([&] { zen::saveLastSynchronousState(*j); /*throw FileError*/ }, callback);
guardUpdateDb.dismiss();
}
}
diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp
index bf8bf758..b40941fa 100644
--- a/FreeFileSync/Source/ui/batch_status_handler.cpp
+++ b/FreeFileSync/Source/ui/batch_status_handler.cpp
@@ -380,7 +380,7 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er
//auto-retry
if (retryNumber < automaticRetryCount_)
{
- errorLog.logMsg(errorMessage + L"\n=> " +
+ errorLog.logMsg(errorMessage + L"\n-> " +
_P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", automaticRetryDelay_), TYPE_INFO);
//delay
const int iterations = static_cast<int>(1000 * automaticRetryDelay_ / UI_UPDATE_INTERVAL); //always round down: don't allow for negative remaining time below
@@ -418,7 +418,7 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er
case ConfirmationButton3::DONT_DO_IT: //retry
guardWriteLog.dismiss();
- errorLog.logMsg(errorMessage + L"\n=> " + _("Retrying operation..."), TYPE_INFO);
+ errorLog.logMsg(errorMessage + L"\n-> " + _("Retrying operation..."), TYPE_INFO);
return ProcessCallback::RETRY;
case ConfirmationButton3::CANCEL:
diff --git a/FreeFileSync/Source/ui/custom_grid.cpp b/FreeFileSync/Source/ui/custom_grid.cpp
index c40c5f86..98f9b37f 100644
--- a/FreeFileSync/Source/ui/custom_grid.cpp
+++ b/FreeFileSync/Source/ui/custom_grid.cpp
@@ -506,7 +506,7 @@ private:
static const int GAP_SIZE = 2;
- virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected) override
+ virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected) override
{
wxRect rectTmp = rect;
@@ -958,20 +958,33 @@ private:
virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override
{
- const FileSystemObject* fsObj = getRawData(row);
- GridData::drawCellBackground(dc, rect, enabled, selected, highlightSyncAction_ ?
- getBackGroundColorSyncAction(fsObj) :
- getBackGroundColorCmpCategory(fsObj));
+ if (enabled)
+ {
+ if (selected)
+ dc.GradientFillLinear(rect, getColorSelectionGradientFrom(), getColorSelectionGradientTo(), wxEAST);
+ else
+ {
+ if (const FileSystemObject* fsObj = getRawData(row))
+ {
+ if (fsObj->isActive())
+ fillBackgroundDefaultColorAlternating(dc, rect, row % 2 == 0);
+ else
+ clearArea(dc, rect, COLOR_NOT_ACTIVE);
+ }
+ else
+ clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
+ }
+ }
+ else
+ clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
}
- virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected) override
+ virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected) override
{
- auto drawInactiveColumBackground = [&](const FileSystemObject& fsObj)
+ auto drawHighlightBackground = [&](const FileSystemObject& fsObj, const wxColor& col)
{
- if (fsObj.isActive())
- fillBackgroundDefaultColorAlternating(dc, rect, row % 2 == 0);
- else
- clearArea(dc, rect, COLOR_NOT_ACTIVE);
+ if (enabled && !selected && fsObj.isActive()) //coordinate with renderRowBackgound()!
+ clearArea(dc, rect, col);
};
switch (static_cast<ColumnTypeMiddle>(colType))
@@ -979,54 +992,46 @@ private:
case COL_TYPE_CHECKBOX:
if (const FileSystemObject* fsObj = getRawData(row))
{
- wxRect rectInside = rect;
-
- //if sync action is shown draw notch on left side, right side otherwise
- if (notch.GetHeight() != rectInside.GetHeight())
- notch.Rescale(notch.GetWidth(), rectInside.GetHeight());
- if (highlightSyncAction_)
- {
- drawBitmapRtlMirror(dc, notch, rectInside, wxALIGN_LEFT, buffer);
- rectInside.x += notch.GetWidth();
- rectInside.width -= notch.GetWidth();
- }
- else
- {
- //wxWidgets screws up again and has wxALIGN_RIGHT off by one pixel! -> use wxALIGN_LEFT instead
- wxRect rectNotch(rectInside.x + rectInside.width - notch.GetWidth(), rectInside.y,
- notch.GetWidth(), rectInside.height);
- drawBitmapRtlMirror(dc, notch, rectNotch, wxALIGN_LEFT, buffer);
- rectInside.width -= notch.GetWidth();
- }
-
const bool rowHighlighted = dragSelection ? row == dragSelection->first : highlight ? row == highlight->row_ : false;
const BlockPosition highlightBlock = dragSelection ? dragSelection->second : highlight ? highlight->blockPos_ : BLOCKPOS_CHECK_BOX;
if (rowHighlighted && highlightBlock == BLOCKPOS_CHECK_BOX)
- drawBitmapRtlMirror(dc, getResourceImage(fsObj->isActive() ? L"checkboxTrueFocus" : L"checkboxFalseFocus"), rectInside, wxALIGN_CENTER, buffer);
+ drawBitmapRtlMirror(dc, getResourceImage(fsObj->isActive() ? L"checkboxTrueFocus" : L"checkboxFalseFocus"), rect, wxALIGN_CENTER, buffer);
else //default
- drawBitmapRtlMirror(dc, getResourceImage(fsObj->isActive() ? L"checkboxTrue" : L"checkboxFalse" ), rectInside, wxALIGN_CENTER, buffer);
+ drawBitmapRtlMirror(dc, getResourceImage(fsObj->isActive() ? L"checkboxTrue" : L"checkboxFalse" ), rect, wxALIGN_CENTER, buffer);
}
break;
case COL_TYPE_CMP_CATEGORY:
if (const FileSystemObject* fsObj = getRawData(row))
{
- if (highlightSyncAction_)
- drawInactiveColumBackground(*fsObj);
+ if (!highlightSyncAction_)
+ drawHighlightBackground(*fsObj, getBackGroundColorCmpCategory(fsObj));
+
+ wxRect rectTmp = rect;
+ {
+ //draw notch on left side
+ if (notch.GetHeight() != rectTmp.GetHeight())
+ notch.Rescale(notch.GetWidth(), rectTmp.GetHeight());
+
+ //wxWidgets screws up again and has wxALIGN_RIGHT off by one pixel! -> use wxALIGN_LEFT instead
+ const wxRect rectNotch(rectTmp.x + rectTmp.width - notch.GetWidth(), rectTmp.y, notch.GetWidth(), rectTmp.height);
+ drawBitmapRtlMirror(dc, notch, rectNotch, wxALIGN_LEFT, buffer);
+ rectTmp.width -= notch.GetWidth();
+ }
if (!highlightSyncAction_)
- drawBitmapRtlMirror(dc, getCmpResultImage(fsObj->getCategory()), rect, wxALIGN_CENTER, buffer);
+ drawBitmapRtlMirror(dc, getCmpResultImage(fsObj->getCategory()), rectTmp, wxALIGN_CENTER, buffer);
else if (fsObj->getCategory() != FILE_EQUAL) //don't show = in both middle columns
- drawBitmapRtlMirror(dc, greyScale(getCmpResultImage(fsObj->getCategory())), rect, wxALIGN_CENTER, buffer);
+ drawBitmapRtlMirror(dc, greyScale(getCmpResultImage(fsObj->getCategory())), rectTmp, wxALIGN_CENTER, buffer);
}
break;
case COL_TYPE_SYNC_ACTION:
if (const FileSystemObject* fsObj = getRawData(row))
{
- if (!highlightSyncAction_)
- drawInactiveColumBackground(*fsObj);
+ if (highlightSyncAction_)
+ drawHighlightBackground(*fsObj, getBackGroundColorSyncAction(fsObj));
const bool rowHighlighted = dragSelection ? row == dragSelection->first : highlight ? row == highlight->row_ : false;
const BlockPosition highlightBlock = dragSelection ? dragSelection->second : highlight ? highlight->blockPos_ : BLOCKPOS_CHECK_BOX;
@@ -1620,14 +1625,14 @@ void gridview::init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, const std
//gridLeft .showScrollBars(Grid::SB_SHOW_AUTOMATIC, Grid::SB_SHOW_NEVER); -> redundant: configuration happens in GridEventManager::onAlignScrollBars()
//gridCenter.showScrollBars(Grid::SB_SHOW_NEVER, Grid::SB_SHOW_NEVER);
- const int widthCategory = 30;
const int widthCheckbox = getResourceImage(L"checkboxTrue").GetWidth() + 4 + getResourceImage(L"notch").GetWidth();
+ const int widthCategory = 30;
const int widthAction = 45;
gridCenter.SetSize(widthCategory + widthCheckbox + widthAction, -1);
std::vector<Grid::ColumnAttribute> attribMiddle;
- attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_CMP_CATEGORY), widthCategory, 0, true));
attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_CHECKBOX ), widthCheckbox, 0, true));
+ attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_CMP_CATEGORY), widthCategory, 0, true));
attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_SYNC_ACTION ), widthAction, 0, true));
gridCenter.setColumnConfig(attribMiddle);
}
diff --git a/FreeFileSync/Source/ui/dir_name.cpp b/FreeFileSync/Source/ui/dir_name.cpp
index 4926985c..ea8a9386 100644
--- a/FreeFileSync/Source/ui/dir_name.cpp
+++ b/FreeFileSync/Source/ui/dir_name.cpp
@@ -221,7 +221,7 @@ void DirectoryName<NameControl>::onSelectDir(wxCommandEvent& event)
'\x8d', '\xc2', '\xc', '\xa5', '\xef', '\x59', '\x6e', '\x3b'
}; //some random GUID => have Windows save IFileDialog state separately from other file/dir pickers!
- showFolderPicker(static_cast<HWND>(selectButton_.GetHWND()), //in; ==HWND
+ showFolderPicker(static_cast<HWND>(selectButton_.GetHWND()), //in; ==HWND
defaultdirpath.empty() ? static_cast<const wchar_t*>(nullptr) : defaultdirpath.c_str(), //in, optional!
&guid,
selectedFolder, //out: call freeString() after use!
diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp
index cb6ecf25..de75eb3f 100644
--- a/FreeFileSync/Source/ui/gui_status_handler.cpp
+++ b/FreeFileSync/Source/ui/gui_status_handler.cpp
@@ -385,7 +385,7 @@ ProcessCallback::Response SyncStatusHandler::reportError(const std::wstring& err
//auto-retry
if (retryNumber < automaticRetryCount_)
{
- errorLog.logMsg(errorMessage + L"\n=> " +
+ errorLog.logMsg(errorMessage + L"\n-> " +
_P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", automaticRetryDelay_), TYPE_INFO);
//delay
const int iterations = static_cast<int>(1000 * automaticRetryDelay_ / UI_UPDATE_INTERVAL); //always round down: don't allow for negative remaining time below
@@ -423,7 +423,7 @@ ProcessCallback::Response SyncStatusHandler::reportError(const std::wstring& err
case ConfirmationButton3::DONT_DO_IT: //retry
guardWriteLog.dismiss();
- errorLog.logMsg(errorMessage + L"\n=> " + _("Retrying operation..."), TYPE_INFO); //explain why there are duplicate "doing operation X" info messages in the log!
+ errorLog.logMsg(errorMessage + L"\n-> " + _("Retrying operation..."), TYPE_INFO); //explain why there are duplicate "doing operation X" info messages in the log!
return ProcessCallback::RETRY;
case ConfirmationButton3::CANCEL:
diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp
index e7f99fdf..69aa6fb6 100644
--- a/FreeFileSync/Source/ui/main_dlg.cpp
+++ b/FreeFileSync/Source/ui/main_dlg.cpp
@@ -474,7 +474,8 @@ MainDialog::MainDialog(const Zstring& globalConfigFile,
manualTimeSpanTo (0),
folderHistoryLeft (std::make_shared<FolderHistory>()), //make sure it is always bound
folderHistoryRight(std::make_shared<FolderHistory>()), //
- focusWindowAfterSearch(nullptr)
+ focusWindowAfterSearch(nullptr),
+ localKeyEventsEnabled(true)
{
m_directoryLeft ->init(folderHistoryLeft);
m_directoryRight->init(folderHistoryRight);
@@ -1547,6 +1548,8 @@ void MainDialog::disableAllElements(bool enableAbort)
//OS X: wxWidgets portability promise is again a mess: http://wxwidgets.10942.n7.nabble.com/Disable-panel-and-appropriate-children-windows-linux-macos-td35357.html
+ localKeyEventsEnabled = false;
+
m_menubar1->EnableTop(0, false);
m_menubar1->EnableTop(1, false);
m_menubar1->EnableTop(2, false);
@@ -1559,6 +1562,7 @@ void MainDialog::disableAllElements(bool enableAbort)
m_panelViewFilter ->Disable();
m_panelConfig ->Disable();
m_gridNavi ->Disable();
+ m_panelSearch ->Disable();
if (enableAbort)
{
@@ -1582,6 +1586,8 @@ void MainDialog::enableAllElements()
EnableCloseButton(true);
+ localKeyEventsEnabled = true;
+
m_menubar1->EnableTop(0, true);
m_menubar1->EnableTop(1, true);
m_menubar1->EnableTop(2, true);
@@ -1594,6 +1600,7 @@ void MainDialog::enableAllElements()
m_panelViewFilter ->Enable();
m_panelConfig ->Enable();
m_gridNavi ->Enable();
+ m_panelSearch ->Enable();
//show compare button
m_buttonCancel->Disable();
@@ -1845,6 +1852,12 @@ void MainDialog::onGridButtonEvent(wxKeyEvent& event, Grid& grid, bool leftSide)
void MainDialog::onLocalKeyEvent(wxKeyEvent& event) //process key events without explicit menu entry :)
{
+ if (!localKeyEventsEnabled)
+ {
+ event.Skip();
+ return;
+ }
+
const int keyCode = event.GetKeyCode();
//CTRL + X
@@ -1926,7 +1939,7 @@ void MainDialog::onLocalKeyEvent(wxKeyEvent& event) //process key events without
m_gridMainL->SetFocus();
event.SetEventType(wxEVT_KEY_DOWN); //the grid event handler doesn't expect wxEVT_CHAR_HOOK!
- evtHandler->ProcessEvent(event); //propagating event catched at wxTheApp to child leads to recursion, but we prevented it...
+ evtHandler->ProcessEvent(event); //propagating event catched at wxTheApp to child leads to recursion, but code in key_event.h prevents it...
event.Skip(false); //definitively handled now!
return;
}
@@ -4055,7 +4068,7 @@ void MainDialog::OnSearchPanelKeyPressed(wxKeyEvent& event)
switch (event.GetKeyCode())
{
case WXK_RETURN:
- case WXK_NUMPAD_ENTER: //catches ENTER keys while focus is on *any* part of m_panelSearch! Seems to obsolete OnHideSearchPanel()!
+ case WXK_NUMPAD_ENTER: //catches ENTER keys while focus is on *any* part of m_panelSearch! Seems to obsolete OnSearchGridEnter()!
startFindNext();
return;
case WXK_ESCAPE:
@@ -4096,7 +4109,8 @@ void MainDialog::hideFindPanel()
void MainDialog::startFindNext() //F3 or ENTER in m_textCtrlSearchTxt
{
- const wxString& searchString = m_textCtrlSearchTxt->GetValue();
+ wxString searchString = m_textCtrlSearchTxt->GetValue();
+ trim(searchString);
if (searchString.empty())
showFindPanel();
diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h
index 84a52e8b..a9becd95 100644
--- a/FreeFileSync/Source/ui/main_dlg.h
+++ b/FreeFileSync/Source/ui/main_dlg.h
@@ -319,6 +319,8 @@ private:
std::unique_ptr<zen::FilterConfig> filterCfgOnClipboard; //copy/paste of filter config
wxWindow* focusWindowAfterSearch; //used to restore focus after search panel is closed
+
+ bool localKeyEventsEnabled;
};
#endif //MAINDIALOG_H_891048132454564
diff --git a/FreeFileSync/Source/ui/osx_dock.h b/FreeFileSync/Source/ui/osx_dock.h
new file mode 100644
index 00000000..62237524
--- /dev/null
+++ b/FreeFileSync/Source/ui/osx_dock.h
@@ -0,0 +1,17 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef OSX_DOCK_837210847312534
+#define OSX_DOCK_837210847312534
+
+#include <zen/sys_error.h>
+
+namespace osx
+{
+void dockIconSetText(const char* str); //throw SysError
+}
+
+#endif //OSX_DOCK_837210847312534
diff --git a/FreeFileSync/Source/ui/osx_dock.mm b/FreeFileSync/Source/ui/osx_dock.mm
new file mode 100644
index 00000000..8f42ae88
--- /dev/null
+++ b/FreeFileSync/Source/ui/osx_dock.mm
@@ -0,0 +1,24 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#include "osx_dock.h"
+#include <Cocoa/Cocoa.h>
+#include <zen/osx_throw_exception.h>
+
+
+void osx::dockIconSetText(const char* str) //throw SysError
+{
+ @try
+ {
+ NSString* label = [NSString stringWithCString:str encoding:NSUTF8StringEncoding];
+ //stringWithCString returns string which is already set to autorelease!
+ [[NSApp dockTile] setBadgeLabel:label]; //label may be nil
+ }
+ @catch (NSException* e)
+ {
+ throwSysError(e); //throw SysError
+ }
+}
diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp
index 76117f6c..dc1f42a6 100644
--- a/FreeFileSync/Source/ui/progress_indicator.cpp
+++ b/FreeFileSync/Source/ui/progress_indicator.cpp
@@ -515,7 +515,7 @@ public:
return wxEmptyString;
}
- virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected) override
+ virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected) override
{
wxRect rectTmp = rect;
@@ -954,24 +954,24 @@ private:
class CurveDataRectangleArea : public CurveData
{
public:
- CurveDataRectangleArea () : x(0), y(0) {}
+ CurveDataRectangleArea() : x_(), y_() {}
- void setValue (double xVal, double yVal) { x = xVal; y = yVal; }
- void setValueX(double xVal) { x = xVal; }
- double getValueX() const { return x; }
+ void setValue (double x, double y) { x_ = x; y_ = y; }
+ void setValueX(double x) { x_ = x; }
+ double getValueX() const { return x_; }
private:
- virtual std::pair<double, double> getRangeX() const override { return std::make_pair(x, x); } //conceptually just a vertical line!
+ virtual std::pair<double, double> getRangeX() const override { return std::make_pair(x_, x_); } //conceptually just a vertical line!
virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector<CurvePoint>& points) const override
{
- points.push_back(CurvePoint(0, y));
- points.push_back(CurvePoint(x, y));
- points.push_back(CurvePoint(x, 0));
+ points.push_back(CurvePoint(0, y_));
+ points.push_back(CurvePoint(x_, y_));
+ points.push_back(CurvePoint(x_, 0));
}
- double x; //time elapsed in seconds
- double y; //items/bytes processed
+ double x_; //time elapsed in seconds
+ double y_; //items/bytes processed
};
@@ -1375,9 +1375,8 @@ void SyncProgressDialogImpl<TopLevelDialog>::initNewPhase()
curveDataItemsCurrent->setValue(0, 0);
curveDataBytesTotal ->setValue(0, 0);
curveDataItemsTotal ->setValue(0, 0);
-
- curveDataBytes->clear();
- curveDataItems->clear();
+ curveDataBytes ->clear();
+ curveDataItems ->clear();
notifyProgressChange(); //make sure graphs get initial values
@@ -1403,9 +1402,14 @@ void SyncProgressDialogImpl<TopLevelDialog>::notifyProgressChange() //noexcept!
break;
case ProcessCallback::PHASE_COMPARING_CONTENT:
case ProcessCallback::PHASE_SYNCHRONIZING:
- curveDataBytes->addRecord(timeElapsed.timeMs(), syncStat_->getDataCurrent (syncStat_->currentPhase()));
- curveDataItems->addRecord(timeElapsed.timeMs(), syncStat_->getObjectsCurrent(syncStat_->currentPhase()));
- break;
+ {
+ const std::int64_t dataCurrent = syncStat_->getDataCurrent (syncStat_->currentPhase());
+ const int itemsCurrent = syncStat_->getObjectsCurrent(syncStat_->currentPhase());
+
+ curveDataBytes->addRecord(timeElapsed.timeMs(), dataCurrent);
+ curveDataItems->addRecord(timeElapsed.timeMs(), itemsCurrent);
+ }
+ break;
}
}
@@ -1538,10 +1542,10 @@ void SyncProgressDialogImpl<TopLevelDialog>::updateGuiInt(bool allowYield)
case ProcessCallback::PHASE_COMPARING_CONTENT:
case ProcessCallback::PHASE_SYNCHRONIZING:
{
- const int itemsCurrent = syncStat_->getObjectsCurrent(syncStat_->currentPhase());
- const int itemsTotal = syncStat_->getObjectsTotal (syncStat_->currentPhase());
const std::int64_t dataCurrent = syncStat_->getDataCurrent (syncStat_->currentPhase());
const std::int64_t dataTotal = syncStat_->getDataTotal (syncStat_->currentPhase());
+ const int itemsCurrent = syncStat_->getObjectsCurrent(syncStat_->currentPhase());
+ const int itemsTotal = syncStat_->getObjectsTotal (syncStat_->currentPhase());
//add both data + obj-count, to handle "deletion-only" cases
const double fraction = dataTotal + itemsTotal == 0 ? 1 : std::max(0.0, 1.0 * (dataCurrent + itemsCurrent) / (dataTotal + itemsTotal));
diff --git a/FreeFileSync/Source/ui/taskbar.cpp b/FreeFileSync/Source/ui/taskbar.cpp
index d65d1fbc..9e7cbcde 100644
--- a/FreeFileSync/Source/ui/taskbar.cpp
+++ b/FreeFileSync/Source/ui/taskbar.cpp
@@ -30,15 +30,15 @@ using namespace tbseven;
class Taskbar::Pimpl //throw TaskbarNotAvailable
{
public:
- Pimpl(const wxFrame& window) :
- assocWindow(window.GetHWND()),
- setStatus_ (getDllName(), funName_setStatus),
- setProgress_(getDllName(), funName_setProgress)
+ Pimpl(const wxFrame& window) : assocWindow(window.GetHWND())
{
- if (!assocWindow || !setProgress_ || !setStatus_)
+ if (!win7OrLater()) //check *before* trying to load DLL
throw TaskbarNotAvailable();
- if (!zen::win7OrLater())
+ setStatus_ = DllFun<FunType_setStatus >(getDllName(), funName_setStatus);
+ setProgress_ = DllFun<FunType_setProgress>(getDllName(), funName_setProgress);
+
+ if (!assocWindow || !setStatus_ || !setProgress_)
throw TaskbarNotAvailable();
}
@@ -72,9 +72,9 @@ public:
}
private:
- void* assocWindow; //HWND
- const DllFun<FunType_setStatus> setStatus_;
- const DllFun<FunType_setProgress> setProgress_;
+ void* const assocWindow; //HWND
+ DllFun<FunType_setStatus> setStatus_;
+ DllFun<FunType_setProgress> setProgress_;
};
#elif defined HAVE_UBUNTU_UNITY //Ubuntu unity
diff --git a/FreeFileSync/Source/ui/tray_icon.cpp b/FreeFileSync/Source/ui/tray_icon.cpp
index 71ee7e20..c9debfb1 100644
--- a/FreeFileSync/Source/ui/tray_icon.cpp
+++ b/FreeFileSync/Source/ui/tray_icon.cpp
@@ -56,7 +56,7 @@ wxIcon generateProgressIcon(const wxImage& logo, double fraction) //generate ico
return wxIcon();
const int pixelCount = logo.GetWidth() * logo.GetHeight();
- const int startFillPixel = numeric::confineCpy(numeric::round(fraction * pixelCount), 0, pixelCount);
+ const int startFillPixel = numeric::clampCpy(numeric::round(fraction * pixelCount), 0, pixelCount);
//minor optimization
static std::pair<int, wxIcon> buffer = std::make_pair(-1, wxNullIcon);
diff --git a/FreeFileSync/Source/ui/tree_view.cpp b/FreeFileSync/Source/ui/tree_view.cpp
index 9768c01a..de952465 100644
--- a/FreeFileSync/Source/ui/tree_view.cpp
+++ b/FreeFileSync/Source/ui/tree_view.cpp
@@ -866,7 +866,7 @@ private:
clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
}
- virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected) override
+ virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected) override
{
//wxRect rectTmp= drawCellBorder(dc, rect);
wxRect rectTmp = rect;
diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h
index a1def1a9..558b09ea 100644
--- a/FreeFileSync/Source/version/version.h
+++ b/FreeFileSync/Source/version/version.h
@@ -3,7 +3,7 @@
namespace zen
{
-const wchar_t currentVersion[] = L"6.9"; //internal linkage!
+const wchar_t currentVersion[] = L"6.10"; //internal linkage!
}
#endif
diff --git a/wx+/graph.cpp b/wx+/graph.cpp
index 22358922..f4e758c1 100644
--- a/wx+/graph.cpp
+++ b/wx+/graph.cpp
@@ -19,7 +19,8 @@ using namespace zen;
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
+//for some buggy reason MSVC isn't able to use a temporary as a default argument
+const std::shared_ptr<LabelFormatter> Graph2D::MainAttributes::defaultFormat = std::make_shared<DecimalNumberFormatter>();
double zen::nextNiceNumber(double blockSize) //round to next number which is a convenient to read block size
@@ -82,7 +83,7 @@ public:
outOfBoundsLow (-1 * scaleToReal + valMin),
outOfBoundsHigh((screenSize + 1) * scaleToReal + valMin) { if (outOfBoundsLow > outOfBoundsHigh) std::swap(outOfBoundsLow, outOfBoundsHigh); }
- double screenToReal(double screenPos) const //input value: [0, screenSize - 1]
+ double screenToReal(double screenPos) const //map [0, screenSize] -> [valMin, valMax]
{
return screenPos * scaleToReal + min_;
}
@@ -93,7 +94,7 @@ public:
int realToScreenRound(double realPos) const //returns -1 and screenSize + 1 if out of bounds!
{
//catch large double values: if double is larger than what int can represent => undefined behavior!
- numeric::confine(realPos , outOfBoundsLow, outOfBoundsHigh);
+ numeric::clamp(realPos , outOfBoundsLow, outOfBoundsHigh);
return numeric::round(realToScreen(realPos));
}
@@ -120,9 +121,14 @@ void widenRange(double& valMin, double& valMax, //in/out
valRangePerBlock = labelFmt.getOptimalBlockSize(valRangePerBlock);
if (!numeric::isNull(valRangePerBlock))
{
- valMin = std::floor(valMin / valRangePerBlock) * valRangePerBlock;
- valMax = std::ceil (valMax / valRangePerBlock) * valRangePerBlock;
- blockCount = numeric::round((valMax - valMin) / valRangePerBlock); //"round" to avoid IEEE 754 surprises
+ int blockMin = std::floor(valMin / valRangePerBlock);
+ int blockMax = std::ceil (valMax / valRangePerBlock);
+ if (blockMin == blockMax) //handle valMin == valMax == integer
+ ++blockMax;
+
+ valMin = blockMin * valRangePerBlock;
+ valMax = blockMax * valRangePerBlock;
+ blockCount = blockMax - blockMin;
return;
}
}
@@ -670,10 +676,10 @@ void Graph2D::render(wxDC& dc) const
const wxPoint screenCurrent = activeSel->refCurrentPos() - graphAreaOrigin;
//normalize positions: a mouse selection is symmetric and *not* an half-open range!
- double screenFromX = confineCpy(screenStart .x, 0, graphArea.width - 1);
- double screenFromY = confineCpy(screenStart .y, 0, graphArea.height - 1);
- double screenToX = confineCpy(screenCurrent.x, 0, graphArea.width - 1);
- double screenToY = confineCpy(screenCurrent.y, 0, graphArea.height - 1);
+ double screenFromX = clampCpy(screenStart .x, 0, graphArea.width - 1);
+ double screenFromY = clampCpy(screenStart .y, 0, graphArea.height - 1);
+ double screenToX = clampCpy(screenCurrent.x, 0, graphArea.width - 1);
+ double screenToY = clampCpy(screenCurrent.y, 0, graphArea.height - 1);
widen(&screenFromX, &screenToX); //use full pixel range for selection!
widen(&screenFromY, &screenToY);
@@ -731,10 +737,10 @@ void Graph2D::render(wxDC& dc) const
shrink(&screenFromX, &screenToX);
shrink(&screenFromY, &screenToY);
- confine(screenFromX, 0.0, graphArea.width - 1.0);
- confine(screenFromY, 0.0, graphArea.height - 1.0);
- confine(screenToX, 0.0, graphArea.width - 1.0);
- confine(screenToY, 0.0, graphArea.height - 1.0);
+ clamp(screenFromX, 0.0, graphArea.width - 1.0);
+ clamp(screenFromY, 0.0, graphArea.height - 1.0);
+ clamp(screenToX, 0.0, graphArea.width - 1.0);
+ clamp(screenToY, 0.0, graphArea.height - 1.0);
const wxPoint pixelFrom = wxPoint(numeric::round(screenFromX),
numeric::round(screenFromY)) + graphAreaOrigin;
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index 248a82ef..5bcac1a5 100644
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -79,7 +79,7 @@ void GridData::renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool
}
-void GridData::renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected)
+void GridData::renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected)
{
wxRect rectTmp = drawCellBorder(dc, rect);
@@ -938,7 +938,7 @@ private:
{
const wxRect cellRect(cellAreaTL.x, cellAreaTL.y + row * rowHeight, cw.width_, rowHeight);
RecursiveDcClipper dummy3(dc, cellRect);
- prov->renderCell(dc, cellRect, row, cw.type_, drawAsSelected(row));
+ prov->renderCell(dc, cellRect, row, cw.type_, refParent().IsThisEnabled(), drawAsSelected(row));
}
cellAreaTL.x += cw.width_;
}
@@ -1161,7 +1161,7 @@ private:
{
//select current row *after* scrolling
wxPoint clientPosTrimmed = clientPos;
- numeric::confine(clientPosTrimmed.y, 0, clientSize.GetHeight() - 1); //do not select row outside client window!
+ numeric::clamp(clientPosTrimmed.y, 0, clientSize.GetHeight() - 1); //do not select row outside client window!
const wxPoint absPos = wnd_.refParent().CalcUnscrolledPosition(clientPosTrimmed);
const ptrdiff_t newRow = wnd_.rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
@@ -1310,8 +1310,8 @@ void Grid::updateWindowSizes(bool updateScrollbar)
{
ptrdiff_t yFrom = CalcUnscrolledPosition(wxPoint(0, 0)).y;
ptrdiff_t yTo = CalcUnscrolledPosition(wxPoint(0, mainWinHeightGross - 1)).y ;
- numeric::confine<ptrdiff_t>(yFrom, 0, logicalHeight - 1);
- numeric::confine<ptrdiff_t>(yTo, 0, logicalHeight - 1);
+ numeric::clamp<ptrdiff_t>(yFrom, 0, logicalHeight - 1);
+ numeric::clamp<ptrdiff_t>(yTo, 0, logicalHeight - 1);
const ptrdiff_t rowFrom = rowLabelWin_->getRowAtPos(yFrom);
const ptrdiff_t rowTo = rowLabelWin_->getRowAtPos(yTo);
@@ -1416,8 +1416,8 @@ wxSize Grid::GetSizeAvailableForScrollTarget(const wxSize& size)
{
ptrdiff_t yFrom = CalcUnscrolledPosition(wxPoint(0, 0)).y;
ptrdiff_t yTo = CalcUnscrolledPosition(wxPoint(0, mainWinHeightGross - 1)).y ;
- numeric::confine<ptrdiff_t>(yFrom, 0, logicalHeight - 1);
- numeric::confine<ptrdiff_t>(yTo, 0, logicalHeight - 1);
+ numeric::clamp<ptrdiff_t>(yFrom, 0, logicalHeight - 1);
+ numeric::clamp<ptrdiff_t>(yTo, 0, logicalHeight - 1);
const ptrdiff_t rowFrom = rowLabelWin_->getRowAtPos(yFrom);
const ptrdiff_t rowTo = rowLabelWin_->getRowAtPos(yTo);
@@ -1454,7 +1454,7 @@ void Grid::onKeyDown(wxKeyEvent& event)
{
if (rowCount > 0)
{
- numeric::confine<ptrdiff_t>(row, 0, rowCount - 1);
+ numeric::clamp<ptrdiff_t>(row, 0, rowCount - 1);
setGridCursor(row);
}
};
@@ -1463,7 +1463,7 @@ void Grid::onKeyDown(wxKeyEvent& event)
{
if (rowCount > 0)
{
- numeric::confine<ptrdiff_t>(row, 0, rowCount - 1);
+ numeric::clamp<ptrdiff_t>(row, 0, rowCount - 1);
selectWithCursor(row);
}
};
@@ -2009,8 +2009,8 @@ void Grid::selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positiv
auto rowLast = std::max(rowFrom, rowTo) + 1;
const size_t rowCount = getRowCount();
- numeric::confine<ptrdiff_t>(rowFirst, 0, rowCount);
- numeric::confine<ptrdiff_t>(rowLast, 0, rowCount);
+ numeric::clamp<ptrdiff_t>(rowFirst, 0, rowCount);
+ numeric::clamp<ptrdiff_t>(rowLast, 0, rowCount);
selection.selectRange(rowFirst, rowLast, positive);
diff --git a/wx+/grid.h b/wx+/grid.h
index 6b6ac8a0..304932ac 100644
--- a/wx+/grid.h
+++ b/wx+/grid.h
@@ -92,8 +92,8 @@ public:
//grid area
virtual wxString getValue(size_t row, ColumnType colType) const = 0;
- virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected); //default implementation
- virtual void renderCell (wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected); //
+ virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected); //default implementation
+ virtual void renderCell (wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected); //
virtual int getBestSize (wxDC& dc, size_t row, ColumnType colType); //must correspond to renderCell()!
virtual wxString getToolTip(size_t row, ColumnType colType) const { return wxString(); }
@@ -250,8 +250,8 @@ private:
{
if (rowFirst <= rowLast)
{
- numeric::confine<size_t>(rowFirst, 0, rowSelectionValue.size());
- numeric::confine<size_t>(rowLast, 0, rowSelectionValue.size());
+ numeric::clamp<size_t>(rowFirst, 0, rowSelectionValue.size());
+ numeric::clamp<size_t>(rowLast, 0, rowSelectionValue.size());
std::fill(rowSelectionValue.begin() + rowFirst, rowSelectionValue.begin() + rowLast, positive);
}
diff --git a/zen/basic_math.h b/zen/basic_math.h
index 56cfd923..69e861be 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -32,9 +32,9 @@ template <class T>
const T& max(const T& a, const T& b, const T& c);
template <class T>
-void confine(T& val, const T& minVal, const T& maxVal); //make sure minVal <= val && val <= maxVal
+void clamp(T& val, const T& minVal, const T& maxVal); //make sure minVal <= val && val <= maxVal
template <class T>
-T confineCpy(const T& val, const T& minVal, const T& maxVal);
+T clampCpy(const T& val, const T& minVal, const T& maxVal);
template <class T, class InputIterator> //precondition: range must be sorted!
auto nearMatch(const T& val, InputIterator first, InputIterator last) -> typename std::iterator_traits<InputIterator>::value_type;
@@ -134,7 +134,7 @@ const T& max(const T& a, const T& b, const T& c)
template <class T> inline
-T confineCpy(const T& val, const T& minVal, const T& maxVal)
+T clampCpy(const T& val, const T& minVal, const T& maxVal)
{
assert(minVal <= maxVal);
if (val < minVal)
@@ -145,7 +145,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, clamp?
+void clamp(T& val, const T& minVal, const T& maxVal)
{
assert(minVal <= maxVal);
if (val < minVal)
diff --git a/zen/dll.h b/zen/dll.h
new file mode 100644
index 00000000..f6422fa7
--- /dev/null
+++ b/zen/dll.h
@@ -0,0 +1,121 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef DLLLOADER_H_4239582598670968
+#define DLLLOADER_H_4239582598670968
+
+#include <memory>
+#ifdef ZEN_WIN
+#include <string>
+#include "scope_guard.h"
+#include "win.h" //includes "windows.h"
+
+#elif defined ZEN_LINUX || defined ZEN_MAC
+#include <dlfcn.h>
+#endif
+
+namespace zen
+{
+/*
+Manage DLL function and library ownership
+ - thread safety: like built-in type
+ - full value semantics
+
+ Usage:
+ typedef BOOL (WINAPI* FunType_IsWow64Process)(HANDLE hProcess, PBOOL Wow64Process);
+ const zen::SysDllFun<FunType_IsWow64Process> isWow64Process(L"kernel32.dll", "IsWow64Process");
+ if (isWow64Process) ... use function ptr ...
+
+ Usage 2:
+ #define DEF_DLL_FUN(name) DllFun<dll_ns::FunType_##name> name(dll_ns::getDllName(), dll_ns::funName_##name);
+ DEF_DLL_FUN(funname1); DEF_DLL_FUN(funname2); DEF_DLL_FUN(funname3);
+*/
+
+template <class Func>
+class DllFun
+{
+public:
+ DllFun() : fun(nullptr) {}
+
+#ifdef ZEN_WIN
+ DllFun(const wchar_t* libraryName, const char* functionName) :
+ hLibRef(::LoadLibrary(libraryName), ::FreeLibrary),
+ fun(hLibRef ? reinterpret_cast<Func>(::GetProcAddress(static_cast<HMODULE>(hLibRef.get()), functionName)) : nullptr) {}
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ DllFun(const char* libraryName, const char* functionName) :
+ hLibRef(::dlopen(libraryName, RTLD_LAZY), ::dlclose),
+ fun(hLibRef ? reinterpret_cast<Func>(::dlsym(hLibRef.get(), functionName)) : nullptr) {}
+#endif
+ operator Func() const { return fun; }
+
+private:
+ std::shared_ptr<void> hLibRef; //we would prefer decltype(*HMODULE()) if only it would work...
+ Func fun;
+};
+
+
+#ifdef ZEN_WIN
+//if the dll is already part of the process space, e.g. "kernel32.dll" or "shell32.dll", we can use a faster variant:
+//NOTE: since the lifetime of the referenced library is *not* controlled, this is safe to use only for permanently loaded libraries like these!
+template <class Func>
+class SysDllFun
+{
+public:
+ SysDllFun() : fun(nullptr) {}
+
+ SysDllFun(const wchar_t* systemLibrary, const char* functionName)
+ {
+ HMODULE mod = ::GetModuleHandle(systemLibrary);
+ fun = mod ? reinterpret_cast<Func>(::GetProcAddress(mod, functionName)) : nullptr;
+ }
+
+ operator Func() const { return fun; }
+
+private:
+ Func fun;
+};
+
+/*
+extract binary resources from .exe/.dll:
+
+-- resource.h --
+#define MY_BINARY_RESOURCE 1337
+
+-- resource.rc --
+MY_BINARY_RESOURCE RCDATA "filename.dat"
+*/
+std::string getResourceStream(const std::wstring& libraryName, size_t resourceId);
+#endif
+
+
+
+
+
+
+
+
+
+
+//--------------- implementation---------------------------------------------------
+#ifdef ZEN_WIN
+inline
+std::string getResourceStream(const wchar_t* libraryName, size_t resourceId)
+{
+ if (HMODULE module = ::LoadLibrary(libraryName))
+ {
+ ZEN_ON_SCOPE_EXIT(::FreeLibrary(module));
+
+ if (HRSRC res = ::FindResource(module, MAKEINTRESOURCE(resourceId), RT_RCDATA))
+ if (HGLOBAL resHandle = ::LoadResource(module, res))
+ if (const char* stream = static_cast<const char*>(::LockResource(resHandle)))
+ return std::string(stream, static_cast<size_t>(::SizeofResource(module, res))); //size is 0 on error
+ }
+ return std::string();
+}
+#endif
+}
+
+#endif //DLLLOADER_H_4239582598670968
diff --git a/zen/file_error.h b/zen/file_error.h
index 73cfa17a..9276e8c5 100644
--- a/zen/file_error.h
+++ b/zen/file_error.h
@@ -38,10 +38,10 @@ DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume);
//CAVEAT: evalulate global error code *before* "throw" statement which may overwrite error code
//due to a memory allocation before it creates the thrown instance! (e.g. affects MinGW + Win XP!!!)
-inline
+template <class FE = FileError> inline
void throwFileError(const std::wstring& msg, const std::wstring& functionName, const ErrorCode ec) //throw FileError
{
- throw FileError(msg, formatSystemError(functionName, ec));
+ throw FE(msg, formatSystemError(functionName, ec));
}
diff --git a/zen/i18n.h b/zen/i18n.h
index 5aedc41c..3790e528 100644
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -11,7 +11,7 @@
#include <memory>
#include <cstdint>
#include "string_tools.h"
-
+#include "format_unit.h"
//minimal layer enabling text translation - without platform/library dependencies!
#ifdef __WXMSW__ //we have wxWidgets
@@ -75,7 +75,7 @@ std::wstring translate(const std::wstring& singular, const std::wstring& plural,
return translation;
}
else
- return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", zen::numberTo<std::wstring>(n));
+ return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n));
}
template <class T> inline
diff --git a/zen/osx_string.h b/zen/osx_string.h
new file mode 100644
index 00000000..ba83ca27
--- /dev/null
+++ b/zen/osx_string.h
@@ -0,0 +1,82 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef OSX_STRING_1873641732143214324
+#define OSX_STRING_1873641732143214324
+
+#include <CoreFoundation/CoreFoundation.h> //CFString
+#include "zstring.h"
+
+namespace osx
+{
+Zstring cfStringToZstring(const CFStringRef& cfStr);
+
+CFStringRef createCFString (const char* utf8Str); //returns nullptr on error
+CFMutableStringRef createMutableCFString(const char* utf8Str); //pass ownership! => ZEN_ON_SCOPE_EXIT(::CFRelease(str));
+
+
+
+
+
+
+
+
+
+
+
+
+
+//################# implementation #####################
+inline
+Zstring cfStringToZstring(const CFStringRef& cfStr)
+{
+ if (cfStr)
+ {
+ //perf: try to get away cheap:
+ if (const char* utf8Str = ::CFStringGetCStringPtr(cfStr, kCFStringEncodingUTF8))
+ return utf8Str;
+
+ CFIndex length = ::CFStringGetLength(cfStr);
+ if (length > 0)
+ {
+ CFIndex bufferSize = ::CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
+ Zstring buffer;
+ buffer.resize(bufferSize);
+
+ if (::CFStringGetCString(cfStr, &*buffer.begin(), bufferSize, kCFStringEncodingUTF8))
+ {
+ buffer.resize(zen::strLength(buffer.c_str())); //caveat: memory consumption of returned string!
+ return buffer;
+ }
+ }
+ }
+ return Zstring();
+}
+
+
+inline
+CFStringRef createCFString(const char* utf8Str)
+{
+ //don't bother with CFStringCreateWithBytes: it's slightly slower, despite passing length info
+ return ::CFStringCreateWithCString(nullptr, //CFAllocatorRef alloc,
+ utf8Str, //const char *cStr,
+ kCFStringEncodingUTF8); //CFStringEncoding encoding
+}
+
+
+inline
+CFMutableStringRef createMutableCFString(const char* utf8Str)
+{
+ if (CFMutableStringRef strRef = ::CFStringCreateMutable(NULL, 0))
+ {
+ ::CFStringAppendCString(strRef, utf8Str, kCFStringEncodingUTF8);
+ return strRef;
+ }
+ return nullptr;
+}
+}
+
+#endif //OSX_STRING_1873641732143214324
diff --git a/zen/osx_throw_exception.h b/zen/osx_throw_exception.h
new file mode 100644
index 00000000..07e3af3e
--- /dev/null
+++ b/zen/osx_throw_exception.h
@@ -0,0 +1,56 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef OSX_EXCEPTION_89274305834255
+#define OSX_EXCEPTION_89274305834255
+
+#import <Cocoa/Cocoa.h>
+#include "sys_error.h"
+#include "utf.h"
+
+namespace osx
+{
+//for use in Objective C implementation files only!
+void throwSysError(NSException* e); //throw SysError
+
+#define ZEN_OSX_ASSERT(obj) ZEN_OSX_ASSERT_IMPL(obj, #obj) //throw SysError
+/*
+Example: ZEN_OSX_ASSERT(obj);
+
+Equivalent to:
+ if (!obj)
+ throw zen::SysError(L"Assertion failed: \"obj\".");
+*/
+
+
+
+
+
+
+//######################## implmentation ############################
+inline
+void throwSysError(NSException* e) //throw SysError
+{
+ std::string msg;
+ if (const char* name = [[e name ] cStringUsingEncoding:NSUTF8StringEncoding]) //"const char*" NOT owned by us!
+ msg += name;
+ if (const char* descr = [[e reason] cStringUsingEncoding:NSUTF8StringEncoding])
+ {
+ msg += "\n";
+ msg += descr;
+ }
+ throw zen::SysError(zen::utfCvrtTo<std::wstring>(msg));
+ /*
+ e.g.
+ NSInvalidArgumentException
+ *** +[NSString stringWithCString:encoding:]: NULL cString
+ */
+}
+}
+
+#define ZEN_OSX_ASSERT_IMPL(obj, txt) if (!(obj)) throw zen::SysError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\".");
+
+#endif //OSX_EXCEPTION_89274305834255
diff --git a/zen/privilege.cpp b/zen/privilege.cpp
new file mode 100644
index 00000000..c2db4701
--- /dev/null
+++ b/zen/privilege.cpp
@@ -0,0 +1,144 @@
+#include "privilege.h"
+#include <map>
+//#include <mutex>
+#include "win.h" //includes "windows.h"
+#include "thread.h"
+#include "zstring.h"
+#include "scope_guard.h"
+#include "win_ver.h"
+
+using namespace zen;
+
+
+namespace
+{
+bool privilegeIsActive(const wchar_t* privilege) //throw FileError
+{
+ HANDLE hToken = nullptr;
+ if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle,
+ TOKEN_QUERY, //__in DWORD DesiredAccess,
+ &hToken)) //__out PHANDLE TokenHandle
+ throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"OpenProcessToken", getLastError());
+ ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken));
+
+ LUID luid = {};
+ if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName,
+ privilege, //__in LPCTSTR lpName,
+ &luid )) //__out PLUID lpLuid
+ throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"LookupPrivilegeValue", getLastError());
+
+ PRIVILEGE_SET priv = {};
+ priv.PrivilegeCount = 1;
+ priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
+ priv.Privilege[0].Luid = luid;
+ priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ BOOL alreadyGranted = FALSE;
+ if (!::PrivilegeCheck(hToken, //__in HANDLE ClientToken,
+ &priv, //__inout PPRIVILEGE_SET RequiredPrivileges,
+ &alreadyGranted)) //__out LPBOOL pfResult
+ throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"PrivilegeCheck", getLastError());
+
+ return alreadyGranted != FALSE;
+}
+
+
+void setPrivilege(const wchar_t* privilege, bool enable) //throw FileError
+{
+ HANDLE hToken = nullptr;
+ if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle,
+ TOKEN_ADJUST_PRIVILEGES, //__in DWORD DesiredAccess,
+ &hToken)) //__out PHANDLE TokenHandle
+ throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"OpenProcessToken", getLastError());
+ ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken));
+
+ LUID luid = {};
+ if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName,
+ privilege, //__in LPCTSTR lpName,
+ &luid )) //__out PLUID lpLuid
+ throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"LookupPrivilegeValue", getLastError());
+
+ TOKEN_PRIVILEGES tp = {};
+ tp.PrivilegeCount = 1;
+ tp.Privileges[0].Luid = luid;
+ tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
+
+ if (!::AdjustTokenPrivileges(hToken, //__in HANDLE TokenHandle,
+ false, //__in BOOL DisableAllPrivileges,
+ &tp, //__in_opt PTOKEN_PRIVILEGES NewState,
+ 0, //__in DWORD BufferLength,
+ nullptr, //__out_opt PTOKEN_PRIVILEGES PreviousState,
+ nullptr)) //__out_opt PDWORD ReturnLength
+ throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"AdjustTokenPrivileges", getLastError());
+
+ DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls!
+ if (lastError == ERROR_NOT_ALL_ASSIGNED) //check although previous function returned with success!
+ {
+#ifdef __MINGW32__ //Shobjidl.h
+#define ERROR_ELEVATION_REQUIRED 740L
+#endif
+ if (vistaOrLater()) //replace this useless error code with what it *really* means!
+ lastError = ERROR_ELEVATION_REQUIRED;
+
+ throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"AdjustTokenPrivileges", lastError);
+ }
+}
+
+
+class Privileges
+{
+public:
+ static Privileges& getInstance()
+ {
+ //meyers singleton: avoid static initialization order problem in global namespace!
+ static Privileges inst;
+ return inst;
+ }
+
+ void ensureActive(const wchar_t* privilege) //throw FileError
+ {
+ boost::lock_guard<boost::mutex> dummy(lockPrivileges);
+
+ if (activePrivileges.find(privilege) != activePrivileges.end())
+ return; //privilege already active
+
+ if (privilegeIsActive(privilege)) //privilege was already active before starting this tool
+ activePrivileges.insert(std::make_pair(privilege, false));
+ else
+ {
+ setPrivilege(privilege, true);
+ activePrivileges.insert(std::make_pair(privilege, true));
+ }
+ }
+
+private:
+ Privileges() {}
+ Privileges (const Privileges&) = delete;
+ Privileges& operator=(const Privileges&) = delete;
+
+ ~Privileges() //clean up: deactivate all privileges that have been activated by this application
+ {
+ for (const auto& priv : activePrivileges)
+ if (priv.second)
+ {
+ try
+ {
+ setPrivilege(priv.first.c_str(), false); //throw FileError
+ }
+ catch (FileError&) {}
+ }
+ }
+
+ std::map<Zstring, bool> activePrivileges; //bool: enabled by this application
+boost::mutex lockPrivileges;
+};
+
+//caveat: function scope static initialization is not thread-safe in VS 2010!
+auto& dummy = Privileges::getInstance();
+}
+
+
+void zen::activatePrivilege(const wchar_t* privilege) //throw FileError
+{
+ Privileges::getInstance().ensureActive(privilege);
+}
diff --git a/zen/privilege.h b/zen/privilege.h
new file mode 100644
index 00000000..e9b83be9
--- /dev/null
+++ b/zen/privilege.h
@@ -0,0 +1,17 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef PRIVILEGE_H_INCLUDED
+#define PRIVILEGE_H_INCLUDED
+
+#include "file_error.h"
+
+namespace zen
+{
+void activatePrivilege(const wchar_t* privilege); //throw FileError; thread-safe!!!
+}
+
+#endif // PRIVILEGE_H_INCLUDED
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index 21833492..4106ed02 100644
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -157,6 +157,13 @@ Zstring getResolvedFilePath_impl(const Zstring& linkPath) //throw FileError
{
using namespace zen;
#ifdef ZEN_WIN
+ //GetFinalPathNameByHandle() is not available before Vista!
+ typedef DWORD (WINAPI* GetFinalPathNameByHandleWFunc)(HANDLE hFile, LPTSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags);
+ const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW");
+ if (!getFinalPathNameByHandle)
+ throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\""));
+
+
const HANDLE hDir = ::CreateFile(applyLongPathPrefix(linkPath).c_str(), //_In_ LPCTSTR lpFileName,
0, //_In_ DWORD dwDesiredAccess,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
@@ -169,13 +176,6 @@ Zstring getResolvedFilePath_impl(const Zstring& linkPath) //throw FileError
throwFileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), L"CreateFile", getLastError());
ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir));
- //GetFinalPathNameByHandle() is not available before Vista!
- typedef DWORD (WINAPI* GetFinalPathNameByHandleWFunc)(HANDLE hFile, LPTSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags);
- const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW");
-
- if (!getFinalPathNameByHandle)
- throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\""));
-
const DWORD bufferSize = getFinalPathNameByHandle(hDir, nullptr, 0, 0);
if (bufferSize == 0)
throwFileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), L"GetFinalPathNameByHandle", getLastError());
diff --git a/zen/win_ver.h b/zen/win_ver.h
index e123737d..0d3f8d70 100644
--- a/zen/win_ver.h
+++ b/zen/win_ver.h
@@ -7,30 +7,48 @@
#ifndef WINDOWS_VERSION_HEADER_238470348254325
#define WINDOWS_VERSION_HEADER_238470348254325
-#include <cstdint>
+#include <utility>
#include "win.h" //includes "windows.h"
namespace zen
{
-std::uint64_t getOsVersion();
-std::uint64_t toBigOsNumber(DWORD high, DWORD low);
+ struct OsVersion
+ {
+ OsVersion() : major(), minor() {}
+ OsVersion(DWORD high, DWORD low) : major(high), minor(low) {}
+
+ DWORD major;
+ DWORD minor;
+ };
+ inline bool operator< (const OsVersion& lhs, const OsVersion& rhs) { return lhs.major != rhs.major ? lhs.major < rhs.major : lhs.minor < rhs.minor; }
+ inline bool operator==(const OsVersion& lhs, const OsVersion& rhs) { return lhs.major == rhs.major && lhs.minor == rhs.minor; }
+
//version overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
-const std::uint64_t osVersionWin81 = toBigOsNumber(6, 3);
-const std::uint64_t osVersionWin8 = toBigOsNumber(6, 2);
-const std::uint64_t osVersionWin7 = toBigOsNumber(6, 1);
-const std::uint64_t osVersionWinVista = toBigOsNumber(6, 0);
-const std::uint64_t osVersionWinServer2003 = toBigOsNumber(5, 2);
-const std::uint64_t osVersionWinXp = toBigOsNumber(5, 1);
+const OsVersion osVersionWin81 (6, 3);
+const OsVersion osVersionWin8 (6, 2);
+const OsVersion osVersionWin7 (6, 1);
+const OsVersion osVersionWinVista (6, 0);
+const OsVersion osVersionWinServer2003(5, 2);
+const OsVersion osVersionWinXp (5, 1);
-inline bool win81OrLater () { return getOsVersion() >= osVersionWin81; }
-inline bool win8OrLater () { return getOsVersion() >= osVersionWin8; }
-inline bool win7OrLater () { return getOsVersion() >= osVersionWin7; }
-inline bool vistaOrLater () { return getOsVersion() >= osVersionWinVista; }
-inline bool winServer2003orLater() { return getOsVersion() >= osVersionWinServer2003; }
-inline bool winXpOrLater () { return getOsVersion() >= osVersionWinXp; }
+/*
+ NOTE: there are two basic APIs to check Windows version: (empiric study following)
+ GetVersionEx -> reports version considering compatibility mode (and compatibility setting in app manifest since Windows 8.1)
+ VerifyVersionInfo -> always reports *real* Windows Version
+*/
+//GetVersionEx()-based APIs:
+OsVersion getOsVersion();
+inline bool win81OrLater () { using namespace std::rel_ops; return getOsVersion() >= osVersionWin81; }
+inline bool win8OrLater () { using namespace std::rel_ops; return getOsVersion() >= osVersionWin8; }
+inline bool win7OrLater () { using namespace std::rel_ops; return getOsVersion() >= osVersionWin7; }
+inline bool vistaOrLater () { using namespace std::rel_ops; return getOsVersion() >= osVersionWinVista; }
+inline bool winServer2003orLater() { using namespace std::rel_ops; return getOsVersion() >= osVersionWinServer2003; }
+inline bool winXpOrLater () { using namespace std::rel_ops; return getOsVersion() >= osVersionWinXp; }
+//VerifyVersionInfo()-based APIs:
+bool isRealOsVersion(const OsVersion& ver);
@@ -39,25 +57,37 @@ inline bool winXpOrLater () { return getOsVersion() >= osVersionWinXp;
//######################### implementation #########################
inline
-std::uint64_t toBigOsNumber(DWORD high, DWORD low)
+OsVersion getOsVersion()
{
- ULARGE_INTEGER tmp = {};
- tmp.HighPart = high;
- tmp.LowPart = low;
-
- static_assert(sizeof(tmp) == sizeof(std::uint64_t), "");
- return tmp.QuadPart;
+ OSVERSIONINFO osvi = {};
+ osvi.dwOSVersionInfoSize = sizeof(osvi);
+ if (!::GetVersionEx(&osvi)) //38 ns per call! (yes, that's nano!) -> we do NOT miss C++11 thread-safe statics right now...
+ {
+ assert(false);
+ return OsVersion();
+ }
+ return OsVersion(osvi.dwMajorVersion, osvi.dwMinorVersion);
}
inline
-std::uint64_t getOsVersion()
+bool isRealOsVersion(const OsVersion& ver)
{
- OSVERSIONINFO osvi = {};
- osvi.dwOSVersionInfoSize = sizeof(osvi);
- if (!::GetVersionEx(&osvi)) //38 ns per call! (yes, that's nano!) -> we do NOT miss C++11 thread safe statics right now...
- return 0;
- return toBigOsNumber(osvi.dwMajorVersion, osvi.dwMinorVersion);
+ OSVERSIONINFOEX verInfo = {};
+ verInfo.dwOSVersionInfoSize = sizeof(verInfo);
+ verInfo.dwMajorVersion = ver.major;
+ verInfo.dwMinorVersion = ver.minor;
+
+ //Syntax: http://msdn.microsoft.com/en-us/library/windows/desktop/ms725491%28v=vs.85%29.aspx
+ DWORDLONG conditionMask = 0;
+ VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_EQUAL);
+ VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_EQUAL);
+
+ const bool rv = ::VerifyVersionInfo(&verInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask)
+ == TRUE; //silence VC "performance warnings"
+ assert(rv || GetLastError() == ERROR_OLD_WIN_VERSION);
+
+ return rv;
}
}
bgstack15