diff options
author | Daniel Wilhelm <daniel@wili.li> | 2015-10-02 14:55:19 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2015-10-02 14:55:19 +0200 |
commit | 46fc289a8776ba253e97d01d6948fb1031ea1973 (patch) | |
tree | b16a99c60f21b04c001f29862bf2ee16ae3a0e00 | |
parent | 6.15 (diff) | |
download | FreeFileSync-46fc289a8776ba253e97d01d6948fb1031ea1973.tar.gz FreeFileSync-46fc289a8776ba253e97d01d6948fb1031ea1973.tar.bz2 FreeFileSync-46fc289a8776ba253e97d01d6948fb1031ea1973.zip |
7.0
143 files changed, 9364 insertions, 7708 deletions
diff --git a/FreeFileSync/Build/Changelog.txt b/FreeFileSync/Build/Changelog.txt index ab145f16..5e576ca5 100644 --- a/FreeFileSync/Build/Changelog.txt +++ b/FreeFileSync/Build/Changelog.txt @@ -1,5 +1,25 @@ -FreeFileSync 6.15 ------------------ +FreeFileSync 7.0 [2015-05-11] +----------------------------- +Support synchronization with MTP devices (Android, iPhone, tablet, digital camera) +Implemented file system abstraction layer +New database format supporting generic file ids +Pre-allocate disk space when writing file output stream +Late failure when moving multiple items to recycle bin +Keep UI responsive while loading/saving database file +Improved error reporting indicating failed item when moving to recycle bin +Transactional error handling when closing file streams +Pass correct thread id when creating Minidump (Windows) +Fixed directory icon loading resource leak (Linux) +Fixed RealtimeSync message provider exception safety issue (Windows) +Avoid locking issues by creating the log file after batch synchronization +Fixed RealtimeSync monitoring for items beyond subfolders (Linux) +Fall back to file extension during file icon load error +Show file icon by extension as temporary placeholder +Work around silent failure to copy file times to external drives (Linux) + + +FreeFileSync 6.15 [2015-04-07] +------------------------------ Revert to log file naming convention without colon character Prevent endless recursion when traversing into folder on corrupt file system Fixed view filter button rendering issue for RTL languages @@ -75,7 +95,7 @@ Updated translation files FreeFileSync 6.11 [2014-11-03] ------------------------------ -Updated Recycle Bin access for Windows 10 +Updated recycle bin access for Windows 10 New command line option "-edit" to load configuration without executing Case-insensitive command line argument evaluation New Explorer context menu options for ffs_gui, ffs_batch files @@ -599,7 +619,7 @@ Resize left and right grids equally Allow to move middle grid position via mouse Automatically resize file name columns Do not follow reparse points other than symlinks and mount points -Warn if Recycle Bin is not available during manual deletion +Warn if recycle bin is not available during manual deletion Fixed error when saving log file into volume root directory Show files which differ in attributes only in the same category as "equal" files Apply hidden attribute to lock file diff --git a/FreeFileSync/Build/Help/html/Comparison Settings.html b/FreeFileSync/Build/Help/html/Comparison Settings.html index 8be6c0a8..2108cfa9 100644 --- a/FreeFileSync/Build/Help/html/Comparison Settings.html +++ b/FreeFileSync/Build/Help/html/Comparison Settings.html @@ -18,16 +18,16 @@ <P>FreeFileSync lets you choose to include symbolic links (also called symlinks or soft links) when scanning directories rather than skipping over them. When included, you can select between two ways to handle them:</P> <OL> + <LI><B>Follow:</B> + Treat symbolic links like the object they are pointing to. Links + pointing to directories are traversed like ordinary directories and + the target of each link is copied during synchronization.<BR> + <LI><B>Direct:</B> Evaluate the symbolic link object directly. Symbolic links will be shown as separate entities. Links pointing to directories are not traversed and the link object - is copied directly during synchronization.<BR> - - <LI><B>Follow:</B> - Treat symbolic links like the object they are pointing to. Links - pointing to directories are traversed like ordinary directories and - the target of each link is copied during synchronization. + is copied directly during synchronization. </OL> <BR> diff --git a/FreeFileSync/Build/Help/html/External Applications.html b/FreeFileSync/Build/Help/html/External Applications.html index 131018c1..c4fb7f9f 100644 --- a/FreeFileSync/Build/Help/html/External Applications.html +++ b/FreeFileSync/Build/Help/html/External Applications.html @@ -17,7 +17,7 @@ Linux <FONT FACE="Courier New, monospace">xdg-open "%item_folder%"</FO <P>You can customize this behavior and integrate other external applications into FreeFileSync: Navigate to <B>Menu → Tools → Global settings: Customize context menu</B> and add or replace a command. The first entry is executed when double-clicking a row on main grid or pressing ENTER while all other entries can be accessed via the -context menu shown after a right mouse click. The following macros can be used: +context menu shown after a right mouse click. In addition to regular <A HREF="Macros.html">Macros</A>, the following specific macros are available: </P> <div class="greybox"> diff --git a/FreeFileSync/Build/Help/html/FreeFileSync.html b/FreeFileSync/Build/Help/html/FreeFileSync.html index c88f9d90..afe9dc11 100644 --- a/FreeFileSync/Build/Help/html/FreeFileSync.html +++ b/FreeFileSync/Build/Help/html/FreeFileSync.html @@ -61,20 +61,20 @@ <div class="bluebox"> <div class="bluebox_inner"> <B>Homepage:</B><BR> - <A HREF="http://www.freefilesync.org">http://www.freefilesync.org</A> - feedback, suggestions, bug-reports, official download mirrors<BR> + <A target="_blank" HREF="http://www.freefilesync.org">http://www.freefilesync.org</A> - feedback, suggestions, bug-reports, official download mirrors<BR> <BR> <b>Support the project:</b><BR> If you like FreeFileSync... - <A HREF="http://www.freefilesync.org/donate.php">Donate now</A> + <A target="_blank" HREF="http://www.freefilesync.org/donate.php">Donate with PayPal</A> <!-- for some inconceivable reason following image is not shown in chm file when not preloaded!!! --> <img style="width:0px; height:0px; visibility:hidden;" alt="" src="../img/donate.png"> <a style="width: 65px; height:51px;display:inline-block;vertical-align:middle;background: url(../img/donate.png) no-repeat;" - title="Donate via PayPal" href="http://www.freefilesync.org/donate.php"></a> + title="Donate via PayPal" target="_blank" href="http://www.freefilesync.org/donate.php"></a> </div> </div> <BR CLEAR=LEFT> diff --git a/FreeFileSync/Build/Languages/arabic.lng b/FreeFileSync/Build/Languages/arabic.lng index bc4c210a..3326cd96 100644 --- a/FreeFileSync/Build/Languages/arabic.lng +++ b/FreeFileSync/Build/Languages/arabic.lng @@ -1062,8 +1062,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>الرماز المصدري مكتوب بلغة C++ باستخدام:</target> -<source>If you like FreeFileSync</source> -<target>إذا أعجبك FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>إذا أعجبك FreeFileSync:</target> <source>Donate with PayPal</source> <target>تبرع باستخدام PayPal</target> diff --git a/FreeFileSync/Build/Languages/bulgarian.lng b/FreeFileSync/Build/Languages/bulgarian.lng index 9c5a0fee..9c583ae8 100644 --- a/FreeFileSync/Build/Languages/bulgarian.lng +++ b/FreeFileSync/Build/Languages/bulgarian.lng @@ -1046,8 +1046,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Изходния код е написан на C++ със:</target> -<source>If you like FreeFileSync</source> -<target>Ако харесвате FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Ако харесвате FreeFileSync:</target> <source>Donate with PayPal</source> <target>Дарение чрез PayPal</target> diff --git a/FreeFileSync/Build/Languages/chinese_simple.lng b/FreeFileSync/Build/Languages/chinese_simple.lng index 4262105b..64db8dc5 100644 --- a/FreeFileSync/Build/Languages/chinese_simple.lng +++ b/FreeFileSync/Build/Languages/chinese_simple.lng @@ -1039,8 +1039,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>源代码用如下C++工具写成:</target> -<source>If you like FreeFileSync</source> -<target>如果你喜欢 FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>如果你喜欢 FreeFileSync:</target> <source>Donate with PayPal</source> <target>通过PayPal捐赠</target> diff --git a/FreeFileSync/Build/Languages/chinese_traditional.lng b/FreeFileSync/Build/Languages/chinese_traditional.lng index bede17c9..52d401c3 100644 --- a/FreeFileSync/Build/Languages/chinese_traditional.lng +++ b/FreeFileSync/Build/Languages/chinese_traditional.lng @@ -1042,8 +1042,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>使用C++編寫的原始碼</target> -<source>If you like FreeFileSync</source> -<target>如果您喜歡FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>如果您喜歡FreeFileSync:</target> <source>Donate with PayPal</source> <target>使用PayPal捐款</target> diff --git a/FreeFileSync/Build/Languages/croatian.lng b/FreeFileSync/Build/Languages/croatian.lng index 2b764e1d..883956b8 100644 --- a/FreeFileSync/Build/Languages/croatian.lng +++ b/FreeFileSync/Build/Languages/croatian.lng @@ -1050,8 +1050,8 @@ Ovo garantira čvrsto stanje čak u slučaju ozbiljne greške. <source>Source code written in C++ using:</source> <target>Izvorni kod napisan u C++ uz korištenje:</target> -<source>If you like FreeFileSync</source> -<target>Ako volite FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Ako volite FreeFileSync:</target> <source>Donate with PayPal</source> <target>Donirajte s PayPal</target> diff --git a/FreeFileSync/Build/Languages/czech.lng b/FreeFileSync/Build/Languages/czech.lng index 0fd201d3..f8939b2c 100644 --- a/FreeFileSync/Build/Languages/czech.lng +++ b/FreeFileSync/Build/Languages/czech.lng @@ -1047,8 +1047,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Zdrojový kód byl napsán kompletně v C++ pomocí:</target> -<source>If you like FreeFileSync</source> -<target>Pokud se Vám FreeFileSync líbí</target> +<source>If you like FreeFileSync:</source> +<target>Pokud se Vám FreeFileSync líbí:</target> <source>Donate with PayPal</source> <target>Přispět pomocí PayPal</target> diff --git a/FreeFileSync/Build/Languages/danish.lng b/FreeFileSync/Build/Languages/danish.lng index 4a9354ea..e954c5bf 100644 --- a/FreeFileSync/Build/Languages/danish.lng +++ b/FreeFileSync/Build/Languages/danish.lng @@ -1043,8 +1043,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Kildekoden er skrevet i C++ med hjælp fra:</target> -<source>If you like FreeFileSync</source> -<target>Er du glad for FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Er du glad for FreeFileSync:</target> <source>Donate with PayPal</source> <target>Donér med PayPal</target> diff --git a/FreeFileSync/Build/Languages/dutch.lng b/FreeFileSync/Build/Languages/dutch.lng index 1f40dad3..273b40a1 100644 --- a/FreeFileSync/Build/Languages/dutch.lng +++ b/FreeFileSync/Build/Languages/dutch.lng @@ -1046,8 +1046,8 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. <source>Source code written in C++ using:</source> <target>Broncode werd in C++ geschreven met:</target> -<source>If you like FreeFileSync</source> -<target>Als FreeFileSync je bevalt</target> +<source>If you like FreeFileSync:</source> +<target>Als FreeFileSync je bevalt:</target> <source>Donate with PayPal</source> <target>Doneer met PayPal</target> diff --git a/FreeFileSync/Build/Languages/english_uk.lng b/FreeFileSync/Build/Languages/english_uk.lng index ec4e344d..7edbd19b 100644 --- a/FreeFileSync/Build/Languages/english_uk.lng +++ b/FreeFileSync/Build/Languages/english_uk.lng @@ -1046,8 +1046,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Source code written in C++ using:</target> -<source>If you like FreeFileSync</source> -<target>If you like FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>If you like FreeFileSync:</target> <source>Donate with PayPal</source> <target>Donate with PayPal</target> diff --git a/FreeFileSync/Build/Languages/finnish.lng b/FreeFileSync/Build/Languages/finnish.lng index a39ff83c..ae9c9b71 100644 --- a/FreeFileSync/Build/Languages/finnish.lng +++ b/FreeFileSync/Build/Languages/finnish.lng @@ -1046,8 +1046,8 @@ Tällä varmistetaan eheys, vaikka vakava virhe tapahtuisi. <source>Source code written in C++ using:</source> <target>Koodattu C++-kielellä käyttäen:</target> -<source>If you like FreeFileSync</source> -<target>Jos pidät FreeFileSyncistä</target> +<source>If you like FreeFileSync:</source> +<target>Jos pidät FreeFileSyncistä:</target> <source>Donate with PayPal</source> <target>Lahjoita PayPalilla</target> diff --git a/FreeFileSync/Build/Languages/french.lng b/FreeFileSync/Build/Languages/french.lng index acb5f1dc..c495428d 100644 --- a/FreeFileSync/Build/Languages/french.lng +++ b/FreeFileSync/Build/Languages/french.lng @@ -1046,8 +1046,8 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <source>Source code written in C++ using:</source> <target>Code source écrit en C++ utilisant :</target> -<source>If you like FreeFileSync</source> -<target>Si vous aimez FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Si vous aimez FreeFileSync:</target> <source>Donate with PayPal</source> <target>Faites un don avec PayPal</target> diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng index 8889b16b..fc531a59 100644 --- a/FreeFileSync/Build/Languages/german.lng +++ b/FreeFileSync/Build/Languages/german.lng @@ -7,24 +7,6 @@ <plural_definition>n == 1 ? 0 : 1</plural_definition> </header> -<source>Cannot copy attributes from %x to %y.</source> -<target>Die Attribute können nicht von %x nach %y kopiert werden.</target> - -<source>Cannot copy permissions from %x to %y.</source> -<target>Die Berechtigungen können nicht von %x nach %y kopiert werden.</target> - -<source>Move down</source> -<target>Nach unten</target> - -<source>Move up</source> -<target>Nach oben</target> - -<source>Arrange folder pair</source> -<target>Ordnerpaar anordnen</target> - -<source>Cannot find file %x.</source> -<target>Die Datei %x wurde nicht gefunden.</target> - <source>Both sides have changed since last synchronization.</source> <target>Beide Seiten wurden seit der letzten Synchronisation verändert.</target> @@ -73,6 +55,9 @@ <source>Syntax error</source> <target>Syntaxfehler</target> +<source>Cannot find file %x.</source> +<target>Die Datei %x wurde nicht gefunden.</target> + <source>File %x does not contain a valid configuration.</source> <target>Die Datei %x enthält keine gültige Konfiguration.</target> @@ -217,6 +202,77 @@ <source>Update attributes on right</source> <target>Aktualisiere Attribute des rechten Elements</target> +<source>Cannot read file %x.</source> +<target>Die Datei %x kann nicht gelesen werden.</target> + +<source> +Unexpected size of data stream. +Expected: %x bytes +Actual: %y bytes +</source> +<target> +Unerwartete Größe des Datenstroms. +Erwartet: %x bytes +Tatsächlich: %y bytes +</target> + +<source>Cannot write permissions of %x.</source> +<target>Die Berechtigungen von %x können nicht geschrieben werden.</target> + +<source>Operation not supported for different base folder types.</source> +<target>Der Vorgang wird für unterschiedliche Basisordner nicht unterstützt.</target> + +<source>Cannot write file %x.</source> +<target>Die Datei %x kann nicht geschrieben werden.</target> + +<source>Cannot copy symbolic link %x to %y.</source> +<target>Die symbolische Verknüpfung %x kann nicht nach %y kopiert werden.</target> + +<source>Cannot move file %x to %y.</source> +<target>Die Datei %x kann nicht nach %y verschoben werden.</target> + +<source>Cannot enumerate directory %x.</source> +<target>Das Verzeichnis %x kann nicht gelistet werden.</target> + +<source>Cannot read file attributes of %x.</source> +<target>Die Dateiattribute von %x können nicht gelesen werden.</target> + +<source>Cannot find %x.</source> +<target>%x wurde nicht gefunden.</target> + +<source>Cannot open file %x.</source> +<target>Die Datei %x kann nicht geöffnet werden.</target> + +<source>Cannot delete directory %x.</source> +<target>Das Verzeichnis %x kann nicht gelöscht werden.</target> + +<source>Cannot delete file %x.</source> +<target>Die Datei %x kann nicht gelöscht werden.</target> + +<source>Cannot find device %x.</source> +<target>Das Gerät %x wurde nicht gefunden.</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Der freie Speicherplatz für %x konnte nicht ermittelt werden.</target> + +<source>Cannot create directory %x.</source> +<target>Das Verzeichnis %x kann nicht erstellt werden.</target> + +<source>Cannot write modification time of %x.</source> +<target>Die Änderungszeit von %x kann nicht geschrieben werden.</target> + +<source>Cannot determine final path for %x.</source> +<target>Der endgültige Pfad für %x kann nicht ermittelt werden.</target> + +<source>Cannot resolve symbolic link %x.</source> +<target>Die symbolische Verknüpfung %x kann nicht aufgelöst werden.</target> + +<source>Unable to move %x to the recycle bin.</source> +<target>%x kann nicht in den Papierkorb verschoben werden.</target> + +<source>Cannot open directory %x.</source> +<target>Das Verzeichnis %x kann nicht geöffnet werden.</target> + <source> <pluralform>1 byte</pluralform> <pluralform>%x bytes</pluralform> @@ -250,20 +306,14 @@ <source>Database file is corrupt:</source> <target>Die Datenbankdatei ist beschädigt:</target> -<source>Cannot write file %x.</source> -<target>Die Datei %x kann nicht geschrieben werden.</target> - -<source>Cannot read file %x.</source> -<target>Die Datei %x kann nicht gelesen werden.</target> - <source>Database files do not share a common session.</source> <target>Die Datenbankdateien teilen keine gemeinsame Sitzung.</target> <source>Searching for folder %x...</source> <target>Suche Ordner %x...</target> -<source>Cannot read file attributes of %x.</source> -<target>Die Dateiattribute von %x können nicht gelesen werden.</target> +<source>Time out while searching for folder %x.</source> +<target>Das Zeitlimit für die Suche nach Ordner %x wurde überschritten.</target> <source>Cannot get process information.</source> <target>Prozessinformationen können nicht gelesen werden.</target> @@ -352,6 +402,12 @@ <source>Unable to create time stamp for versioning:</source> <target>Der Zeitstempel für die Versionierung kann nicht erstellt werden:</target> +<source>Drag && drop</source> +<target>Drag && Drop</target> + +<source>Select a folder</source> +<target>Ordner auswählen</target> + <source>&Open...</source> <target>Ö&ffnen...</target> @@ -400,9 +456,6 @@ <source>Browse</source> <target>Auswählen</target> -<source>Select a folder</source> -<target>Ordner auswählen</target> - <source>Idle time (in seconds):</source> <target>Ruhezeit (in Sekunden):</target> @@ -513,12 +566,15 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Updating attributes of %x</source> <target>Aktualisiere Attribute von %x</target> +<source>%x and %y have different content.</source> +<target>%x und %y haben unterschiedlichen Inhalt.</target> + +<source>Data verification error:</source> +<target>Verifizierungsfehler:</target> + <source>Creating a Volume Shadow Copy for %x...</source> <target>Erstelle eine Volumenschattenkopie für %x...</target> -<source>Data verification error: %x and %y have different content.</source> -<target>Verifizierungsfehler: %x und %y haben unterschiedlichen Inhalt.</target> - <source>Target folder %x already existing.</source> <target>Der Zielordner %x existiert bereits.</target> @@ -582,6 +638,9 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Stopped</source> <target>Unterbrochen</target> +<source>Warning</source> +<target>Warnung</target> + <source>You can switch to FreeFileSync's main window to resolve this issue.</source> <target>Sie können auf FreeFileSyncs Hauptfenster wechseln, um das Problem zu beheben.</target> @@ -672,9 +731,6 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Action</source> <target>Aktion</target> -<source>Drag && drop</source> -<target>Drag && Drop</target> - <source>Local comparison settings</source> <target>Lokale Vergleichseinstellungen</target> @@ -705,6 +761,9 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Local Synchronization Settings</source> <target>Lokale Synchronisationseinstellungen</target> +<source>The selected folder %x cannot be used with FreeFileSync. Please select a folder on a local file system, network or an MTP device.</source> +<target>Der ausgewählte Ordner %x kann nicht mit FreeFileSync verwendet werden. Bitte wählen Sie einen Ordner auf einem lokalen Dateisystem, Netzwerk oder MTP Gerät.</target> + <source>&New</source> <target>&Neu</target> @@ -843,12 +902,12 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Include symbolic links:</source> <target>Symbolische Verknüpfungen einschließen:</target> -<source>Direct</source> -<target>Direkt</target> - <source>Follow</source> <target>Folgen</target> +<source>Direct</source> +<target>Direkt</target> + <source>More information</source> <target>Mehr Information</target> @@ -950,6 +1009,9 @@ Die Befehlszeile wird ausgelöst, wenn: <source>&Don't show this dialog again</source> <target>&Diesen Dialog nicht mehr anzeigen</target> +<source>Arrange folder pair</source> +<target>Ordnerpaar anordnen</target> + <source>Items found:</source> <target>Gefundene Elemente:</target> @@ -1061,8 +1123,8 @@ Dadurch wird ein konsistenter Datenstand auch im schweren Fehlerfall garantiert. <source>Source code written in C++ using:</source> <target>Der Quellcode wurde in C++ geschrieben mit:</target> -<source>If you like FreeFileSync</source> -<target>Wenn Sie FreeFileSync mögen</target> +<source>If you like FreeFileSync:</source> +<target>Wenn Sie FreeFileSync mögen:</target> <source>Donate with PayPal</source> <target>Mit PayPal spenden</target> @@ -1289,6 +1351,12 @@ Dadurch wird ein konsistenter Datenstand auch im schweren Fehlerfall garantiert. <source>Cannot find %x</source> <target>%x wurde nicht gefunden.</target> +<source>Move up</source> +<target>Nach oben</target> + +<source>Move down</source> +<target>Nach unten</target> + <source>Comma-separated values</source> <target>Kommagetrennte Werte</target> @@ -1319,9 +1387,6 @@ Dadurch wird ein konsistenter Datenstand auch im schweren Fehlerfall garantiert. <source>Info</source> <target>Info</target> -<source>Warning</source> -<target>Warnung</target> - <source>Select all</source> <target>Alle auswählen</target> @@ -1463,27 +1528,21 @@ Dadurch wird ein konsistenter Datenstand auch im schweren Fehlerfall garantiert. <source>Percentage</source> <target>Prozent</target> +<source>Unable to register to receive system messages.</source> +<target>Die Registrierung zum Empfang von Systemmeldungen ist fehlgeschlagen.</target> + +<source>Unable to register device notifications for %x.</source> +<target>Die Registrierung für Gerätemeldungen ist für %x fehlgeschlagen.</target> + <source>Cannot monitor directory %x.</source> <target>Das Verzeichnis %x kann nicht überwacht werden.</target> -<source>Cannot delete file %x.</source> -<target>Die Datei %x kann nicht gelöscht werden.</target> - <source>The file is locked by another process:</source> <target>Die Datei wird von einem anderen Prozess gesperrt:</target> -<source>Cannot move file %x to %y.</source> -<target>Die Datei %x kann nicht nach %y verschoben werden.</target> - -<source>Cannot delete directory %x.</source> -<target>Das Verzeichnis %x kann nicht gelöscht werden.</target> - <source>Cannot write file attributes of %x.</source> <target>Die Dateiattribute von %x können nicht geschrieben werden.</target> -<source>Cannot write modification time of %x.</source> -<target>Die Änderungszeit von %x kann nicht geschrieben werden.</target> - <source>Cannot read security context of %x.</source> <target>Der Sicherheitskontext von %x kann nicht gelesen werden.</target> @@ -1493,36 +1552,21 @@ Dadurch wird ein konsistenter Datenstand auch im schweren Fehlerfall garantiert. <source>Cannot read permissions of %x.</source> <target>Die Berechtigungen von %x können nicht gelesen werden.</target> -<source>Cannot write permissions of %x.</source> -<target>Die Berechtigungen von %x können nicht geschrieben werden.</target> - -<source>Cannot create directory %x.</source> -<target>Das Verzeichnis %x kann nicht erstellt werden.</target> - -<source>Cannot copy symbolic link %x to %y.</source> -<target>Die symbolische Verknüpfung %x kann nicht nach %y kopiert werden.</target> +<source>Cannot copy permissions from %x to %y.</source> +<target>Die Berechtigungen können nicht von %x nach %y kopiert werden.</target> <source>Cannot find system function %x.</source> <target>Die Systemfunktion %x wurde nicht gefunden.</target> +<source>Cannot copy attributes from %x to %y.</source> +<target>Die Attribute können nicht von %x nach %y kopiert werden.</target> + <source>Cannot copy file %x to %y.</source> <target>Die Datei %x kann nicht nach %y kopiert werden.</target> <source>Type of item %x is not supported:</source> <target>Der Typ des Elements %x wird nicht unterstützt:</target> -<source>Cannot open file %x.</source> -<target>Die Datei %x kann nicht geöffnet werden.</target> - -<source>Cannot resolve symbolic link %x.</source> -<target>Die symbolische Verknüpfung %x kann nicht aufgelöst werden.</target> - -<source>Cannot open directory %x.</source> -<target>Das Verzeichnis %x kann nicht geöffnet werden.</target> - -<source>Cannot enumerate directory %x.</source> -<target>Das Verzeichnis %x kann nicht gelistet werden.</target> - <source>%x TB</source> <target>%x TB</target> @@ -1556,9 +1600,6 @@ Dadurch wird ein konsistenter Datenstand auch im schweren Fehlerfall garantiert. <pluralform>%x Tage</pluralform> </target> -<source>Unable to register to receive system messages.</source> -<target>Die Registrierung zum Empfang von Systemmeldungen ist fehlgeschlagen.</target> - <source>Cannot set privilege %x.</source> <target>Das Privileg %x kann nicht gesetzt werden.</target> @@ -1568,15 +1609,9 @@ Dadurch wird ein konsistenter Datenstand auch im schweren Fehlerfall garantiert. <source>Cannot change process I/O priorities.</source> <target>Die Eingabe/Ausgabe Prioritäten für den Prozess können nicht geändert werden.</target> -<source>Unable to move %x to the recycle bin.</source> -<target>%x kann nicht in den Papierkorb verschoben werden.</target> - <source>Checking recycle bin failed for folder %x.</source> <target>Die Prüfung des Papierkorbs für Ordner %x ist fehlgeschlagen.</target> -<source>Cannot determine final path for %x.</source> -<target>Der endgültige Pfad für %x kann nicht ermittelt werden.</target> - <source>Error Code %x:</source> <target>Fehlercode %x:</target> diff --git a/FreeFileSync/Build/Languages/greek.lng b/FreeFileSync/Build/Languages/greek.lng index 37042e12..9dfb724b 100644 --- a/FreeFileSync/Build/Languages/greek.lng +++ b/FreeFileSync/Build/Languages/greek.lng @@ -1046,8 +1046,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Ο πηγαίος κώδικας γράφτηκε σε C++ χρησιμοποιώντας τα:</target> -<source>If you like FreeFileSync</source> -<target>Αν σας αρέσει το FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Αν σας αρέσει το FreeFileSync:</target> <source>Donate with PayPal</source> <target>Κάντε μια δωρεά μέσω PayPal</target> diff --git a/FreeFileSync/Build/Languages/hebrew.lng b/FreeFileSync/Build/Languages/hebrew.lng index e50eb86b..190861f4 100644 --- a/FreeFileSync/Build/Languages/hebrew.lng +++ b/FreeFileSync/Build/Languages/hebrew.lng @@ -1046,8 +1046,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>קוד מקור נכתב ב- C++ באמצעות:</target> -<source>If you like FreeFileSync</source> -<target>במידה ו-FreeFileSync מוצאת חן בעינכם</target> +<source>If you like FreeFileSync:</source> +<target>במידה ו-FreeFileSync מוצאת חן בעינכם:</target> <source>Donate with PayPal</source> <target>תרום עם פייפל</target> diff --git a/FreeFileSync/Build/Languages/hindi.lng b/FreeFileSync/Build/Languages/hindi.lng index b08df15d..9e008c91 100644 --- a/FreeFileSync/Build/Languages/hindi.lng +++ b/FreeFileSync/Build/Languages/hindi.lng @@ -1061,9 +1061,6 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>स्रोत कोड C++ में लिखा गया इनके प्रयोग से :</target> -<source>If you like FreeFileSync</source> -<target>यदि आपको FreeFileSync पसंद है तो</target> - <source>Donate with PayPal</source> <target>PayPal (पेपॉल) द्वारा दान करें</target> diff --git a/FreeFileSync/Build/Languages/hungarian.lng b/FreeFileSync/Build/Languages/hungarian.lng index 28321cf7..1e8b0bc7 100644 --- a/FreeFileSync/Build/Languages/hungarian.lng +++ b/FreeFileSync/Build/Languages/hungarian.lng @@ -1046,8 +1046,8 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. <source>Source code written in C++ using:</source> <target>A programot C++-ban fejlesztették a következők felhasználásával:</target> -<source>If you like FreeFileSync</source> -<target>Ha szereted a FreeFileSync-et</target> +<source>If you like FreeFileSync:</source> +<target>Ha szereted a FreeFileSync-et:</target> <source>Donate with PayPal</source> <target>Támogasd a PayPal segítségével</target> diff --git a/FreeFileSync/Build/Languages/italian.lng b/FreeFileSync/Build/Languages/italian.lng index 22e109de..0da1aa99 100644 --- a/FreeFileSync/Build/Languages/italian.lng +++ b/FreeFileSync/Build/Languages/italian.lng @@ -1046,8 +1046,8 @@ Questo garantisce uno stato consistente anche in caso di errore grave. <source>Source code written in C++ using:</source> <target>Codice sorgente scritto in C++ utilizzando:</target> -<source>If you like FreeFileSync</source> -<target>Se ti piace FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Se ti piace FreeFileSync:</target> <source>Donate with PayPal</source> <target>Fai una donazione con PayPal</target> diff --git a/FreeFileSync/Build/Languages/japanese.lng b/FreeFileSync/Build/Languages/japanese.lng index 46769af7..cdd85e92 100644 --- a/FreeFileSync/Build/Languages/japanese.lng +++ b/FreeFileSync/Build/Languages/japanese.lng @@ -1042,8 +1042,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>ソースコードは C++ で書かれています</target> -<source>If you like FreeFileSync</source> -<target>FreeFileSync を気に入ってくれた方へ</target> +<source>If you like FreeFileSync:</source> +<target>FreeFileSync を気に入ってくれた方へ:</target> <source>Donate with PayPal</source> <target>PayPal から寄付</target> diff --git a/FreeFileSync/Build/Languages/korean.lng b/FreeFileSync/Build/Languages/korean.lng index 60c75ef0..5a6b8533 100644 --- a/FreeFileSync/Build/Languages/korean.lng +++ b/FreeFileSync/Build/Languages/korean.lng @@ -1042,8 +1042,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>소스코드는 C++ 언어로 아래 툴을 사용하여 작성되었습니다:</target> -<source>If you like FreeFileSync</source> -<target>FreeFileSync를 위한 기부</target> +<source>If you like FreeFileSync:</source> +<target>FreeFileSync를 위한 기부:</target> <source>Donate with PayPal</source> <target>PayPal로 기부하기</target> diff --git a/FreeFileSync/Build/Languages/lithuanian.lng b/FreeFileSync/Build/Languages/lithuanian.lng index bbc8c4d5..2c43b1ca 100644 --- a/FreeFileSync/Build/Languages/lithuanian.lng +++ b/FreeFileSync/Build/Languages/lithuanian.lng @@ -1056,8 +1056,8 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. <source>Source code written in C++ using:</source> <target>Šaltinio kodas parašytas su C++ naudojant:</target> -<source>If you like FreeFileSync</source> -<target>Jei Jums patinka FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Jei Jums patinka FreeFileSync:</target> <source>Donate with PayPal</source> <target>Paremkite per PayPal</target> diff --git a/FreeFileSync/Build/Languages/norwegian.lng b/FreeFileSync/Build/Languages/norwegian.lng index 14c9287a..1f857220 100644 --- a/FreeFileSync/Build/Languages/norwegian.lng +++ b/FreeFileSync/Build/Languages/norwegian.lng @@ -1043,8 +1043,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Kildekoden er skrevet i C++ med hjelp fra:</target> -<source>If you like FreeFileSync</source> -<target>Hvis du liker FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Hvis du liker FreeFileSync:</target> <source>Donate with PayPal</source> <target>Donér med PayPal</target> diff --git a/FreeFileSync/Build/Languages/outdated/scottish_gaelic.lng b/FreeFileSync/Build/Languages/outdated/scottish_gaelic.lng index 0de67972..97797069 100644 --- a/FreeFileSync/Build/Languages/outdated/scottish_gaelic.lng +++ b/FreeFileSync/Build/Languages/outdated/scottish_gaelic.lng @@ -1050,11 +1050,8 @@ Nì seo cinnteach gum bi fuasgladh ann ma thachras mearachd mhòr. <source>Source code written in C++ using:</source> <target>Chaidh an còd tùsail a sgrìobhadh ann an C++ le taic:</target> -<source>If you like FreeFileSync</source> -<target>Ma tha FreeFileSync a’ còrdadh riut</target> - -<source>Donate with PayPal</source> -<target>Nach doir sibh tabhartas le PayPal?</target> +<source>If you like FreeFileSync:</source> +<target>Ma tha FreeFileSync a’ còrdadh riut:</target> <source>Feedback and suggestions are welcome</source> <target>Tha sinn a’ cur fàilte mhòr air beachd is moladh sam bith</target> diff --git a/FreeFileSync/Build/Languages/outdated/slovenian.lng b/FreeFileSync/Build/Languages/outdated/slovenian.lng index 9374d49f..fb47d8b0 100644 --- a/FreeFileSync/Build/Languages/outdated/slovenian.lng +++ b/FreeFileSync/Build/Languages/outdated/slovenian.lng @@ -1050,8 +1050,8 @@ To zagotavlja konsistenco podatkov v primeru napake. <source>Source code written in C++ using:</source> <target>Izvorna koda napisana v C++ z uporabo:</target> -<source>If you like FreeFileSync</source> -<target>Če vam je FreeFileSync všeč</target> +<source>If you like FreeFileSync:</source> +<target>Če vam je FreeFileSync všeč:</target> <source>Donate with PayPal</source> <target>Doniraj s PayPal</target> diff --git a/FreeFileSync/Build/Languages/polish.lng b/FreeFileSync/Build/Languages/polish.lng index 593765f7..a4d8410b 100644 --- a/FreeFileSync/Build/Languages/polish.lng +++ b/FreeFileSync/Build/Languages/polish.lng @@ -1050,9 +1050,6 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp <source>Source code written in C++ using:</source> <target>Kod stworzony w C++ z wykorzystaniem:</target> -<source>If you like FreeFileSync</source> -<target>Podoba Ci się FreeFileSync?</target> - <source>Donate with PayPal</source> <target>Wesprzyj z PayPal</target> diff --git a/FreeFileSync/Build/Languages/portuguese.lng b/FreeFileSync/Build/Languages/portuguese.lng index 5efcccfc..68fb8832 100644 --- a/FreeFileSync/Build/Languages/portuguese.lng +++ b/FreeFileSync/Build/Languages/portuguese.lng @@ -1046,8 +1046,8 @@ Isto garante um estado consistente mesmo em caso de falha grave. <source>Source code written in C++ using:</source> <target>Código fonte escrito em C++ utilizando:</target> -<source>If you like FreeFileSync</source> -<target>Se gosta do FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Se gosta do FreeFileSync:</target> <source>Donate with PayPal</source> <target>Doar usando PayPal</target> diff --git a/FreeFileSync/Build/Languages/portuguese_br.lng b/FreeFileSync/Build/Languages/portuguese_br.lng index 4310425e..ddd5735f 100644 --- a/FreeFileSync/Build/Languages/portuguese_br.lng +++ b/FreeFileSync/Build/Languages/portuguese_br.lng @@ -1046,8 +1046,8 @@ Isto garante um estado consistente mesmo em caso de erro grave. <source>Source code written in C++ using:</source> <target>Código-fonte escrito em C++ utilizando:</target> -<source>If you like FreeFileSync</source> -<target>Se você gosta do FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Se você gosta do FreeFileSync:</target> <source>Donate with PayPal</source> <target>Doar usando PayPal</target> diff --git a/FreeFileSync/Build/Languages/romanian.lng b/FreeFileSync/Build/Languages/romanian.lng index 7c23ae51..25ae1c43 100644 --- a/FreeFileSync/Build/Languages/romanian.lng +++ b/FreeFileSync/Build/Languages/romanian.lng @@ -1050,8 +1050,8 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției <source>Source code written in C++ using:</source> <target>Cod sursă scris în C++ folosind:</target> -<source>If you like FreeFileSync</source> -<target>Donează pentru FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Donează pentru FreeFileSync:</target> <source>Donate with PayPal</source> <target>Donează prin PayPal</target> diff --git a/FreeFileSync/Build/Languages/russian.lng b/FreeFileSync/Build/Languages/russian.lng index 78f8f3a7..c994d27c 100644 --- a/FreeFileSync/Build/Languages/russian.lng +++ b/FreeFileSync/Build/Languages/russian.lng @@ -1060,8 +1060,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Исходный код написан на C++ с использованием:</target> -<source>If you like FreeFileSync</source> -<target>Если Вам понравился FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Если Вам понравился FreeFileSync:</target> <source>Donate with PayPal</source> <target>Отправить деньги через PayPal</target> diff --git a/FreeFileSync/Build/Languages/serbian.lng b/FreeFileSync/Build/Languages/serbian.lng index 140f0fde..b7542d79 100644 --- a/FreeFileSync/Build/Languages/serbian.lng +++ b/FreeFileSync/Build/Languages/serbian.lng @@ -1050,8 +1050,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Изворни код написан у C++ уз коришћење:</target> -<source>If you like FreeFileSync</source> -<target>Ако вам се свиђа FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Ако вам се свиђа FreeFileSync:</target> <source>Donate with PayPal</source> <target>Донација са PayPal-ом</target> diff --git a/FreeFileSync/Build/Languages/spanish.lng b/FreeFileSync/Build/Languages/spanish.lng index e32edc27..3d1cc018 100644 --- a/FreeFileSync/Build/Languages/spanish.lng +++ b/FreeFileSync/Build/Languages/spanish.lng @@ -1046,9 +1046,6 @@ Se garantiza un estado coherente incluso en caso de error grave. <source>Source code written in C++ using:</source> <target>Código fuente C++ con soporte de :</target> -<source>If you like FreeFileSync</source> -<target>¿Te gusta FreeFileSync? :</target> - <source>Donate with PayPal</source> <target>Haz una donación por PayPal</target> diff --git a/FreeFileSync/Build/Languages/swedish.lng b/FreeFileSync/Build/Languages/swedish.lng index dd617fed..74d6745c 100644 --- a/FreeFileSync/Build/Languages/swedish.lng +++ b/FreeFileSync/Build/Languages/swedish.lng @@ -1046,8 +1046,8 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. <source>Source code written in C++ using:</source> <target>Källkod skriven i C++ med hjälp av:</target> -<source>If you like FreeFileSync</source> -<target>Om du gillar FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Om du gillar FreeFileSync:</target> <source>Donate with PayPal</source> <target>Donera via PayPal</target> diff --git a/FreeFileSync/Build/Languages/turkish.lng b/FreeFileSync/Build/Languages/turkish.lng index f3d9846b..de4300ac 100644 --- a/FreeFileSync/Build/Languages/turkish.lng +++ b/FreeFileSync/Build/Languages/turkish.lng @@ -1046,8 +1046,8 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya <source>Source code written in C++ using:</source> <target>Kaynak kodu C++ kullanılarak yazılmıştır:</target> -<source>If you like FreeFileSync</source> -<target>FreeFileSync’i beğendiyseniz</target> +<source>If you like FreeFileSync:</source> +<target>FreeFileSync’i beğendiyseniz:</target> <source>Donate with PayPal</source> <target>PayPal üzerinden bağış yapın</target> diff --git a/FreeFileSync/Build/Languages/ukrainian.lng b/FreeFileSync/Build/Languages/ukrainian.lng index bd180bc0..5a89d999 100644 --- a/FreeFileSync/Build/Languages/ukrainian.lng +++ b/FreeFileSync/Build/Languages/ukrainian.lng @@ -1056,8 +1056,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Код програми написаний на C++ з використанням:</target> -<source>If you like FreeFileSync</source> -<target>Якщо Вам сподобався FreeFileSync</target> +<source>If you like FreeFileSync:</source> +<target>Якщо Вам сподобався FreeFileSync:</target> <source>Donate with PayPal</source> <target>Пожертвувати через PayPal</target> diff --git a/FreeFileSync/Build/Resources.zip b/FreeFileSync/Build/Resources.zip Binary files differindex 5ba74a51..a0edb0c6 100644 --- a/FreeFileSync/Build/Resources.zip +++ b/FreeFileSync/Build/Resources.zip diff --git a/FreeFileSync/Source/Makefile b/FreeFileSync/Source/Makefile index 90f56b9e..a50270eb 100644 --- a/FreeFileSync/Source/Makefile +++ b/FreeFileSync/Source/Makefile @@ -34,11 +34,14 @@ CPP_LIST+=application.cpp CPP_LIST+=comparison.cpp CPP_LIST+=structures.cpp CPP_LIST+=synchronization.cpp +CPP_LIST+=fs/abstract.cpp +CPP_LIST+=fs/concrete.cpp +CPP_LIST+=fs/native.cpp CPP_LIST+=file_hierarchy.cpp CPP_LIST+=ui/custom_grid.cpp CPP_LIST+=ui/folder_history_box.cpp CPP_LIST+=ui/on_completion_box.cpp -CPP_LIST+=ui/dir_name.cpp +CPP_LIST+=ui/folder_selector.cpp CPP_LIST+=ui/batch_config.cpp CPP_LIST+=ui/batch_status_handler.cpp CPP_LIST+=ui/check_version.cpp @@ -54,12 +57,12 @@ CPP_LIST+=ui/sync_cfg.cpp CPP_LIST+=ui/taskbar.cpp CPP_LIST+=ui/triple_splitter.cpp CPP_LIST+=ui/tray_icon.cpp -CPP_LIST+=lib/deep_file_traverser.cpp CPP_LIST+=lib/binary.cpp CPP_LIST+=lib/db_file.cpp CPP_LIST+=lib/dir_lock.cpp CPP_LIST+=lib/hard_filter.cpp CPP_LIST+=lib/icon_buffer.cpp +CPP_LIST+=lib/icon_loader.cpp CPP_LIST+=lib/localization.cpp CPP_LIST+=lib/parallel_scan.cpp CPP_LIST+=lib/process_xml.cpp diff --git a/FreeFileSync/Source/RealtimeSync/Makefile b/FreeFileSync/Source/RealtimeSync/Makefile index 560edc9f..a69c668b 100644 --- a/FreeFileSync/Source/RealtimeSync/Makefile +++ b/FreeFileSync/Source/RealtimeSync/Makefile @@ -18,9 +18,8 @@ CPP_LIST+=main_dlg.cpp CPP_LIST+=tray_menu.cpp CPP_LIST+=monitor.cpp CPP_LIST+=xml_proc.cpp +CPP_LIST+=folder_selector2.cpp CPP_LIST+=../structures.cpp -CPP_LIST+=../ui/dir_name.cpp -CPP_LIST+=../ui/folder_history_box.cpp CPP_LIST+=../lib/localization.cpp CPP_LIST+=../lib/process_xml.cpp CPP_LIST+=../lib/resolve_path.cpp diff --git a/FreeFileSync/Source/RealtimeSync/folder_selector2.cpp b/FreeFileSync/Source/RealtimeSync/folder_selector2.cpp new file mode 100644 index 00000000..4ff6a044 --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/folder_selector2.cpp @@ -0,0 +1,201 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "folder_selector2.h" +#include <zen/thread.h> +#include <zen/file_access.h> +#include <zen/optional.h> +#include <wx/dirdlg.h> +#include <wx/scrolwin.h> +#include <wx+/string_conv.h> +#include <wx+/popup_dlg.h> +#include "../lib/resolve_path.h" + +#ifdef ZEN_WIN + #include <zen/dll.h> + #include <zen/win_ver.h> + #include "../dll/IFileDialog_Vista\ifile_dialog.h" +#endif + +using namespace zen; + + +namespace +{ +void setFolderPath(const Zstring& dirpath, wxTextCtrl* txtCtrl, wxWindow& tooltipWnd, wxStaticText* staticText) //pointers are optional +{ + if (txtCtrl) + txtCtrl->ChangeValue(toWx(dirpath)); + + const Zstring displayPath = getResolvedDirectoryPath(dirpath); //may block when resolving [<volume name>] + + tooltipWnd.SetToolTip(nullptr); //workaround wxComboBox bug http://trac.wxwidgets.org/ticket/10512 / http://trac.wxwidgets.org/ticket/12659 + tooltipWnd.SetToolTip(toWx(displayPath)); //who knows when the real bugfix reaches mere mortals via an official release... + + if (staticText) //change static box label only if there is a real difference to what is shown in wxTextCtrl anyway + staticText->SetLabel(EqualFilePath()(appendSeparator(trimCpy(dirpath)), appendSeparator(displayPath)) ? wxString(_("Drag && drop")) : toWx(displayPath)); +} +} + +//############################################################################################################## + +FolderSelector2::FolderSelector2(wxWindow& dropWindow, + wxButton& selectButton, + wxTextCtrl& dirpath, + wxStaticText* staticText) : + dropWindow_(dropWindow), + selectButton_(selectButton), + dirpath_(dirpath), + staticText_(staticText) +{ + //prepare drag & drop + setupFileDrop(dropWindow_); + dropWindow_.Connect(EVENT_DROP_FILE, FileDropEventHandler(FolderSelector2::onFilesDropped), nullptr, this); + + //keep dirPicker and dirpath synchronous + dirpath_ .Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler (FolderSelector2::onMouseWheel ), nullptr, this); + dirpath_ .Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(FolderSelector2::onWriteDirManually), nullptr, this); + selectButton_.Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderSelector2::onSelectDir ), nullptr, this); +} + + +FolderSelector2::~FolderSelector2() +{ + dropWindow_.Disconnect(EVENT_DROP_FILE, FileDropEventHandler(FolderSelector2::onFilesDropped), nullptr, this); + + dirpath_ .Disconnect(wxEVT_MOUSEWHEEL, wxMouseEventHandler (FolderSelector2::onMouseWheel ), nullptr, this); + dirpath_ .Disconnect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(FolderSelector2::onWriteDirManually), nullptr, this); + selectButton_.Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderSelector2::onSelectDir ), nullptr, this); +} + + +void FolderSelector2::onMouseWheel(wxMouseEvent& event) +{ + //for combobox: although switching through available items is wxWidgets default, this is NOT windows default, e.g. explorer + //additionally this will delete manual entries, although all the users wanted is scroll the parent window! + + //redirect to parent scrolled window! + wxWindow* wnd = &dirpath_; + while ((wnd = wnd->GetParent()) != nullptr) //silence MSVC warning + if (dynamic_cast<wxScrolledWindow*>(wnd) != nullptr) + if (wxEvtHandler* evtHandler = wnd->GetEventHandler()) + { + evtHandler->AddPendingEvent(event); + break; + } + // event.Skip(); +} + + +void FolderSelector2::onFilesDropped(FileDropEvent& event) +{ + const auto& files = event.getFiles(); + if (files.empty()) + return; + + const Zstring fileName = toZ(event.getFiles()[0]); + if (dirExists(fileName)) + setFolderPath(fileName, &dirpath_, dirpath_, staticText_); + else + { + Zstring parentName = beforeLast(fileName, FILE_NAME_SEPARATOR); //returns empty string if ch not found +#ifdef ZEN_WIN + if (endsWith(parentName, L":")) //volume root + parentName += FILE_NAME_SEPARATOR; +#endif + if (dirExists(parentName)) + setFolderPath(parentName, &dirpath_, dirpath_, staticText_); + else //set original name unconditionally: usecase: inactive mapped network shares + setFolderPath(fileName, &dirpath_, dirpath_, staticText_); + } + //event.Skip(); +} + + +void FolderSelector2::onWriteDirManually(wxCommandEvent& event) +{ + setFolderPath(toZ(event.GetString()), nullptr, dirpath_, staticText_); + event.Skip(); +} + + +void FolderSelector2::onSelectDir(wxCommandEvent& event) +{ + //IFileDialog requirements for default path: 1. accepts native paths only!!! 2. path must exist! + Zstring defaultFolderPath; + { + const Zstring folderPath = getResolvedDirectoryPath(getPath()); + + auto ft = async([folderPath] { return dirExists(folderPath); }); + + if (ft.timed_wait(boost::posix_time::milliseconds(200)) && ft.get()) //potentially slow network access: wait 200ms at most + defaultFolderPath = folderPath; + } + + //wxDirDialog internally uses lame-looking SHBrowseForFolder(); we better use IFileDialog() instead! (remembers size and position!) + Opt<Zstring> newFolder; +#ifdef ZEN_WIN + if (vistaOrLater()) + { +#define DEF_DLL_FUN(name) const DllFun<ifile::FunType_##name> name(ifile::getDllName(), ifile::funName_##name); + DEF_DLL_FUN(showFolderPicker); + DEF_DLL_FUN(freeString); +#undef DEF_DLL_FUN + + if (showFolderPicker && freeString) + { + wchar_t* selectedFolder = nullptr; + wchar_t* errorMsg = nullptr; + bool cancelled = false; + ZEN_ON_SCOPE_EXIT(freeString(selectedFolder)); + ZEN_ON_SCOPE_EXIT(freeString(errorMsg)); + + const ifile::GuidProxy guid = { '\x5d', '\x1f', '\x9c', '\xe8', '\x17', '\xb2', '\x46', '\x55', + '\xa3', '\xc0', '\xdc', '\xcb', '\x37', '\xbb', '\x4e', '\x35' + }; //some random GUID => have Windows save IFileDialog state separately from other file/dir pickers! + + showFolderPicker(static_cast<HWND>(selectButton_.GetHWND()), //in; ==HWND + defaultFolderPath.empty() ? nullptr : defaultFolderPath.c_str(), //in, optional! + &guid, + nullptr, + nullptr /*callbackSink*/, + selectedFolder, //out: call freeString() after use! + cancelled, //out + errorMsg); //out, optional: call freeString() after use! + if (errorMsg) + { + showNotificationDialog(&dropWindow_, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(errorMsg)); + return; + } + if (cancelled || !selectedFolder) + return; + + newFolder = Zstring(selectedFolder); + } + } +#endif + if (!newFolder) + { + wxDirDialog dirPicker(&selectButton_, _("Select a folder"), toWx(defaultFolderPath)); //put modal wxWidgets dialogs on stack: creating on freestore leads to memleak! + if (dirPicker.ShowModal() != wxID_OK) + return; + newFolder = toZ(dirPicker.GetPath()); + } + + setFolderPath(*newFolder, &dirpath_, dirpath_, staticText_); +} + + +Zstring FolderSelector2::getPath() const +{ + return toZ(dirpath_.GetValue()); +} + + +void FolderSelector2::setPath(const Zstring& dirpath) +{ + setFolderPath(dirpath, &dirpath_, dirpath_, staticText_); +} diff --git a/FreeFileSync/Source/RealtimeSync/folder_selector2.h b/FreeFileSync/Source/RealtimeSync/folder_selector2.h new file mode 100644 index 00000000..14093117 --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/folder_selector2.h @@ -0,0 +1,46 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef DIR_NAME2_H_073246031245342566 +#define DIR_NAME2_H_073246031245342566 + +#include <zen/zstring.h> +#include <wx/stattext.h> +#include <wx/button.h> +#include <wx/textctrl.h> +#include <wx+/file_drop.h> + +namespace zen +{ +//handle drag and drop, tooltip, label and manual input, coordinating a wxWindow, wxButton, and wxTextCtrl + +class FolderSelector2 : public wxEvtHandler +{ +public: + FolderSelector2(wxWindow& dropWindow, + wxButton& selectButton, + wxTextCtrl& dirpath, + wxStaticText* staticText = nullptr); //optional + + ~FolderSelector2(); + + Zstring getPath() const; + void setPath(const Zstring& dirpath); + +private: + void onMouseWheel (wxMouseEvent& event); + void onFilesDropped (FileDropEvent& event); + void onWriteDirManually(wxCommandEvent& event); + void onSelectDir (wxCommandEvent& event); + + wxWindow& dropWindow_; + wxButton& selectButton_; + wxTextCtrl& dirpath_; + wxStaticText* staticText_; //optional +}; +} + +#endif //DIR_NAME2_H_073246031245342566 diff --git a/FreeFileSync/Source/RealtimeSync/main_dlg.cpp b/FreeFileSync/Source/RealtimeSync/main_dlg.cpp index a68cb742..3e200819 100644 --- a/FreeFileSync/Source/RealtimeSync/main_dlg.cpp +++ b/FreeFileSync/Source/RealtimeSync/main_dlg.cpp @@ -48,11 +48,11 @@ public: #endif } - void setPath(const wxString& dirpath) { dirpath_.setPath(dirpath); } - wxString getPath() const { return dirpath_.getPath(); } + void setPath(const Zstring& dirpath) { dirpath_.setPath(dirpath); } + Zstring getPath() const { return dirpath_.getPath(); } private: - zen::DirectoryName<wxTextCtrl> dirpath_; + zen::FolderSelector2 dirpath_; }; @@ -92,7 +92,7 @@ MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName) Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnKeyPressed), nullptr, this); //prepare drag & drop - dirpathFirst = zen::make_unique<DirectoryName<wxTextCtrl>>(*m_panelMainFolder, *m_buttonSelectDirMain, *m_txtCtrlDirectoryMain, m_staticTextFinalPath); + dirpathFirst = zen::make_unique<FolderSelector2>(*m_panelMainFolder, *m_buttonSelectDirMain, *m_txtCtrlDirectoryMain, m_staticTextFinalPath); //--------------------------- load config values ------------------------------------ xmlAccess::XmlRealConfig newConfig; @@ -256,9 +256,8 @@ void MainDialog::OnConfigSave(wxCommandEvent& event) { Zstring defaultFileName = currentConfigFileName.empty() ? Zstr("Realtime.ffs_real") : currentConfigFileName; //attention: currentConfigFileName may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config! - if (endsWith(defaultFileName, Zstr(".ffs_batch"))) - replace(defaultFileName, Zstr(".ffs_batch"), Zstr(".ffs_real"), false); - + if (pathEndsWith(defaultFileName, Zstr(".ffs_batch"))) + defaultFileName = beforeLast(defaultFileName, Zstr(".")) + Zstr(".ffs_real"); wxFileDialog filePicker(this, wxEmptyString, @@ -349,13 +348,13 @@ void MainDialog::onFilesDropped(FileDropEvent& event) void MainDialog::setConfiguration(const xmlAccess::XmlRealConfig& cfg) { //clear existing folders - dirpathFirst->setPath(wxString()); + dirpathFirst->setPath(Zstring()); clearAddFolders(); if (!cfg.directories.empty()) { //fill top folder - dirpathFirst->setPath(utfCvrtTo<wxString>(*cfg.directories.begin())); + dirpathFirst->setPath(*cfg.directories.begin()); //fill additional folders addFolder(std::vector<Zstring>(cfg.directories.begin() + 1, cfg.directories.end())); @@ -389,7 +388,7 @@ void MainDialog::OnAddFolder(wxCommandEvent& event) const Zstring topFolder = utfCvrtTo<Zstring>(dirpathFirst->getPath()); //clear existing top folder first - dirpathFirst->setPath(wxString()); + dirpathFirst->setPath(Zstring()); std::vector<Zstring> newFolders; newFolders.push_back(topFolder); @@ -462,7 +461,7 @@ void MainDialog::addFolder(const std::vector<Zstring>& newFolders, bool addFront newFolder->m_bpButtonRemoveFolder->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MainDialog::OnRemoveFolder), nullptr, this ); //insert directory name - newFolder->setPath(utfCvrtTo<wxString>(dirpath)); + newFolder->setPath(dirpath); } //set size of scrolled window diff --git a/FreeFileSync/Source/RealtimeSync/main_dlg.h b/FreeFileSync/Source/RealtimeSync/main_dlg.h index a6071c1b..159a135f 100644 --- a/FreeFileSync/Source/RealtimeSync/main_dlg.h +++ b/FreeFileSync/Source/RealtimeSync/main_dlg.h @@ -14,7 +14,7 @@ #include <zen/async_task.h> #include <wx+/file_drop.h> #include <wx/timer.h> -#include "../ui/dir_name.h" +#include "folder_selector2.h" namespace xmlAccess { @@ -62,7 +62,7 @@ private: static const Zstring& lastConfigFileName(); - std::unique_ptr<zen::DirectoryName<wxTextCtrl>> dirpathFirst; + std::unique_ptr<zen::FolderSelector2> dirpathFirst; std::vector<DirectoryPanel*> dirpathsExtra; //additional pairs to the standard pair Zstring currentConfigFileName; diff --git a/FreeFileSync/Source/RealtimeSync/monitor.cpp b/FreeFileSync/Source/RealtimeSync/monitor.cpp index 1cf09740..4332abff 100644 --- a/FreeFileSync/Source/RealtimeSync/monitor.cpp +++ b/FreeFileSync/Source/RealtimeSync/monitor.cpp @@ -15,6 +15,7 @@ #include "../lib/resolve_path.h" //#include "../library/db_file.h" //SYNC_DB_FILE_ENDING -> complete file too much of a dependency; file ending too little to decouple into single header //#include "../library/lock_holder.h" //LOCK_FILE_ENDING +//TEMP_FILE_ENDING using namespace zen; @@ -26,10 +27,10 @@ const int CHECK_DIR_INTERVAL = 1; //unit: [s] std::vector<Zstring> getFormattedDirs(const std::vector<Zstring>& dirpathPhrases) //throw FileError { - std::set<Zstring, LessFilename> dirpaths; //make unique - for (const Zstring& phrase : std::set<Zstring, LessFilename>(dirpathPhrases.begin(), dirpathPhrases.end())) + std::set<Zstring, LessFilePath> dirpaths; //make unique + for (const Zstring& phrase : std::set<Zstring, LessFilePath>(dirpathPhrases.begin(), dirpathPhrases.end())) //make unique: no need to resolve duplicate phrases more than once! (consider "[volume name]" syntax) -> shouldn't this be already buffered by OS? - dirpaths.insert(getFormattedDirectoryPath(phrase)); + dirpaths.insert(getResolvedDirectoryPath(phrase)); return std::vector<Zstring>(dirpaths.begin(), dirpaths.end()); } @@ -119,11 +120,11 @@ WaitResult waitForChanges(const std::vector<Zstring>& dirpathPhrases, //throw Fi { return #ifdef ZEN_MAC - endsWith(e.filepath_, Zstr("/.DS_Store")) || + pathEndsWith(e.filepath_, Zstr("/.DS_Store")) || #endif - endsWith(e.filepath_, TEMP_FILE_ENDING) || - endsWith(e.filepath_, Zstr(".ffs_lock")) || //sync.ffs_lock, sync.Del.ffs_lock - endsWith(e.filepath_, Zstr(".ffs_db")); //sync.ffs_db, .sync.tmp.ffs_db + pathEndsWith(e.filepath_, Zstr(".ffs_tmp")) || + pathEndsWith(e.filepath_, Zstr(".ffs_lock")) || //sync.ffs_lock, sync.Del.ffs_lock + pathEndsWith(e.filepath_, Zstr(".ffs_db")); //sync.ffs_db, .sync.tmp.ffs_db //no need to ignore temporal recycle bin directory: this must be caused by a file deletion anyway }); @@ -151,7 +152,7 @@ void waitForMissingDirs(const std::vector<Zstring>& dirpathPhrases, //throw File while (true) { bool allExisting = true; - //support specifying volume by name => call getFormattedDirectoryPath() repeatedly + //support specifying volume by name => call getResolvedDirectoryPath() repeatedly for (const Zstring& dirpathFmt : getFormattedDirs(dirpathPhrases)) //throw FileError { auto ftDirExisting = async([=]() -> bool diff --git a/FreeFileSync/Source/RealtimeSync/tray_menu.cpp b/FreeFileSync/Source/RealtimeSync/tray_menu.cpp index f70acc70..0604ac21 100644 --- a/FreeFileSync/Source/RealtimeSync/tray_menu.cpp +++ b/FreeFileSync/Source/RealtimeSync/tray_menu.cpp @@ -246,7 +246,7 @@ private: rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& config, const wxString& jobname) { std::vector<Zstring> dirNamesNonFmt = config.directories; - vector_remove_if(dirNamesNonFmt, [](Zstring str) -> bool { trim(str); return str.empty(); }); //remove empty entries WITHOUT formatting paths yet! + vector_remove_if(dirNamesNonFmt, [](const Zstring& str) -> bool { return trimCpy(str).empty(); }); //remove empty entries WITHOUT formatting paths yet! if (dirNamesNonFmt.empty()) { @@ -254,8 +254,7 @@ rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& conf return SHOW_GUI; } - Zstring cmdLine = config.commandline; - trim(cmdLine); + const Zstring cmdLine = trimCpy(config.commandline); if (cmdLine.empty()) { diff --git a/FreeFileSync/Source/RealtimeSync/xml_proc.cpp b/FreeFileSync/Source/RealtimeSync/xml_proc.cpp index b60cf0a6..73b543c1 100644 --- a/FreeFileSync/Source/RealtimeSync/xml_proc.cpp +++ b/FreeFileSync/Source/RealtimeSync/xml_proc.cpp @@ -86,7 +86,7 @@ namespace { xmlAccess::XmlRealConfig convertBatchToReal(const xmlAccess::XmlBatchConfig& batchCfg, const Zstring& filepath) { - std::set<Zstring, LessFilename> uniqueFolders; + std::set<Zstring, LessFilePath> uniqueFolders; //add main folders uniqueFolders.insert(batchCfg.mainCfg.firstPair.dirpathPhraseLeft); diff --git a/FreeFileSync/Source/algorithm.cpp b/FreeFileSync/Source/algorithm.cpp index b95f3101..adc33fc2 100644 --- a/FreeFileSync/Source/algorithm.cpp +++ b/FreeFileSync/Source/algorithm.cpp @@ -7,25 +7,20 @@ #include "algorithm.h" #include <set> #include <unordered_map> -#include <stdexcept> -#include <zen/file_access.h> -#include <zen/recycler.h> -#include <zen/stl_tools.h> -#include <zen/scope_guard.h> -#include <wx+/image_resources.h> #include "lib/norm_filter.h" #include "lib/db_file.h" #include "lib/cmp_filetime.h" -#include "lib/norm_filter.h" using namespace zen; -using namespace std::rel_ops; +//using namespace std::rel_ops; void zen::swapGrids(const MainConfiguration& config, FolderComparison& folderCmp) { std::for_each(begin(folderCmp), end(folderCmp), [](BaseDirPair& baseObj) { baseObj.flip(); }); - redetermineSyncDirection(config, folderCmp, [](const std::wstring&) {}); + redetermineSyncDirection(config, folderCmp, + nullptr, //onReportWarning + nullptr); //onUpdateStatus -> status update while loading db file } //---------------------------------------------------------------------------------------------- @@ -55,9 +50,9 @@ private: const CompareFilesResult cat = fileObj.getCategory(); //##################### schedule old temporary files for deletion #################### - if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getItemName<LEFT_SIDE>(), TEMP_FILE_ENDING)) + if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getItemName<LEFT_SIDE>(), ABF::TEMP_FILE_ENDING)) return fileObj.setSyncDir(SyncDirection::LEFT); - else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getItemName<RIGHT_SIDE>(), TEMP_FILE_ENDING)) + else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getItemName<RIGHT_SIDE>(), ABF::TEMP_FILE_ENDING)) return fileObj.setSyncDir(SyncDirection::RIGHT); //#################################################################################### @@ -128,9 +123,9 @@ private: const CompareDirResult cat = dirObj.getDirCategory(); //########### schedule abandoned temporary recycle bin directory for deletion ########## - if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getItemName<LEFT_SIDE>(), TEMP_FILE_ENDING)) + if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getItemName<LEFT_SIDE>(), ABF::TEMP_FILE_ENDING)) return setSyncDirectionRec(SyncDirection::LEFT, dirObj); // - else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getItemName<RIGHT_SIDE>(), TEMP_FILE_ENDING)) + else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getItemName<RIGHT_SIDE>(), ABF::TEMP_FILE_ENDING)) return setSyncDirectionRec(SyncDirection::RIGHT, dirObj); //don't recurse below! //####################################################################################### @@ -336,21 +331,21 @@ private: if (cat == FILE_LEFT_SIDE_ONLY) { - if (fileObj.getFileId<LEFT_SIDE>() != FileId()) + if (!fileObj.getFileId<LEFT_SIDE>().empty()) { auto rv = exLeftOnly.emplace(fileObj.getFileId<LEFT_SIDE>(), &fileObj); assert(rv.second); - if (!rv.second) //duplicate file ID! + if (!rv.second) //duplicate file ID! NTFS hard links? rv.first->second = nullptr; } } else if (cat == FILE_RIGHT_SIDE_ONLY) { - if (fileObj.getFileId<RIGHT_SIDE>() != FileId()) + if (!fileObj.getFileId<RIGHT_SIDE>().empty()) { auto rv = exRightOnly.emplace(fileObj.getFileId<RIGHT_SIDE>(), &fileObj); assert(rv.second); - if (!rv.second) //duplicate file ID! + if (!rv.second) //duplicate file ID! NTFS hard links? rv.first->second = nullptr; } } @@ -387,11 +382,11 @@ private: void findAndSetMovePair(const InSyncFile& dbEntry) const { - const FileId idLeft = dbEntry.left .fileId; - const FileId idRight = dbEntry.right.fileId; + const ABF::FileId idLeft = dbEntry.left .fileId; + const ABF::FileId idRight = dbEntry.right.fileId; - if (idLeft != FileId() && - idRight != FileId() && + if (!idLeft .empty() && + !idRight.empty() && stillInSync(dbEntry, cmpVar, fileTimeTolerance, optTimeShiftHours)) { auto itL = exLeftOnly.find(idLeft); @@ -417,8 +412,8 @@ private: const int fileTimeTolerance; const unsigned int optTimeShiftHours; - std::map<FileId, FilePair*> exLeftOnly; //FilePair* == nullptr for duplicate ids! => consider aliasing through symlinks! - std::map<FileId, FilePair*> exRightOnly; //=> avoid ambiguity for mixtures of files/symlinks on one side and allow 1-1 mapping only! + std::map<ABF::FileId, FilePair*> exLeftOnly; //FilePair* == nullptr for duplicate ids! => consider aliasing through symlinks! + std::map<ABF::FileId, FilePair*> exRightOnly; //=> avoid ambiguity for mixtures of files/symlinks on one side and allow 1-1 mapping only! /* detect renamed files: @@ -488,9 +483,9 @@ private: return; //##################### schedule old temporary files for deletion #################### - if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getItemName<LEFT_SIDE>(), TEMP_FILE_ENDING)) + if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getItemName<LEFT_SIDE>(), ABF::TEMP_FILE_ENDING)) return fileObj.setSyncDir(SyncDirection::LEFT); - else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getItemName<RIGHT_SIDE>(), TEMP_FILE_ENDING)) + else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getItemName<RIGHT_SIDE>(), ABF::TEMP_FILE_ENDING)) return fileObj.setSyncDir(SyncDirection::RIGHT); //#################################################################################### @@ -565,9 +560,9 @@ private: const CompareDirResult cat = dirObj.getDirCategory(); //########### schedule abandoned temporary recycle bin directory for deletion ########## - if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getItemName<LEFT_SIDE>(), TEMP_FILE_ENDING)) + if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getItemName<LEFT_SIDE>(), ABF::TEMP_FILE_ENDING)) return setSyncDirectionRec(SyncDirection::LEFT, dirObj); // - else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getItemName<RIGHT_SIDE>(), TEMP_FILE_ENDING)) + else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getItemName<RIGHT_SIDE>(), ABF::TEMP_FILE_ENDING)) return setSyncDirectionRec(SyncDirection::RIGHT, dirObj); //don't recurse below! //####################################################################################### @@ -635,7 +630,10 @@ std::vector<DirectionConfig> zen::extractDirectionCfg(const MainConfiguration& m } -void zen::redetermineSyncDirection(const DirectionConfig& dirCfg, BaseDirPair& baseDirectory, std::function<void(const std::wstring& msg)> reportWarning) +void zen::redetermineSyncDirection(const DirectionConfig& dirCfg, + BaseDirPair& baseDirectory, + const std::function<void(const std::wstring& msg)>& reportWarning, + const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) { //try to load sync-database files std::shared_ptr<InSyncDir> lastSyncState; @@ -645,14 +643,15 @@ void zen::redetermineSyncDirection(const DirectionConfig& dirCfg, BaseDirPair& b if (allItemsCategoryEqual(baseDirectory)) return; //nothing to do: abort and don't even try to open db files - lastSyncState = loadLastSynchronousState(baseDirectory); //throw FileError, FileErrorDatabaseNotExisting + lastSyncState = loadLastSynchronousState(baseDirectory, onUpdateStatus); //throw FileError, FileErrorDatabaseNotExisting } catch (FileErrorDatabaseNotExisting&) {} //let's ignore this error, there's no value in reporting it other than confuse users - catch (FileError& error) //e.g. incompatible database version + catch (const FileError& e) //e.g. incompatible database version { - reportWarning(error.toString() + - (dirCfg.var == DirectionConfig::TWOWAY ? - L" \n\n" + _("Setting default synchronization directions: Old files will be overwritten with newer files.") : std::wstring())); + if (reportWarning) + reportWarning(e.toString() + + (dirCfg.var == DirectionConfig::TWOWAY ? + L" \n\n" + _("Setting default synchronization directions: Old files will be overwritten with newer files.") : std::wstring())); } //set sync directions @@ -672,7 +671,10 @@ void zen::redetermineSyncDirection(const DirectionConfig& dirCfg, BaseDirPair& b } -void zen::redetermineSyncDirection(const MainConfiguration& mainCfg, FolderComparison& folderCmp, std::function<void(const std::wstring& msg)> reportWarning) +void zen::redetermineSyncDirection(const MainConfiguration& mainCfg, + FolderComparison& folderCmp, + const std::function<void(const std::wstring& msg)>& reportWarning, + const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) { if (folderCmp.empty()) return; @@ -685,7 +687,7 @@ void zen::redetermineSyncDirection(const MainConfiguration& mainCfg, FolderCompa for (auto it = folderCmp.begin(); it != folderCmp.end(); ++it) { const DirectionConfig& cfg = directCfgs[it - folderCmp.begin()]; - redetermineSyncDirection(cfg, **it, reportWarning); + redetermineSyncDirection(cfg, **it, reportWarning, onUpdateStatus); } } @@ -1089,14 +1091,14 @@ std::pair<Zstring, int> zen::deleteFromGridAndHDPreview(const std::vector<FileSy for (const FileSystemObject* fsObj : selectionLeft) if (!fsObj->isEmpty<LEFT_SIDE>()) { - fileList += fsObj->getFullPath<LEFT_SIDE>() + Zstr('\n'); + fileList += ABF::getDisplayPath(fsObj->getAbstractPath<LEFT_SIDE>()) + Zstr('\n'); ++totalDelCount; } for (const FileSystemObject* fsObj : selectionRight) if (!fsObj->isEmpty<RIGHT_SIDE>()) { - fileList += fsObj->getFullPath<RIGHT_SIDE>() + Zstr('\n'); + fileList += ABF::getDisplayPath(fsObj->getAbstractPath<RIGHT_SIDE>()) + Zstr('\n'); ++totalDelCount; } @@ -1136,35 +1138,30 @@ void categorize(const std::set<FileSystemObject*>& rowsIn, std::vector<FileSystemObject*>& deletePermanent, std::vector<FileSystemObject*>& deleteRecyler, bool useRecycleBin, - std::map<Zstring, bool, LessFilename>& hasRecyclerBuffer, + std::map<const ABF*, bool, ABF::LessItemPath>& recyclerSupported, DeleteFilesHandler& callback) { - auto hasRecycler = [&](const Zstring& baseDirPf) -> bool + auto hasRecycler = [&](const ABF& baseFolder) -> bool { -#ifdef ZEN_WIN - auto it = hasRecyclerBuffer.find(baseDirPf); - if (it != hasRecyclerBuffer.end()) + auto it = recyclerSupported.find(&baseFolder); //perf: avoid duplicate checks! + if (it != recyclerSupported.end()) return it->second; - const std::wstring msg = replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", fmtFileName(baseDirPf), false); - - bool recExists = false; + const std::wstring msg = replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", + fmtFileName(ABF::getDisplayPath(baseFolder.getAbstractPath()))); + bool recSupported = false; tryReportingError([&]{ - recExists = recycleBinExists(baseDirPf, [&] { callback.reportStatus(msg); /*may throw*/ }); //throw FileError + recSupported = baseFolder.supportsRecycleBin([&] { callback.reportStatus(msg); /*may throw*/ }); //throw FileError }, callback); //throw X? - hasRecyclerBuffer.emplace(baseDirPf, recExists); - return recExists; - -#elif defined ZEN_LINUX || defined ZEN_MAC - return true; -#endif + recyclerSupported.emplace(&baseFolder, recSupported); + return recSupported; }; for (FileSystemObject* row : rowsIn) if (!row->isEmpty<side>()) { - if (useRecycleBin && hasRecycler(row->root().getBaseDirPf<side>())) //Windows' ::SHFileOperation() will delete permanently anyway, but we have a superior deletion routine + if (useRecycleBin && hasRecycler(row->root().getABF<side>())) //Windows' ::SHFileOperation() will delete permanently anyway, but we have a superior deletion routine deleteRecyler.push_back(row); else deletePermanent.push_back(row); @@ -1173,7 +1170,7 @@ void categorize(const std::set<FileSystemObject*>& rowsIn, template <SelectedSide side> -struct ItemDeleter : public FSObjectVisitor //throw FileError, but nothrow constructor!!! +struct ItemDeleter : public FSObjectVisitor //throw FileError, but nothrow constructor!!! { ItemDeleter(bool useRecycleBin, DeleteFilesHandler& handler) : handler_(handler), useRecycleBin_(useRecycleBin) @@ -1194,52 +1191,52 @@ struct ItemDeleter : public FSObjectVisitor //throw FileError, but nothrow cons void visit(const FilePair& fileObj) override { - notifyFileDeletion(fileObj.getFullPath<side>()); + notifyFileDeletion(ABF::getDisplayPath(fileObj.getAbstractPath<side>())); if (useRecycleBin_) - zen::recycleOrDelete(fileObj.getFullPath<side>()); //throw FileError + ABF::recycleItemDirectly(fileObj.getAbstractPath<side>()); //throw FileError else - zen::removeFile(fileObj.getFullPath<side>()); //throw FileError + ABF::removeFile(fileObj.getAbstractPath<side>()); //throw FileError } void visit(const SymlinkPair& linkObj) override { - notifySymlinkDeletion(linkObj.getFullPath<side>()); + notifySymlinkDeletion(ABF::getDisplayPath(linkObj.getAbstractPath<side>())); if (useRecycleBin_) - zen::recycleOrDelete(linkObj.getFullPath<side>()); //throw FileError + ABF::recycleItemDirectly(linkObj.getAbstractPath<side>()); //throw FileError else { - if (dirExists(linkObj.getFullPath<side>())) //dir symlink - zen::removeDirectory(linkObj.getFullPath<side>()); //throw FileError + if (ABF::dirExists(linkObj.getAbstractPath<side>())) //dir symlink + ABF::removeFolder(linkObj.getAbstractPath<side>()); //throw FileError else //file symlink, broken symlink - zen::removeFile(linkObj.getFullPath<side>()); //throw FileError + ABF::removeFile(linkObj.getAbstractPath<side>()); //throw FileError } } void visit(const DirPair& dirObj) override { - notifyDirectoryDeletion(dirObj.getFullPath<side>()); //notfied twice; see below -> no big deal + notifyDirectoryDeletion(ABF::getDisplayPath(dirObj.getAbstractPath<side>())); //notfied twice; see below -> no big deal if (useRecycleBin_) - zen::recycleOrDelete(dirObj.getFullPath<side>()); //throw FileError + ABF::recycleItemDirectly(dirObj.getAbstractPath<side>()); //throw FileError else { - auto onBeforeFileDeletion = [&](const Zstring& filepath) { this->notifyFileDeletion (filepath); }; //without "this->" GCC 4.7.2 runtime crash on Debian - auto onBeforeDirDeletion = [&](const Zstring& dirpath ) { this->notifyDirectoryDeletion(dirpath ); }; + auto onBeforeFileDeletion = [&](const Zstring& displayPath) { this->notifyFileDeletion (displayPath); }; //without "this->" GCC 4.7.2 runtime crash on Debian + auto onBeforeDirDeletion = [&](const Zstring& displayPath) { this->notifyDirectoryDeletion(displayPath ); }; - zen::removeDirectory(dirObj.getFullPath<side>(), onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError + ABF::removeFolder(dirObj.getAbstractPath<side>(), onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError } } private: - void notifyFileDeletion (const Zstring& objName) { notifyItemDeletion(txtRemovingFile , objName); } - void notifyDirectoryDeletion(const Zstring& objName) { notifyItemDeletion(txtRemovingDirectory, objName); } - void notifySymlinkDeletion (const Zstring& objName) { notifyItemDeletion(txtRemovingSymlink , objName); } + void notifyFileDeletion (const Zstring& displayPath) { notifyItemDeletion(txtRemovingFile , displayPath); } + void notifyDirectoryDeletion(const Zstring& displayPath) { notifyItemDeletion(txtRemovingDirectory, displayPath); } + void notifySymlinkDeletion (const Zstring& displayPath) { notifyItemDeletion(txtRemovingSymlink , displayPath); } - void notifyItemDeletion(const std::wstring& statusText, const Zstring& objName) + void notifyItemDeletion(const std::wstring& statusText, const Zstring& displayPath) { - handler_.reportStatus(replaceCpy(statusText, L"%x", fmtFileName(objName))); + handler_.reportStatus(replaceCpy(statusText, L"%x", fmtFileName(displayPath))); } DeleteFilesHandler& handler_; @@ -1336,19 +1333,19 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete std::vector<FileSystemObject*> deleteRecylerLeft; std::vector<FileSystemObject*> deleteRecylerRight; - std::map<Zstring, bool, LessFilename> hasRecyclerBuffer; - categorize<LEFT_SIDE >(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, hasRecyclerBuffer, statusHandler); - categorize<RIGHT_SIDE>(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, hasRecyclerBuffer, statusHandler); + std::map<const ABF*, bool, ABF::LessItemPath> recyclerSupported; + categorize<LEFT_SIDE >(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, recyclerSupported, statusHandler); + categorize<RIGHT_SIDE>(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, recyclerSupported, statusHandler); //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong if (useRecycleBin && - std::any_of(hasRecyclerBuffer.begin(), hasRecyclerBuffer.end(), [](std::pair<Zstring, bool> item) { return !item.second; })) + std::any_of(recyclerSupported.begin(), recyclerSupported.end(), [](const decltype(recyclerSupported)::value_type& item) { return !item.second; })) { std::wstring msg = _("The recycle bin is not available for the following folders. Files will be deleted permanently instead:") + L"\n"; - for (const auto& item : hasRecyclerBuffer) + for (const auto& item : recyclerSupported) if (!item.second) - msg += std::wstring(L"\n") + item.first; + msg += std::wstring(L"\n") + ABF::getDisplayPath(item.first->getAbstractPath()); statusHandler.reportWarning(msg, warningRecyclerMissing); //throw? } diff --git a/FreeFileSync/Source/algorithm.h b/FreeFileSync/Source/algorithm.h index 5adaafbb..6c6cca18 100644 --- a/FreeFileSync/Source/algorithm.h +++ b/FreeFileSync/Source/algorithm.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ALGORITHM_H_INCLUDED -#define ALGORITHM_H_INCLUDED +#ifndef ALGORITHM_H_34218518475321452548 +#define ALGORITHM_H_34218518475321452548 #include <functional> #include "file_hierarchy.h" @@ -17,8 +17,15 @@ void swapGrids(const MainConfiguration& config, FolderComparison& folderCmp); std::vector<DirectionConfig> extractDirectionCfg(const MainConfiguration& mainCfg); -void redetermineSyncDirection(const DirectionConfig& directConfig, BaseDirPair& baseDirectory, std::function<void(const std::wstring& msg)> reportWarning); -void redetermineSyncDirection(const MainConfiguration& mainCfg, FolderComparison& folderCmp, std::function<void(const std::wstring& msg)> reportWarning); +void redetermineSyncDirection(const DirectionConfig& directConfig, + BaseDirPair& baseDirectory, + const std::function<void(const std::wstring& msg)>& reportWarning, + const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus); + +void redetermineSyncDirection(const MainConfiguration& mainCfg, + FolderComparison& folderCmp, + const std::function<void(const std::wstring& msg)>& reportWarning, + const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus); void setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsObj); //set new direction (recursively) @@ -63,4 +70,4 @@ void deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDeleteOnLef bool& warningRecyclerMissing); } -#endif //ALGORITHM_H_INCLUDED +#endif //ALGORITHM_H_34218518475321452548 diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp index 567907ea..5cb6f50b 100644 --- a/FreeFileSync/Source/application.cpp +++ b/FreeFileSync/Source/application.cpp @@ -618,7 +618,7 @@ void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCf BatchStatusHandler statusHandler(!batchCfg.runMinimized, //throw BatchAbortProcess extractJobName(referenceFile), timeStamp, - batchCfg.logFileDirectory, + batchCfg.logFolderPathPhrase, batchCfg.logfilesCountLimit, globalCfg.lastSyncsLogFileSizeMax, batchCfg.handleError, diff --git a/FreeFileSync/Source/comparison.cpp b/FreeFileSync/Source/comparison.cpp index f39415db..b68c3a0c 100644 --- a/FreeFileSync/Source/comparison.cpp +++ b/FreeFileSync/Source/comparison.cpp @@ -5,22 +5,15 @@ // ************************************************************************** #include "comparison.h" -#include <stdexcept> -#include <numeric> -#include <zen/perf.h> -#include <zen/scope_guard.h> #include <zen/process_priority.h> -#include <zen/symlink_target.h> -#include <zen/format_unit.h> -#include <zen/stl_tools.h> +#include <zen/perf.h> #include "algorithm.h" #include "lib/parallel_scan.h" -#include "lib/resolve_path.h" #include "lib/dir_exist_async.h" #include "lib/binary.h" #include "lib/cmp_filetime.h" #include "lib/status_handler_impl.h" -#include "lib/parallel_scan.h" +#include "fs/concrete.h" using namespace zen; @@ -56,62 +49,82 @@ namespace { struct ResolvedFolderPair { - ResolvedFolderPair(const Zstring& left, const Zstring& right) : - dirpathLeft(left), - dirpathRight(right) {} + ResolvedFolderPair(const std::shared_ptr<ABF>& left, + const std::shared_ptr<ABF>& right) : + abfLeft (left), + abfRight(right) {} - Zstring dirpathLeft; //resolved directory names - Zstring dirpathRight; // + std::shared_ptr<ABF> abfLeft; //always bound + std::shared_ptr<ABF> abfRight; // }; -std::vector<ResolvedFolderPair> resolveDirectoryNames(const std::vector<FolderPairCfg>& cfgList) +struct ResolvedBaseFolders { - std::vector<ResolvedFolderPair> output; - - for (const FolderPairCfg& fpCfg : cfgList) - output.emplace_back(getFormattedDirectoryPath(fpCfg.dirpathPhraseLeft), - getFormattedDirectoryPath(fpCfg.dirpathPhraseRight)); - warn_static("get volume by name for idle HDD! => call async getFormattedDirectoryPath, but currently not thread-safe") - return output; -} - + std::vector<ResolvedFolderPair> resolvedPairs; + std::set<const ABF*, ABF::LessItemPath> existingBaseFolders; //references resolvedPairs variable!!! +}; -struct ResolutionInfo +struct LessItemPathSharedPtr { - std::vector<ResolvedFolderPair> resolvedPairs; - std::set<Zstring, LessFilename> existingDirs; + bool operator()(const std::shared_ptr<ABF>& lhs, const std::shared_ptr<ABF>& rhs) const + { + return ABF::LessItemPath()(lhs.get(), rhs.get()); + } }; -ResolutionInfo resolveFolderPairs(const std::vector<FolderPairCfg>& cfgList, - bool allowUserInteraction, - ProcessCallback& callback) +ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& cfgList, + bool allowUserInteraction, + ProcessCallback& callback) { - ResolutionInfo output; + ResolvedBaseFolders output; tryReportingError([&] { - //support "retry" for environment variable and and variable driver letter resolution! - output.resolvedPairs = resolveDirectoryNames(cfgList); - assert(output.resolvedPairs.size() == cfgList.size()); //postcondition! + std::set<std::shared_ptr<ABF>, LessItemPathSharedPtr> uniqueBaseFolders; - std::set<Zstring, LessFilename> dirpaths; - for (const ResolvedFolderPair& fp : output.resolvedPairs) + //support "retry" for environment variable and and variable driver letter resolution! + output.resolvedPairs.clear(); + for (const FolderPairCfg& fpCfg : cfgList) { - dirpaths.insert(fp.dirpathLeft); - dirpaths.insert(fp.dirpathRight); + std::shared_ptr<ABF> abfLeft = createAbstractBaseFolder(fpCfg.dirpathPhraseLeft); + std::shared_ptr<ABF> abfRight = createAbstractBaseFolder(fpCfg.dirpathPhraseRight); + + auto rv1 = uniqueBaseFolders.insert(abfLeft); + if (!rv1.second) abfLeft = *rv1.first; //use same instance for all identical base paths => e.g. single FTP connect attempt for all! + + auto rv2 = uniqueBaseFolders.insert(abfRight); + if (!rv2.second) abfRight = *rv2.first; + + output.resolvedPairs.emplace_back(abfLeft, abfRight); } - const DirectoryStatus dirStatus = getExistingDirsUpdating(dirpaths, allowUserInteraction, callback); //check *all* directories on each try! - output.existingDirs = dirStatus.existing; + std::set<const ABF*, ABF::LessItemPath> uniqueBaseFolders2; + for (const auto& item : uniqueBaseFolders) + uniqueBaseFolders2.insert(item.get()); - if (!dirStatus.missing.empty()) + const DirectoryStatus status = checkFolderExistenceUpdating(uniqueBaseFolders2, allowUserInteraction, callback); //re-check *all* directories on each try! + output.existingBaseFolders = status.existingBaseFolder; + + if (!status.missingBaseFolder.empty() || !status.failedChecks.empty()) { - std::wstring msg = _("Cannot find the following folders:") + L"\n"; - for (const Zstring& dirpath : dirStatus.missing) - msg += std::wstring(L"\n") + dirpath; - throw FileError(msg, _("You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.")); + std::wstring errorMsg = _("Cannot find the following folders:") + L"\n"; + + for (const auto& baseFolder : status.missingBaseFolder) + errorMsg += std::wstring(L"\n") + ABF::getDisplayPath(baseFolder->getAbstractPath()); + + for (const auto& fc : status.failedChecks) + errorMsg += std::wstring(L"\n") + ABF::getDisplayPath(fc.first->getAbstractPath()); + + if (!status.failedChecks.empty()) + { + errorMsg += L"\n"; + for (const auto& fc : status.failedChecks) + errorMsg += std::wstring(L"\n") + fc.second.toString(); + } + + throw FileError(errorMsg, _("You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.")); } }, callback); //throw X? @@ -125,9 +138,9 @@ void checkForIncompleteInput(const std::vector<ResolvedFolderPair>& folderPairs, bool haveFullPair = false; for (const ResolvedFolderPair& fp : folderPairs) - if (fp.dirpathLeft.empty() != fp.dirpathRight.empty()) + if (fp.abfLeft->emptyBaseFolderPath() != fp.abfRight->emptyBaseFolderPath()) havePartialPair = true; - else if (!fp.dirpathLeft.empty()) + else if (!fp.abfLeft->emptyBaseFolderPath()) haveFullPair = true; if (havePartialPair == haveFullPair) //error if: all empty or exist both full and partial pairs -> support single-dir scenario @@ -140,28 +153,21 @@ void checkForIncompleteInput(const std::vector<ResolvedFolderPair>& folderPairs, //similar check if one directory is read/written by multiple pairs not before beginning of synchronization void checkFolderDependency(const std::vector<ResolvedFolderPair>& folderPairs, bool& warningDependentFolders, ProcessCallback& callback) //returns warning message, empty if all ok { - std::vector<std::pair<Zstring, Zstring>> dependentDirs; - - auto havePathDependency = [](const Zstring& lhs, const Zstring& rhs) - { - return EqualFilename()(Zstring(lhs.c_str(), std::min(lhs.length(), rhs.length())), //note: this is NOT an equivalence relation! - Zstring(rhs.c_str(), std::min(lhs.length(), rhs.length()))); - }; + std::vector<ResolvedFolderPair> dependentFolderPairs; for (const ResolvedFolderPair& fp : folderPairs) - if (!fp.dirpathLeft.empty() && !fp.dirpathRight.empty()) //empty folders names may be accepted by user - { - if (havePathDependency(fp.dirpathLeft, fp.dirpathRight)) //test wheter leftDirectory begins with rightDirectory or the other way round - dependentDirs.emplace_back(fp.dirpathLeft, fp.dirpathRight); - } + if (!fp.abfLeft->emptyBaseFolderPath() && !fp.abfRight->emptyBaseFolderPath()) //empty folders names may be accepted by user + //test wheter leftDirectory begins with rightDirectory or the other way round + if (ABF::havePathDependency(*fp.abfLeft, *fp.abfRight)) + dependentFolderPairs.push_back(fp); - if (!dependentDirs.empty()) + if (!dependentFolderPairs.empty()) { std::wstring warningMsg = _("The following folder paths are dependent from each other:"); - for (auto it = dependentDirs.begin(); it != dependentDirs.end(); ++it) + for (const ResolvedFolderPair& pair : dependentFolderPairs) warningMsg += std::wstring(L"\n\n") + - it->first + L"\n" + - it->second; + ABF::getDisplayPath(pair.abfLeft ->getAbstractPath()) + L"\n" + + ABF::getDisplayPath(pair.abfRight->getAbstractPath()); callback.reportWarning(warningMsg, warningDependentFolders); } @@ -246,9 +252,9 @@ const wchar_t arrowRight[] = L"-->"; //check for very old dates or dates in the future -std::wstring getConflictInvalidDate(const Zstring& fileNameFull, std::int64_t utcTime) +std::wstring getConflictInvalidDate(const Zstring& displayPath, std::int64_t utcTime) { - return replaceCpy(_("File %x has an invalid date."), L"%x", fmtFileName(fileNameFull)) + L"\n" + + return replaceCpy(_("File %x has an invalid date."), L"%x", fmtFileName(displayPath)) + L"\n" + _("Date:") + L" " + utcToLocalTimeString(utcTime); } @@ -312,11 +318,11 @@ void categorizeSymlinkByTime(SymlinkPair& linkObj, int fileTimeTolerance, unsign break; case TimeResult::LEFT_INVALID: - linkObj.setCategoryConflict(getConflictInvalidDate(linkObj.getFullPath<LEFT_SIDE>(), linkObj.getLastWriteTime<LEFT_SIDE>())); + linkObj.setCategoryConflict(getConflictInvalidDate(ABF::getDisplayPath(linkObj.getAbstractPath<LEFT_SIDE>()), linkObj.getLastWriteTime<LEFT_SIDE>())); break; case TimeResult::RIGHT_INVALID: - linkObj.setCategoryConflict(getConflictInvalidDate(linkObj.getFullPath<RIGHT_SIDE>(), linkObj.getLastWriteTime<RIGHT_SIDE>())); + linkObj.setCategoryConflict(getConflictInvalidDate(ABF::getDisplayPath(linkObj.getAbstractPath<RIGHT_SIDE>()), linkObj.getLastWriteTime<RIGHT_SIDE>())); break; } } @@ -364,11 +370,11 @@ std::shared_ptr<BaseDirPair> ComparisonBuffer::compareByTimeSize(const ResolvedF break; case TimeResult::LEFT_INVALID: - fileObj->setCategoryConflict(getConflictInvalidDate(fileObj->getFullPath<LEFT_SIDE>(), fileObj->getLastWriteTime<LEFT_SIDE>())); + fileObj->setCategoryConflict(getConflictInvalidDate(ABF::getDisplayPath(fileObj->getAbstractPath<LEFT_SIDE>()), fileObj->getLastWriteTime<LEFT_SIDE>())); break; case TimeResult::RIGHT_INVALID: - fileObj->setCategoryConflict(getConflictInvalidDate(fileObj->getFullPath<RIGHT_SIDE>(), fileObj->getLastWriteTime<RIGHT_SIDE>())); + fileObj->setCategoryConflict(getConflictInvalidDate(ABF::getDisplayPath(fileObj->getAbstractPath<RIGHT_SIDE>()), fileObj->getLastWriteTime<RIGHT_SIDE>())); break; } } @@ -383,11 +389,12 @@ void categorizeSymlinkByContent(SymlinkPair& linkObj, int fileTimeTolerance, uns Zstring targetPathRawR; Opt<std::wstring> errMsg = tryReportingError([&] { - callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtFileName(linkObj.getFullPath<LEFT_SIDE>()))); - targetPathRawL = getSymlinkTargetRaw(linkObj.getFullPath<LEFT_SIDE>()); //throw FileError + callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtFileName(ABF::getDisplayPath(linkObj.getAbstractPath<LEFT_SIDE>())))); + + targetPathRawL = ABF::getSymlinkContentBuffer(linkObj.getAbstractPath<LEFT_SIDE>()); //throw FileError - callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtFileName(linkObj.getFullPath<RIGHT_SIDE>()))); - targetPathRawR = getSymlinkTargetRaw(linkObj.getFullPath<RIGHT_SIDE>()); //throw FileError + callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtFileName(ABF::getDisplayPath(linkObj.getAbstractPath<RIGHT_SIDE>())))); + targetPathRawR = ABF::getSymlinkContentBuffer(linkObj.getAbstractPath<RIGHT_SIDE>()); //throw FileError }, callback); //throw X? if (errMsg) @@ -397,8 +404,8 @@ void categorizeSymlinkByContent(SymlinkPair& linkObj, int fileTimeTolerance, uns if (targetPathRawL == targetPathRawR #ifdef ZEN_WIN //type of symbolic link is relevant for Windows only && - dirExists(linkObj.getFullPath<LEFT_SIDE >()) == //check if dir-symlink - dirExists(linkObj.getFullPath<RIGHT_SIDE>()) // + ABF::dirExists(linkObj.getAbstractPath<LEFT_SIDE >()) == //check if dir-symlink + ABF::dirExists(linkObj.getAbstractPath<RIGHT_SIDE>()) // #endif ) { @@ -478,7 +485,7 @@ std::list<std::shared_ptr<BaseDirPair>> ComparisonBuffer::compareByContent(const //compare files (that have same size) bytewise... for (FilePair* fileObj : filesToCompareBytewise) { - callback_.reportStatus(replaceCpy(txtComparingContentOfFiles, L"%x", fmtFileName(fileObj->getPairRelativePath()), false)); + callback_.reportStatus(replaceCpy(txtComparingContentOfFiles, L"%x", fmtFileName(fileObj->getPairRelativePath()))); //check files that exist in left and right model but have different content @@ -489,8 +496,8 @@ std::list<std::shared_ptr<BaseDirPair>> ComparisonBuffer::compareByContent(const auto onUpdateStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - haveSameContent = filesHaveSameContent(fileObj->getFullPath<LEFT_SIDE>(), - fileObj->getFullPath<RIGHT_SIDE>(), onUpdateStatus); //throw FileError + haveSameContent = filesHaveSameContent(fileObj->getAbstractPath<LEFT_SIDE>(), + fileObj->getAbstractPath<RIGHT_SIDE>(), onUpdateStatus); //throw FileError statReporter.reportDelta(1, 0); statReporter.reportFinished(); @@ -526,7 +533,7 @@ std::list<std::shared_ptr<BaseDirPair>> ComparisonBuffer::compareByContent(const class MergeSides { public: - MergeSides(const std::map<Zstring, std::wstring, LessFilename>& failedItemReads, + MergeSides(const std::map<Zstring, std::wstring, LessFilePath>& failedItemReads, std::vector<FilePair*>& undefinedFilesOut, std::vector<SymlinkPair*>& undefinedLinksOut) : failedItemReads_(failedItemReads), @@ -550,7 +557,7 @@ private: const std::wstring* checkFailedRead(FileSystemObject& fsObj, const std::wstring* errorMsg); - const std::map<Zstring, std::wstring, LessFilename>& failedItemReads_; //base-relative paths or empty if read-error for whole base directory + const std::map<Zstring, std::wstring, LessFilePath>& failedItemReads_; //base-relative paths or empty if read-error for whole base directory std::vector<FilePair*>& undefinedFiles; std::vector<SymlinkPair*>& undefinedLinks; }; @@ -724,7 +731,7 @@ void stripExcludedDirectories(HierarchyObject& hierObj, const HardFilter& filter if (!included) //falsify only! (e.g. might already be inactive due to read error!) dirObj.setActive(false); - return !included & //don't check active status, but eval filter directly! + return !included && //don't check active status, but eval filter directly! dirObj.refSubDirs ().empty() && dirObj.refSubLinks().empty() && dirObj.refSubFiles().empty(); @@ -741,16 +748,16 @@ std::shared_ptr<BaseDirPair> ComparisonBuffer::performComparison(const ResolvedF callback_.reportStatus(_("Generating file list...")); callback_.forceUiRefresh(); - auto getDirValue = [&](const Zstring& dirpathFmt) -> const DirectoryValue* + auto getDirValue = [&](const ABF& baseFolder) -> const DirectoryValue* { - auto it = directoryBuffer.find(DirectoryKey(dirpathFmt, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); + auto it = directoryBuffer.find(DirectoryKey(baseFolder, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); return it != directoryBuffer.end() ? &it->second : nullptr; }; - const DirectoryValue* bufValueLeft = getDirValue(fp.dirpathLeft); - const DirectoryValue* bufValueRight = getDirValue(fp.dirpathRight); + const DirectoryValue* bufValueLeft = getDirValue(*fp.abfLeft); + const DirectoryValue* bufValueRight = getDirValue(*fp.abfRight); - std::map<Zstring, std::wstring, LessFilename> failedReads; //base-relative paths or empty if read-error for whole base directory + std::map<Zstring, std::wstring, LessFilePath> failedReads; //base-relative paths or empty if read-error for whole base directory { //mix failedDirReads with failedItemReads: //mark directory errors already at directory-level (instead for child items only) to show on GUI! See "MergeSides" @@ -769,9 +776,9 @@ std::shared_ptr<BaseDirPair> ComparisonBuffer::performComparison(const ResolvedF for (const auto& item : failedReads) excludefilterFailedRead += item.first + Zstr("\n"); //exclude item AND (potential) child items! - std::shared_ptr<BaseDirPair> output = std::make_shared<BaseDirPair>(fp.dirpathLeft, + std::shared_ptr<BaseDirPair> output = std::make_shared<BaseDirPair>(fp.abfLeft, bufValueLeft != nullptr, //dir existence must be checked only once: available iff buffer entry exists! - fp.dirpathRight, + fp.abfRight, bufValueRight != nullptr, fpCfg.filter.nameFilter->copyFilterAddingExclusion(excludefilterFailedRead), fpCfg.compareVar, @@ -841,7 +848,7 @@ void zen::compare(xmlAccess::OptionalDialogs& warnings, //-------------------some basic checks:------------------------------------------ - const ResolutionInfo& resInfo = resolveFolderPairs(cfgList, allowUserInteraction, callback); + const ResolvedBaseFolders& resInfo = initializeBaseFolders(cfgList, allowUserInteraction, callback); //directory existence only checked *once* to avoid race conditions! if (resInfo.resolvedPairs.size() != cfgList.size()) @@ -854,7 +861,7 @@ void zen::compare(xmlAccess::OptionalDialogs& warnings, //-------------------end of basic checks------------------------------------------ - auto dirAvailable = [&](const Zstring& dirpathFmt) { return resInfo.existingDirs.find(dirpathFmt) != resInfo.existingDirs.end(); }; + auto basefolderExisting = [&](const ABF& baseFolder) { return resInfo.existingBaseFolders.find(&baseFolder) != resInfo.existingBaseFolders.end(); }; std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>> totalWorkLoad; for (size_t i = 0; i < cfgList.size(); ++i) @@ -862,7 +869,14 @@ void zen::compare(xmlAccess::OptionalDialogs& warnings, //lock (existing) directories before comparison if (createDirLocks) - dirLocks = zen::make_unique<LockHolder>(resInfo.existingDirs, warnings.warningDirectoryLockFailed, callback); + { + std::set<Zstring, LessFilePath> dirPathsExisting; + for (const ABF* baseFolder : resInfo.existingBaseFolders) + if (Opt<Zstring> folderPath = ABF::getNativeItemPath(baseFolder->getAbstractPath())) //restrict directory locking to native paths until further + dirPathsExisting.insert(*folderPath); + + dirLocks = zen::make_unique<LockHolder>(dirPathsExisting, warnings.warningDirectoryLockFailed, callback); + } try { @@ -871,10 +885,10 @@ void zen::compare(xmlAccess::OptionalDialogs& warnings, for (const auto& w : totalWorkLoad) { - if (dirAvailable(w.first.dirpathLeft)) //only traverse *currently existing* directories: at this point user is aware that non-ex + empty string are seen as empty folder! - dirsToRead.emplace(w.first.dirpathLeft, w.second.filter.nameFilter, w.second.handleSymlinks); - if (dirAvailable(w.first.dirpathRight)) - dirsToRead.emplace(w.first.dirpathRight, w.second.filter.nameFilter, w.second.handleSymlinks); + if (basefolderExisting(*w.first.abfLeft)) //only traverse *currently existing* directories: at this point user is aware that non-ex + empty string are seen as empty folder! + dirsToRead.emplace(*w.first.abfLeft, w.second.filter.nameFilter, w.second.handleSymlinks); + if (basefolderExisting(*w.first.abfRight)) + dirsToRead.emplace(*w.first.abfRight, w.second.filter.nameFilter, w.second.handleSymlinks); } FolderComparison outputTmp; //write to output as a transaction! @@ -927,8 +941,10 @@ void zen::compare(xmlAccess::OptionalDialogs& warnings, callback.reportStatus(_("Calculating sync directions...")); callback.forceUiRefresh(); + zen::redetermineSyncDirection(fpCfg.directionCfg, *j, - [&](const std::wstring& warning) { callback.reportWarning(warning, warnings.warningDatabaseError); }); + [&](const std::wstring& warning) { callback.reportWarning(warning, warnings.warningDatabaseError); }, + [&](std::int64_t bytesDelta) { callback.requestUiRefresh(); });//throw X } //output is written only if everything was processed correctly diff --git a/FreeFileSync/Source/file_hierarchy.cpp b/FreeFileSync/Source/file_hierarchy.cpp index 8fb6a323..50d5ee1b 100644 --- a/FreeFileSync/Source/file_hierarchy.cpp +++ b/FreeFileSync/Source/file_hierarchy.cpp @@ -408,7 +408,7 @@ std::wstring zen::getSyncOpDescription(const FileSystemObject& fsObj) const Zstring relTarget = getRelName(*targetFile, !onLeft); return getSyncOpDescription(op) + L"\n" + - (EqualFilename()(beforeLast(relSource, FILE_NAME_SEPARATOR), beforeLast(relTarget, FILE_NAME_SEPARATOR)) ? //returns empty string if ch not found + (EqualFilePath()(beforeLast(relSource, FILE_NAME_SEPARATOR), beforeLast(relTarget, FILE_NAME_SEPARATOR)) ? //returns empty string if ch not found //detected pure "rename" fmtFileName(afterLast(relSource, FILE_NAME_SEPARATOR)) + L" ->\n" + //show short name only fmtFileName(afterLast(relTarget, FILE_NAME_SEPARATOR)) : diff --git a/FreeFileSync/Source/file_hierarchy.h b/FreeFileSync/Source/file_hierarchy.h index a3c162ea..249346ca 100644 --- a/FreeFileSync/Source/file_hierarchy.h +++ b/FreeFileSync/Source/file_hierarchy.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FILEHIERARCHY_H_INCLUDED -#define FILEHIERARCHY_H_INCLUDED +#ifndef FILEHIERARCHY_H_257235289645296 +#define FILEHIERARCHY_H_257235289645296 #include <map> #include <cstddef> //required by GCC 4.8.1 to find ptrdiff_t @@ -20,31 +20,30 @@ #include <zen/file_id_def.h> #include "structures.h" #include "lib/hard_filter.h" +#include "fs/native.h" namespace zen { +using ABF = AbstractBaseFolder; + struct FileDescriptor { - FileDescriptor() : lastWriteTimeRaw(), fileSize(), fileIdx(), devId(), isFollowedSymlink() {} + FileDescriptor() : lastWriteTimeRaw(), fileSize(), fileId(), isFollowedSymlink() {} FileDescriptor(std::int64_t lastWriteTimeRawIn, std::uint64_t fileSizeIn, - const FileId& idIn, + const ABF::FileId& idIn, bool isSymlink) : lastWriteTimeRaw(lastWriteTimeRawIn), fileSize(fileSizeIn), - fileIdx(idIn.second), - devId(idIn.first), + fileId(idIn), isFollowedSymlink(isSymlink) {} std::int64_t lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) std::uint64_t fileSize; - FileIndex fileIdx; // == file id: optional! (however, always set on Linux, and *generally* available on Windows) - DeviceId devId; //split into file id into components to avoid padding overhead of a std::pair! + ABF::FileId fileId; // optional! bool isFollowedSymlink; }; -inline -FileId getFileId(const FileDescriptor& fd) { return FileId(fd.devId, fd.fileIdx); } struct LinkDescriptor { @@ -100,9 +99,9 @@ class FileSystemObject; struct DirContainer { //------------------------------------------------------------------ - typedef std::map<Zstring, DirContainer, LessFilename> DirList; // - typedef std::map<Zstring, FileDescriptor, LessFilename> FileList; //key: file name - typedef std::map<Zstring, LinkDescriptor, LessFilename> LinkList; // + typedef std::map<Zstring, DirContainer, LessFilePath> DirList; // + typedef std::map<Zstring, FileDescriptor, LessFilePath> FileList; //key: file name + typedef std::map<Zstring, LinkDescriptor, LessFilePath> LinkList; // //------------------------------------------------------------------ DirContainer() = default; @@ -231,9 +230,9 @@ private: class BaseDirPair : public HierarchyObject //synchronization base directory { public: - BaseDirPair(const Zstring& dirPostfixedLeft, + BaseDirPair(const std::shared_ptr<ABF>& abfLeftIn, bool dirExistsLeft, - const Zstring& dirPostfixedRight, + const std::shared_ptr<ABF>& abfRightIn, bool dirExistsRight, const HardFilter::FilterRef& filter, CompareVariant cmpVar, @@ -244,12 +243,13 @@ public: #endif HierarchyObject(Zstring(), *this), filter_(filter), cmpVar_(cmpVar), fileTimeTolerance_(fileTimeTolerance), optTimeShiftHours_(optTimeShiftHours), - baseDirPfL (dirPostfixedLeft ), - baseDirPfR (dirPostfixedRight), - dirExistsLeft_ (dirExistsLeft ), - dirExistsRight_(dirExistsRight) {} + dirExistsLeft_ (dirExistsLeft), + dirExistsRight_(dirExistsRight), + abfLeft(abfLeftIn), + abfRight(abfRightIn) {} + + template <SelectedSide side> ABF& getABF() const; - template <SelectedSide side> const Zstring& getBaseDirPf() const; //base sync directory postfixed with FILE_NAME_SEPARATOR (or empty!) static void removeEmpty(BaseDirPair& baseDir) { baseDir.removeEmptyRec(); }; //physically remove all invalid entries (where both sides are empty) recursively template <SelectedSide side> bool isExisting() const; //status of directory existence at the time of comparison! @@ -258,7 +258,7 @@ public: //get settings which were used while creating BaseDirPair const HardFilter& getFilter() const { return *filter_; } CompareVariant getCompVariant() const { return cmpVar_; } - int getFileTimeTolerance() const { return fileTimeTolerance_; } + int getFileTimeTolerance() const { return fileTimeTolerance_; } unsigned int getTimeShift() const { return optTimeShiftHours_; } void flip() override; @@ -269,24 +269,24 @@ private: const int fileTimeTolerance_; const unsigned int optTimeShiftHours_; - Zstring baseDirPfL; //base sync dir postfixed - Zstring baseDirPfR; // - bool dirExistsLeft_; bool dirExistsRight_; + + std::shared_ptr<ABF> abfLeft; //always bound + std::shared_ptr<ABF> abfRight; // }; template <> inline -const Zstring& BaseDirPair::getBaseDirPf<LEFT_SIDE>() const { return baseDirPfL; } +ABF& BaseDirPair::getABF<LEFT_SIDE>() const { return *abfLeft; } template <> inline -const Zstring& BaseDirPair::getBaseDirPf<RIGHT_SIDE>() const { return baseDirPfR; } +ABF& BaseDirPair::getABF<RIGHT_SIDE>() const { return *abfRight; } //get rid of shared_ptr indirection -template <class IterTy, //underlying iterator type - class U> //target object type +template <class IterTy, //underlying iterator type + class U> //target object type class DerefIter : public std::iterator<std::bidirectional_iterator_tag, U> { public: @@ -364,9 +364,11 @@ public: Zstring getPairRelativePath() const; //like getRelativePath() but also returns value if either side is empty template <SelectedSide side> bool isEmpty() const; template <SelectedSide side> const Zstring& getItemName() const; //case sensitive! - template <SelectedSide side> Zstring getRelativePath() const; //get name relative to base sync dir without FILE_NAME_SEPARATOR prefix - template <SelectedSide side> const Zstring& getBaseDirPf() const; //base sync directory postfixed with FILE_NAME_SEPARATOR - template <SelectedSide side> Zstring getFullPath() const; //getFullPath() == getBaseDirPf() + getRelativePath() + template <SelectedSide side> Zstring getRelativePath() const; //get path relative to base sync dir without FILE_NAME_SEPARATOR prefix + +public: + template <SelectedSide side> ABF& getABF() const; + template <SelectedSide side> AbstractPathRef getAbstractPath() const; //comparison result CompareFilesResult getCategory() const { return cmpResult; } @@ -503,8 +505,8 @@ public: template <SelectedSide side> std::int64_t getLastWriteTime() const; template <SelectedSide side> std::uint64_t getFileSize() const; - template <SelectedSide side> FileId getFileId () const; - template <SelectedSide side> bool isFollowedSymlink() const; + template <SelectedSide side> ABF::FileId getFileId () const; + template <SelectedSide side> bool isFollowedSymlink() const; void setMoveRef(ObjectId refId) { moveFileRef = refId; } //reference to corresponding renamed file ObjectId getMoveRef() const { return moveFileRef; } //may be nullptr @@ -519,8 +521,8 @@ public: std::uint64_t fileSize, std::int64_t lastWriteTimeTrg, std::int64_t lastWriteTimeSrc, - const FileId& fileIdTrg, - const FileId& fileIdSrc, + const ABF::FileId& fileIdTrg, + const ABF::FileId& fileIdSrc, bool isSymlinkTrg, bool isSymlinkSrc); @@ -736,18 +738,17 @@ Zstring FileSystemObject::getPairShortName() const template <SelectedSide side> inline -Zstring FileSystemObject::getFullPath() const +ABF& FileSystemObject::getABF() const { - if (isEmpty<side>()) //avoid ternary-WTF! - return Zstring(); - return getBaseDirPf<side>() + parent_.getPairRelativePathPf() + getItemName<side>(); + return root().getABF<side>(); } template <SelectedSide side> inline -const Zstring& FileSystemObject::getBaseDirPf() const +AbstractPathRef FileSystemObject::getAbstractPath() const { - return root().getBaseDirPf<side>(); + assert(!isEmpty<side>()); + return root().getABF<side>().getAbstractPath(parent_.getPairRelativePathPf() + getItemName<side>()); } @@ -935,8 +936,8 @@ inline void BaseDirPair::flip() { HierarchyObject::flip(); - std::swap(baseDirPfL, baseDirPfR); std::swap(dirExistsLeft_, dirExistsRight_); + std::swap(abfLeft, abfRight); } @@ -1023,10 +1024,9 @@ std::uint64_t FilePair::getFileSize() const template <SelectedSide side> inline -FileId FilePair::getFileId() const +ABF::FileId FilePair::getFileId() const { - return FileId(SelectParam<side>::ref(dataLeft, dataRight).devId, - SelectParam<side>::ref(dataLeft, dataRight).fileIdx); + return SelectParam<side>::ref(dataLeft, dataRight).fileId; } @@ -1042,8 +1042,8 @@ void FilePair::setSyncedTo(const Zstring& shortName, std::uint64_t fileSize, std::int64_t lastWriteTimeTrg, std::int64_t lastWriteTimeSrc, - const FileId& fileIdTrg, - const FileId& fileIdSrc, + const ABF::FileId& fileIdTrg, + const ABF::FileId& fileIdSrc, bool isSymlinkTrg, bool isSymlinkSrc) { @@ -1115,4 +1115,4 @@ void SymlinkPair::removeObjectR() } } -#endif // FILEHIERARCHY_H_INCLUDED +#endif //FILEHIERARCHY_H_257235289645296 diff --git a/FreeFileSync/Source/fs/abstract.cpp b/FreeFileSync/Source/fs/abstract.cpp new file mode 100644 index 00000000..ee1c0f8c --- /dev/null +++ b/FreeFileSync/Source/fs/abstract.cpp @@ -0,0 +1,138 @@ +// ************************************************************************** +// * 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 "abstract.h" +//#include <zen/string_tools.h> + +using namespace zen; +using ABF = AbstractBaseFolder; + + +ABF::FileAttribAfterCopy ABF::copyFileAsStream(const AbstractPathRef& apSource, const AbstractPathRef& apTarget, //throw FileError, ErrorTargetExisting, ErrorFileLocked + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) +{ + auto streamIn = getInputStream(apSource); //throw FileError, ErrorFileLocked + if (onNotifyCopyStatus) onNotifyCopyStatus(0); //throw X! + + std::uint64_t bytesWritten = 0; + std::uint64_t fileSizeExpected = streamIn->getFileSize (); //throw FileError + const std::int64_t modificationTime = streamIn->getModificationTime(); //throw FileError + const FileId sourceFileId = streamIn->getFileId (); //throw FileError + FileId targetFileId; + + auto streamOut = getOutputStream(apTarget, &fileSizeExpected, &modificationTime); //throw FileError, ErrorTargetExisting + if (onNotifyCopyStatus) onNotifyCopyStatus(0); //throw X! + + std::vector<char> buffer(std::min(streamIn ->optimalBlockSize(), + streamOut->optimalBlockSize())); + for (;;) + { + const size_t bytesRead = streamIn->read(&buffer[0], buffer.size()); //throw FileError + assert(bytesRead <= buffer.size()); + + streamOut->write(&buffer[0], bytesRead); //throw FileError + bytesWritten += bytesRead; + + if (onNotifyCopyStatus) + onNotifyCopyStatus(bytesRead); //throw X! + + if (bytesRead != buffer.size()) //end of file + break; + } + + if (bytesWritten != fileSizeExpected) + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(ABF::getDisplayPath(apSource))), + replaceCpy(replaceCpy(_("Unexpected size of data stream.\nExpected: %x bytes\nActual: %y bytes"), L"%x", numberTo<std::wstring>(fileSizeExpected)), L"%y", numberTo<std::wstring>(bytesWritten))); + + //modification time should be set here! + targetFileId = streamOut->finalize([&] { if (onNotifyCopyStatus) onNotifyCopyStatus(0); /*throw X*/ }); //throw FileError + + ABF::FileAttribAfterCopy attr = {}; + attr.fileSize = bytesWritten; + attr.modificationTime = modificationTime; + attr.sourceFileId = sourceFileId; + attr.targetFileId = targetFileId; + + return attr; +} + + +const Zchar* ABF::TEMP_FILE_ENDING = Zstr(".ffs_tmp"); + + +ABF::FileAttribAfterCopy ABF::copyFileTransactional(const AbstractPathRef& apSource, const AbstractPathRef& apTarget, //throw FileError, ErrorFileLocked + bool copyFilePermissions, + bool transactionalCopy, + const std::function<void()>& onDeleteTargetFile, + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) +{ + auto copyFileBestEffort = [&](const AbstractPathRef& apTargetTmp) + { + //caveat: typeid returns static type for pointers, dynamic type for references!!! + if (typeid(*apSource.abf) == typeid(*apTarget.abf)) + return apSource.abf->copyFileForSameAbfType(apSource.itemPathImpl, apTargetTmp, copyFilePermissions, onNotifyCopyStatus); //throw FileError, ErrorTargetExisting, ErrorFileLocked + + //fall back to stream-based file copy: + if (copyFilePermissions) + throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(getDisplayPath(apTargetTmp))), + _("Operation not supported for different base folder types.")); + + return copyFileAsStream(apSource, apTargetTmp, onNotifyCopyStatus); //throw FileError, ErrorTargetExisting, ErrorFileLocked + }; + + if (transactionalCopy) + { + AbstractPathRef apTargetTmp(*apTarget.abf, apTarget.itemPathImpl + TEMP_FILE_ENDING); + ABF::FileAttribAfterCopy attr = {}; + + for (int i = 0;; ++i) + try + { + attr = copyFileBestEffort(apTargetTmp); //throw FileError, ErrorTargetExisting, ErrorFileLocked + break; + } + catch (const ErrorTargetExisting&) //optimistic strategy: assume everything goes well, but recover on error -> minimize file accesses + { + if (i == 10) throw; //avoid endless recursion in pathological cases, e.g. https://sourceforge.net/p/freefilesync/discussion/open-discussion/thread/36adac33 + apTargetTmp.itemPathImpl = apTarget.itemPathImpl + Zchar('_') + numberTo<Zstring>(i) + TEMP_FILE_ENDING; + } + + //transactional behavior: ensure cleanup; not needed before copyFileBestEffort() which is already transactional + zen::ScopeGuard guardTempFile = zen::makeGuard([&] { try { removeFile(apTargetTmp); } catch (FileError&) {} }); + + //have target file deleted (after read access on source and target has been confirmed) => allow for almost transactional overwrite + if (onDeleteTargetFile) + onDeleteTargetFile(); //throw X + + //perf: this call is REALLY expensive on unbuffered volumes! ~40% performance decrease on FAT USB stick! + renameItem(apTargetTmp, apTarget); //throw FileError + + /* + CAVEAT on FAT/FAT32: the sequence of deleting the target file and renaming "file.txt.ffs_tmp" to "file.txt" does + NOT PRESERVE the creation time of the .ffs_tmp file, but SILENTLY "reuses" whatever creation time the old "file.txt" had! + This "feature" is called "File System Tunneling": + http://blogs.msdn.com/b/oldnewthing/archive/2005/07/15/439261.aspx + http://support.microsoft.com/kb/172190/en-us + */ + + guardTempFile.dismiss(); + return attr; + } + else + { + /* + Note: non-transactional file copy solves at least four problems: + -> skydrive - doesn't allow for .ffs_tmp extension and returns ERROR_INVALID_PARAMETER + -> network renaming issues + -> allow for true delete before copy to handle low disk space problems + -> higher performance on non-buffered drives (e.g. usb sticks) + */ + if (onDeleteTargetFile) + onDeleteTargetFile(); + + return copyFileBestEffort(apTarget); //throw FileError, ErrorTargetExisting, ErrorFileLocked + } +} diff --git a/FreeFileSync/Source/fs/abstract.h b/FreeFileSync/Source/fs/abstract.h new file mode 100644 index 00000000..6c2c6c7f --- /dev/null +++ b/FreeFileSync/Source/fs/abstract.h @@ -0,0 +1,548 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ABSTRACT_FS_873450978453042524534234 +#define ABSTRACT_FS_873450978453042524534234 + +#include <functional> +#include <zen/file_error.h> +#include <zen/zstring.h> +#include <zen/optional.h> +#include "../lib/icon_holder.h" + +namespace zen +{ +struct AbstractBaseFolder; + + +struct AbstractPathRef +{ + struct ItemId; + ItemId getUniqueId() const; //non-referencing identifier value + +private: + AbstractPathRef(const AbstractBaseFolder& abfIn, const Zstring& itemPathImplIn) : abf(&abfIn), itemPathImpl(itemPathImplIn) {} + friend struct AbstractBaseFolder; + + const AbstractBaseFolder* abf; //always bound + Zstring itemPathImpl; //valid only in context of a specific AbstractBaseFolder *instance*, e.g. FTP session! + //referenced item needs not exist; no file I/O!!! +}; + +//============================================================================================================== +//============================================================================================================== + +struct AbstractBaseFolder +{ + static Zstring getDisplayPath(const AbstractPathRef& ap) { return ap.abf->getDisplayPath(ap.itemPathImpl); } + + static Zstring getFileShortName(const AbstractPathRef& ap) { return ap.abf->getFileShortName(ap.itemPathImpl); } + + struct LessItemPath; + static bool equalItemPath(const AbstractPathRef& lhs, const AbstractPathRef& rhs); + + static bool havePathDependency(const AbstractBaseFolder& lhs, const AbstractBaseFolder& rhs); + + static AbstractPathRef appendRelPath(const AbstractPathRef& ap, const Zstring& relPath) { return AbstractPathRef(*ap.abf, ap.abf->appendRelPathToItemPathImpl(ap.itemPathImpl, relPath)); } + + static Opt<Zstring> getNativeItemPath(const AbstractPathRef& ap) { return ap.abf->isNativeFileSystem() ? Opt<Zstring>(ap.itemPathImpl) : NoValue(); } + + //---------------------------------------------------------------------------------------------------------------- + static bool fileExists (const AbstractPathRef& ap) { return ap.abf->fileExists (ap.itemPathImpl); } //noexcept; check whether file or file-symlink exists + static bool dirExists (const AbstractPathRef& ap) { return ap.abf->dirExists (ap.itemPathImpl); } //noexcept; check whether directory or dir-symlink exists + static bool symlinkExists (const AbstractPathRef& ap) { return ap.abf->symlinkExists (ap.itemPathImpl); } //noexcept; check whether a symbolic link exists + static bool somethingExists(const AbstractPathRef& ap) { return ap.abf->somethingExists(ap.itemPathImpl); } //noexcept; check whether any object with this name exists + //---------------------------------------------------------------------------------------------------------------- + + //if parent directory is not existing: create recursively + //should provide for single ATOMIC folder creation! + static void createNewFolder(const AbstractPathRef& ap) { return ap.abf->createNewFolder(ap.itemPathImpl); } //throw FileError, ErrorTargetExisting + + static bool removeFile(const AbstractPathRef& ap) { return ap.abf->removeFile(ap.itemPathImpl); }; //throw FileError; return "false" if file is not existing + + static void removeFolder(const AbstractPathRef& ap, //throw FileError + const std::function<void (const Zstring& displayPath)>& onBeforeFileDeletion = nullptr, //optional; + const std::function<void (const Zstring& displayPath)>& onBeforeDirDeletion = nullptr) //one call for each *existing* object! + { return ap.abf->removeFolder(ap.itemPathImpl, onBeforeFileDeletion, onBeforeDirDeletion); }; + + //---------------------------------------------------------------------------------------------------------------- + + enum class SymlinkHandling + { + DIRECT, + FOLLOW + }; + + static void setFileTime(const AbstractPathRef& ap, std::int64_t modificationTime, SymlinkHandling procSl) { return ap.abf->setFileTime(ap.itemPathImpl, modificationTime, procSl); } //throw FileError + + static AbstractPathRef getResolvedSymlinkPath(const AbstractPathRef& ap) { return ap.abf->getResolvedSymlinkPath(ap.itemPathImpl); } //throw FileError + + static Zstring getSymlinkContentBuffer(const AbstractPathRef& ap) { return ap.abf->getSymlinkContentBuffer(ap.itemPathImpl); } //throw FileError + + //---------------------------------------------------------------------------------------------------------------- + struct IconLoader + { + std::function<ImageHolder(int pixelSize)> getThumbnailImage; //optional, noexcept! + std::function<ImageHolder(int pixelSize)> getFileIcon; // + }; + + //Async operations: retrieve function objects for asynchronous loading + //- THREAD-SAFETY: must be thread-safe like an int! => no dangling references to this instance! + static IconLoader getAsyncIconLoader(const AbstractPathRef& ap) { return ap.abf->getAsyncIconLoader(ap.itemPathImpl); } //noexcept! + virtual std::function<void()> /*throw FileError*/ getAsyncConnectFolder(bool allowUserInteraction) const = 0; //noexcept, optional return value + static std::function<bool()> /*noexcept*/ getAsyncCheckDirExists(const AbstractPathRef& ap) { return ap.abf->getAsyncCheckDirExists(ap.itemPathImpl); } //noexcept + //---------------------------------------------------------------------------------------------------------------- + + using FileId = Zbase<char>; + + //---------------------------------------------------------------------------------------------------------------- + struct InputStream + { + virtual ~InputStream() {} + virtual size_t read(void* buffer, size_t bytesToRead) = 0; //throw FileError; returns "bytesToRead", unless end of file! + virtual FileId getFileId () = 0; //throw FileError + virtual std::int64_t getModificationTime() = 0; //throw FileError + virtual std::uint64_t getFileSize () = 0; //throw FileError + virtual size_t optimalBlockSize() const = 0; //non-zero block size is ABF contract! it's implementers job to always give a reasonable buffer size! + protected: + InputStream() {} + private: + InputStream (InputStream&) = delete; + InputStream& operator=(InputStream&) = delete; + }; + + struct OutputStream //TRANSACTIONAL output stream! => call finalize when done! + { + virtual ~OutputStream(); + void write(const void* buffer, size_t bytesToWrite); //throw FileError + FileId finalize(const std::function<void()>& onUpdateStatus); //throw FileError + virtual size_t optimalBlockSize() const = 0; //non-zero block size is ABF contract! + + protected: + OutputStream(const AbstractPathRef& filePath, const std::uint64_t* streamSize); + bool finalizeHasSucceeded() const { return finalizeSucceeded; } + private: + virtual void writeImpl (const void* buffer, size_t bytesToWrite ) = 0; //throw FileError + virtual FileId finalizeImpl(const std::function<void()>& onUpdateStatus) = 0; //throw FileError + + OutputStream (OutputStream&) = delete; + OutputStream& operator=(OutputStream&) = delete; + + const AbstractPathRef filePath_; + bool finalizeSucceeded = false; + Opt<std::uint64_t> bytesExpected; + std::uint64_t bytesWritten = 0; + }; + + //return value always bound: + static std::unique_ptr<InputStream > getInputStream (const AbstractPathRef& ap) { return ap.abf->getInputStream (ap.itemPathImpl); } //throw FileError, ErrorFileLocked + static std::unique_ptr<OutputStream> getOutputStream(const AbstractPathRef& ap, + const std::uint64_t* streamSize, //optional + const std::int64_t* modificationTime) // + { return ap.abf->getOutputStream(ap.itemPathImpl, streamSize, modificationTime); } //throw FileError, ErrorTargetExisting + //---------------------------------------------------------------------------------------------------------------- + + struct TraverserCallback + { + virtual ~TraverserCallback() {} + + struct SymlinkInfo + { + const Zchar* shortName; + std::int64_t lastWriteTime; //number of seconds since Jan. 1st 1970 UTC + }; + + struct FileInfo + { + const Zchar* shortName; + std::uint64_t fileSize; //unit: bytes! + std::int64_t lastWriteTime; //number of seconds since Jan. 1st 1970 UTC + const FileId& id; //optional: empty if not supported! + const SymlinkInfo* symlinkInfo; //only filled if file is a followed symlink + }; + + struct DirInfo + { + const Zchar* shortName; + }; + + enum HandleLink + { + LINK_FOLLOW, //dereferences link, then calls "onDir()" or "onFile()" + LINK_SKIP + }; + + enum HandleError + { + ON_ERROR_RETRY, + ON_ERROR_IGNORE + }; + + virtual void onFile (const FileInfo& fi) = 0; + virtual TraverserCallback* onDir (const DirInfo& di) = 0; + virtual HandleLink onSymlink(const SymlinkInfo& si) = 0; + //nullptr: ignore directory, non-nullptr: traverse into using the (new) callback => implement releaseDirTraverser() if necessary! + virtual void releaseDirTraverser(TraverserCallback* trav) {} + + virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) = 0; //failed directory traversal -> consider directory data at current level as incomplete! + virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) = 0; //failed to get data for single file/dir/symlink only! + }; + + //- THREAD-SAFETY: expect accesses from multiple threads to the same instance => synchronize internally!!! + //- client needs to handle duplicate file reports! (FilePlusTraverser fallback, retrying to read directory contents, ...) + static void traverseFolder(const AbstractPathRef& ap, TraverserCallback& sink) { ap.abf->traverseFolder(ap.itemPathImpl, sink); } + //---------------------------------------------------------------------------------------------------------------- + + static bool supportPermissionCopy(const AbstractBaseFolder& baseFolderLeft, const AbstractBaseFolder& baseFolderRight); //throw FileError + + struct FileAttribAfterCopy + { + std::uint64_t fileSize; + std::int64_t modificationTime; //time_t UTC compatible + FileId sourceFileId; + FileId targetFileId; + }; + //return current attributes at the time of copy + //symlink handling: dereference source + static FileAttribAfterCopy copyFileAsStream(const AbstractPathRef& apSource, const AbstractPathRef& apTarget, //throw FileError, ErrorTargetExisting, ErrorFileLocked + //accummulated delta != file size! consider ADS, sparse, compressed files + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); //may be nullptr; throw X! + + //Note: it MAY happen that copyFileTransactional() leaves temp files behind, e.g. temporary network drop. + // => clean them up at an appropriate time (automatically set sync directions to delete them). They have the following ending: + static const Zchar* TEMP_FILE_ENDING; //don't use Zstring as global constant: avoid static initialization order problem in global namespace! + + static FileAttribAfterCopy copyFileTransactional(const AbstractPathRef& apSource, const AbstractPathRef& apTarget, //throw FileError, ErrorFileLocked + bool copyFilePermissions, + bool transactionalCopy, + //if target is existing user needs to implement deletion: copyFile() NEVER overwrites target if already existing! + //if transactionalCopy == true, full read access on source had been proven at this point, so it's safe to delete it. + const std::function<void()>& onDeleteTargetFile, + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); + + static void copyNewFolder(const AbstractPathRef& apSource, const AbstractPathRef& apTarget, bool copyFilePermissions); //throw FileError + static void copySymlink (const AbstractPathRef& apSource, const AbstractPathRef& apTarget, bool copyFilePermissions); //throw FileError + static void renameItem (const AbstractPathRef& apSource, const AbstractPathRef& apTarget); //throw FileError, ErrorTargetExisting, ErrorDifferentVolume + + //---------------------------------------------------------------------------------------------------------------- + //---------------------------------------------------------------------------------------------------------------- + + virtual ~AbstractBaseFolder() {} + + //- THREAD-SAFETY: expect accesses from multiple threads to the same instance => synchronize internally!!! + AbstractPathRef getAbstractPath(const Zstring& relPath) const { return AbstractPathRef(*this, appendRelPathToItemPathImpl(getBasePathImpl(), relPath)); } + AbstractPathRef getAbstractPath() const { return AbstractPathRef(*this, getBasePathImpl()); } + + //limitation: zen::Opt requires default-constructibility => we need to use std::unique_ptr: + std::unique_ptr<AbstractPathRef> getAbstractPathFromNativePath(const Zstring& nativePath) const { return isNativeFileSystem() ? std::unique_ptr<AbstractPathRef>(new AbstractPathRef(*this, nativePath)) : nullptr; } + + virtual bool emptyBaseFolderPath() const = 0; + + virtual std::uint64_t getFreeDiskSpace() const = 0; //throw FileError, returns 0 if not available + + //---------------------------------------------------------------------------------------------------------------- + virtual bool supportsRecycleBin(const std::function<void ()>& onUpdateGui) const = 0; //throw FileError + + struct RecycleSession + { + virtual ~RecycleSession() {}; + virtual bool recycleItem(const AbstractPathRef& ap, const Zstring& logicalRelPath) = 0; //throw FileError; return true if item existed + virtual void tryCleanup(const std::function<void (const Zstring& displayPath)>& notifyDeletionStatus /*optional; currentItem may be empty*/) = 0; //throw FileError + }; + virtual std::unique_ptr<RecycleSession> createRecyclerSession() const = 0; //throw FileError, precondition: supportsRecycleBin(); return value must be bound! + + static void recycleItemDirectly(const AbstractPathRef& ap) { ap.abf->recycleItemDirectly(ap.itemPathImpl); } //throw FileError + + //================================================================================================================ + //================================================================================================================ + + //no need to protect access: + static const AbstractBaseFolder& getAbf(const AbstractPathRef& ap) { return *ap.abf; } + + static Zstring appendPaths(const Zstring& basePath, const Zstring& relPath); + +protected: //grant derived classes access to AbstractPathRef: + static Zstring getItemPathImpl(const AbstractPathRef& ap) { return ap.itemPathImpl; } + static AbstractPathRef makeAbstractItem(const AbstractBaseFolder& abfIn, const Zstring& itemPathImplIn) { return AbstractPathRef(abfIn, itemPathImplIn); } + +private: + virtual bool isNativeFileSystem() const { return false; }; + + //- THREAD-SAFETY: expect accesses from multiple threads to the same instance => synchronize internally!!! + virtual Zstring getDisplayPath(const Zstring& itemPathImpl) const = 0; + + //- THREAD-SAFETY: expect accesses from multiple threads to the same instance => synchronize internally!!! + virtual Zstring appendRelPathToItemPathImpl(const Zstring& itemPathImpl, const Zstring& relPath) const = 0; + + //- THREAD-SAFETY: expect accesses from multiple threads to the same instance => synchronize internally!!! + virtual Zstring getBasePathImpl() const = 0; + + virtual Zstring getFileShortName(const Zstring& itemPathImpl) const = 0; + + virtual bool lessItemPathSameAbfType(const Zstring& itemPathImplLhs, const AbstractPathRef& apRhs) const = 0; + + virtual bool havePathDependencySameAbfType(const AbstractBaseFolder& other) const = 0; + + //---------------------------------------------------------------------------------------------------------------- + virtual bool fileExists (const Zstring& itemPathImpl) const = 0; //noexcept + virtual bool dirExists (const Zstring& itemPathImpl) const = 0; //noexcept + virtual bool symlinkExists (const Zstring& itemPathImpl) const = 0; //noexcept + virtual bool somethingExists(const Zstring& itemPathImpl) const = 0; //noexcept + //---------------------------------------------------------------------------------------------------------------- + //should provide for single ATOMIC folder creation! creates recursively! + virtual void createNewFolder(const Zstring& itemPathImpl) const = 0; //throw FileError, ErrorTargetExisting + + virtual bool removeFile(const Zstring& itemPathImpl) const = 0; //throw FileError + + virtual void removeFolder(const Zstring& itemPathImpl, //throw FileError + const std::function<void (const Zstring& displayPath)>& onBeforeFileDeletion, //optional; + const std::function<void (const Zstring& displayPath)>& onBeforeDirDeletion) const = 0; + //---------------------------------------------------------------------------------------------------------------- + virtual void setFileTime(const Zstring& itemPathImpl, std::int64_t modificationTime, SymlinkHandling procSl) const = 0; //throw FileError + + virtual AbstractPathRef getResolvedSymlinkPath(const Zstring& itemPathImpl) const = 0; //throw FileError + + virtual Zstring getSymlinkContentBuffer(const Zstring& itemPathImpl) const = 0; //throw FileError + + virtual void recycleItemDirectly(const Zstring& itemPathImpl) const = 0; //throw FileError + + //---------------------------------------------------------------------------------------------------------------- + //- THREAD-SAFETY: must be thread-safe like an int! => no dangling references to this instance! + virtual IconLoader getAsyncIconLoader(const Zstring& itemPathImpl) const = 0; //noexcept! + virtual std::function<bool()> /*noexcept*/ getAsyncCheckDirExists(const Zstring& itemPathImpl) const = 0; //noexcept + //---------------------------------------------------------------------------------------------------------------- + virtual std::unique_ptr<InputStream > getInputStream (const Zstring& itemPathImpl) const = 0; //throw FileError, ErrorFileLocked + virtual std::unique_ptr<OutputStream> getOutputStream(const Zstring& itemPathImpl, //throw FileError, ErrorTargetExisting + const std::uint64_t* streamSize, //optional + const std::int64_t* modificationTime) const = 0; // + //---------------------------------------------------------------------------------------------------------------- + virtual void traverseFolder(const Zstring& itemPathImpl, TraverserCallback& sink) const = 0; + //---------------------------------------------------------------------------------------------------------------- + + //symlink handling: follow link! + virtual FileAttribAfterCopy copyFileForSameAbfType(const Zstring& itemPathImplSource, const AbstractPathRef& apTarget, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked + //accummulated delta != file size! consider ADS, sparse, compressed files + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) const = 0; //may be nullptr; throw X! + + //symlink handling: follow link! + virtual void copyNewFolderForSameAbfType(const Zstring& itemPathImplSource, const AbstractPathRef& apTarget, bool copyFilePermissions) const = 0; //throw FileError + virtual void copySymlinkForSameAbfType (const Zstring& itemPathImplSource, const AbstractPathRef& apTarget, bool copyFilePermissions) const = 0; //throw FileError + virtual void renameItemForSameAbfType (const Zstring& itemPathImplSource, const AbstractPathRef& apTarget) const = 0; //throw FileError, ErrorTargetExisting, ErrorDifferentVolume + virtual bool supportsPermissions() const = 0; //throw FileError +}; + + +//implement "retry" in a generic way: +template <class Command> inline //function object expecting to throw FileError if operation fails +bool tryReportingDirError(Command cmd, AbstractBaseFolder::TraverserCallback& callback) //return "true" on success, "false" if error was ignored +{ + for (size_t retryNumber = 0;; ++retryNumber) + try + { + cmd(); //throw FileError + return true; + } + catch (const FileError& e) + { + switch (callback.reportDirError(e.toString(), retryNumber)) + { + case AbstractBaseFolder::TraverserCallback::ON_ERROR_RETRY: + break; + case AbstractBaseFolder::TraverserCallback::ON_ERROR_IGNORE: + return false; + } + } +} + + +template <class Command> inline //function object expecting to throw FileError if operation fails +bool tryReportingItemError(Command cmd, AbstractBaseFolder::TraverserCallback& callback, const Zchar* shortName) //return "true" on success, "false" if error was ignored +{ + for (size_t retryNumber = 0;; ++retryNumber) + try + { + cmd(); //throw FileError + return true; + } + catch (const FileError& e) + { + switch (callback.reportItemError(e.toString(), retryNumber, shortName)) + { + case AbstractBaseFolder::TraverserCallback::ON_ERROR_RETRY: + break; + case AbstractBaseFolder::TraverserCallback::ON_ERROR_IGNORE: + return false; + } + } +} + + + + + + + + +//------------------------------------ implementation ----------------------------------------- +struct AbstractPathRef::ItemId +{ + ItemId(const AbstractBaseFolder* abfIn, const Zstring& itemPathImplIn) : abf(abfIn), itemPathImpl(itemPathImplIn) {} + + inline friend bool operator<(const ItemId& lhs, const ItemId& rhs) { return lhs.abf != rhs.abf ? lhs.abf < rhs.abf : lhs.itemPathImpl < rhs.itemPathImpl; } + +private: + const void* abf; + Zstring itemPathImpl; +}; + +inline +AbstractPathRef::ItemId AbstractPathRef::getUniqueId() const { return ItemId(abf, itemPathImpl); } + + +struct AbstractBaseFolder::LessItemPath +{ + bool operator()(const AbstractPathRef& lhs, const AbstractPathRef& rhs) const + { + //note: in worst case, order is guaranteed to be stable only during each program run + return typeid(*lhs.abf) != typeid(*rhs.abf) ? typeid(*lhs.abf).before(typeid(*rhs.abf)) : lhs.abf->lessItemPathSameAbfType(lhs.itemPathImpl, rhs); + //caveat: typeid returns static type for pointers, dynamic type for references!!! + } + bool operator()(const AbstractBaseFolder* lhs, const AbstractBaseFolder* rhs) const + { + return (*this)(lhs->getAbstractPath(), rhs->getAbstractPath()); + } + //bool operator()(const std::shared_ptr<AbstractBaseFolder>& lhs, const std::shared_ptr<AbstractBaseFolder>& rhs) const + //-> avoid overload ambiguity with "const ABF*" which may erroneously convert to std::shared_ptr!!! +}; + + +inline +bool AbstractBaseFolder::equalItemPath(const AbstractPathRef& lhs, const AbstractPathRef& rhs) +{ + return !LessItemPath()(lhs, rhs) && !LessItemPath()(rhs, lhs); +} + + +inline +bool AbstractBaseFolder::havePathDependency(const AbstractBaseFolder& lhs, const AbstractBaseFolder& rhs) +{ + return typeid(lhs) != typeid(rhs) ? false : lhs.havePathDependencySameAbfType(rhs); +}; + + +inline +Zstring AbstractBaseFolder::appendPaths(const Zstring& basePath, const Zstring& relPath) +{ + if (relPath.empty()) + return basePath; + if (basePath.empty()) + return relPath; + + if (!startsWith(relPath, FILE_NAME_SEPARATOR)) + return appendSeparator(basePath) + relPath; + + assert(false); + if (relPath.size() == 1) + return basePath; + + if (endsWith(basePath, FILE_NAME_SEPARATOR)) + return Zstring(basePath.begin(), basePath.end() - 1) + relPath; + + return basePath + relPath; +} + +//-------------------------------------------------------------------------- + +inline +AbstractBaseFolder::OutputStream::OutputStream(const AbstractPathRef& filePath, const std::uint64_t* streamSize) : filePath_(filePath) +{ + if (streamSize) + bytesExpected = *streamSize; +} + + +inline +AbstractBaseFolder::OutputStream::~OutputStream() +{ + if (!finalizeSucceeded) //transactional output stream! => clean up! + try { removeFile(filePath_); /*throw FileError*/ } + catch (FileError&) { assert(false); } +} + + +inline +void AbstractBaseFolder::OutputStream::write(const void* buffer, size_t bytesToWrite) +{ + bytesWritten += bytesToWrite; + writeImpl(buffer, bytesToWrite); //throw FileError +} + + +inline +AbstractBaseFolder::FileId AbstractBaseFolder::OutputStream::finalize(const std::function<void()>& onUpdateStatus) //throw FileError +{ + if (bytesExpected && bytesWritten != *bytesExpected) + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getDisplayPath(filePath_))), + replaceCpy(replaceCpy(_("Unexpected size of data stream.\nExpected: %x bytes\nActual: %y bytes"), L"%x", numberTo<std::wstring>(*bytesExpected)), L"%y", numberTo<std::wstring>(bytesWritten))); + + const FileId fileId = finalizeImpl(onUpdateStatus); //throw FileError + + finalizeSucceeded = true; + return fileId; +} + +//-------------------------------------------------------------------------- + +inline +void AbstractBaseFolder::copyNewFolder(const AbstractPathRef& apSource, const AbstractPathRef& apTarget, bool copyFilePermissions) //throw FileError +{ + if (typeid(*apSource.abf) == typeid(*apTarget.abf)) + return apSource.abf->copyNewFolderForSameAbfType(apSource.itemPathImpl, apTarget, copyFilePermissions); //throw FileError + + //fall back: + if (copyFilePermissions) + throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(getDisplayPath(apTarget))), + _("Operation not supported for different base folder types.")); + + createNewFolder(apTarget); //throw FileError, ErrorTargetExisting +} + + +inline +void AbstractBaseFolder::copySymlink(const AbstractPathRef& apSource, const AbstractPathRef& apTarget, bool copyFilePermissions) //throw FileError +{ + if (typeid(*apSource.abf) == typeid(*apTarget.abf)) + return apSource.abf->copySymlinkForSameAbfType(apSource.itemPathImpl, apTarget, copyFilePermissions); //throw FileError + + throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", + L"\n" + fmtFileName(getDisplayPath(apSource))), L"%y", + L"\n" + fmtFileName(getDisplayPath(apTarget))), _("Operation not supported for different base folder types.")); +} + + +inline +void AbstractBaseFolder::renameItem(const AbstractPathRef& apSource, const AbstractPathRef& apTarget) //throw FileError, ErrorTargetExisting, ErrorDifferentVolume +{ + if (typeid(*apSource.abf) == typeid(*apTarget.abf)) + return apSource.abf->renameItemForSameAbfType(apSource.itemPathImpl, apTarget); //throw FileError, ErrorTargetExisting, ErrorDifferentVolume + + throw ErrorDifferentVolume(replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", + L"\n" + fmtFileName(getDisplayPath(apSource))), L"%y", + L"\n" + fmtFileName(getDisplayPath(apTarget))), _("Operation not supported for different base folder types.")); +} + + +inline +bool AbstractBaseFolder::supportPermissionCopy(const AbstractBaseFolder& baseFolderLeft, const AbstractBaseFolder& baseFolderRight) //throw FileError +{ + if (typeid(baseFolderLeft) == typeid(baseFolderRight)) + return baseFolderLeft .supportsPermissions() && //throw FileError + baseFolderRight.supportsPermissions(); + return false; +} +} + +#endif //ABSTRACT_FS_873450978453042524534234 diff --git a/FreeFileSync/Source/fs/concrete.cpp b/FreeFileSync/Source/fs/concrete.cpp new file mode 100644 index 00000000..b22abbda --- /dev/null +++ b/FreeFileSync/Source/fs/concrete.cpp @@ -0,0 +1,47 @@ +// ************************************************************************** +// * 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 "concrete.h" +#include "native.h" +#ifdef _MSC_VER + #include "mtp.h" +#endif + +using namespace zen; + + +std::unique_ptr<AbstractBaseFolder> zen::createAbstractBaseFolder(const Zstring& folderPathPhrase) //noexcept +{ + //greedy: try native evaluation first + if (acceptsFolderPathPhraseNative(folderPathPhrase)) //noexcept + return createBaseFolderNative(folderPathPhrase); //noexcept + + //then the rest: +#ifdef _MSC_VER + if (acceptsFolderPathPhraseMtp(folderPathPhrase)) //noexcept + return createBaseFolderMtp(folderPathPhrase); //noexcept +#endif + + //no idea? => native! + return createBaseFolderNative(folderPathPhrase); +} + + +Zstring zen::getResolvedDisplayPath(const Zstring& folderPathPhrase) //noexcept +{ + //greedy: try native evaluation first + if (acceptsFolderPathPhraseNative(folderPathPhrase)) //noexcept + return getResolvedDisplayPathNative(folderPathPhrase); //noexcept + + //then the rest: +#ifdef _MSC_VER + if (acceptsFolderPathPhraseMtp(folderPathPhrase)) //noexcept + return getResolvedDisplayPathMtp(folderPathPhrase); //noexcept +#endif + + //no idea? => native! + return getResolvedDisplayPathNative(folderPathPhrase); +} diff --git a/FreeFileSync/Source/fs/concrete.h b/FreeFileSync/Source/fs/concrete.h new file mode 100644 index 00000000..dedbcf56 --- /dev/null +++ b/FreeFileSync/Source/fs/concrete.h @@ -0,0 +1,19 @@ +// ************************************************************************** +// * 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 FS_CONCRETE_348787329573243 +#define FS_CONCRETE_348787329573243 + +#include "abstract.h" + +namespace zen +{ +std::unique_ptr<AbstractBaseFolder> createAbstractBaseFolder(const Zstring& folderPathPhrase); //noexcept + +Zstring getResolvedDisplayPath(const Zstring& folderPathPhrase); //noexcept +} + +#endif //FS_CONCRETE_348787329573243 diff --git a/FreeFileSync/Source/fs/native.cpp b/FreeFileSync/Source/fs/native.cpp new file mode 100644 index 00000000..0eac2946 --- /dev/null +++ b/FreeFileSync/Source/fs/native.cpp @@ -0,0 +1,530 @@ +// ************************************************************************** +// * 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 "native.h" +#include <zen/file_access.h> +#include <zen/symlink_target.h> +#include <zen/file_io.h> +#include <zen/file_id_def.h> +#include <zen/int64.h> +#include <zen/stl_tools.h> +#include <zen/recycler.h> +#include "../lib/resolve_path.h" +#include "../lib/icon_loader.h" +#include "native_traverser_impl.h" + +#if defined ZEN_LINUX || defined ZEN_MAC + #include <fcntl.h> //posix_fallocate, fcntl +#endif + + +namespace +{ +class RecycleSessionNative : public AbstractBaseFolder::RecycleSession +{ +public: + RecycleSessionNative(const Zstring baseDirPathPf) : baseDirPathPf_(baseDirPathPf) {} + + bool recycleItem(const AbstractPathRef& ap, const Zstring& logicalRelPath) override; //throw FileError + void tryCleanup(const std::function<void (const Zstring& displayPath)>& notifyDeletionStatus) override; //throw FileError + +private: +#ifdef ZEN_WIN + Zstring getOrCreateRecyclerTempDirPf(); //throw FileError + + std::vector<Zstring> toBeRecycled; //full path of files located in temporary folder, waiting for batch-recycling + Zstring recyclerTmpDir; //temporary folder holding files/folders for *deferred* recycling +#endif + + const Zstring baseDirPathPf_; //ends with path separator +}; + +//=========================================================================================================================== + +template <class Function> inline +void evalAttributeByHandle(FileHandle fh, const Zstring& filePath, Function evalFileInfo) //throw FileError +{ +#ifdef ZEN_WIN + BY_HANDLE_FILE_INFORMATION fileInfo = {}; + if (!::GetFileInformationByHandle(fh, &fileInfo)) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filePath)), L"GetFileInformationByHandle", getLastError()); + +#elif defined ZEN_LINUX || defined ZEN_MAC + struct ::stat fileInfo = {}; + if (::fstat(fh, &fileInfo) != 0) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filePath)), L"fstat", getLastError()); +#endif + evalFileInfo(fileInfo); +} + + +inline +ABF::FileId getFileId(FileHandle fh, const Zstring& filePath) //throw FileError +{ + zen::FileId fid; +#ifdef ZEN_WIN + evalAttributeByHandle(fh, filePath, [&](const BY_HANDLE_FILE_INFORMATION& fileInfo) { fid = extractFileId(fileInfo); }); //throw FileError +#elif defined ZEN_LINUX || defined ZEN_MAC + evalAttributeByHandle(fh, filePath, [&](const struct ::stat& fileInfo) { fid = extractFileId(fileInfo); }); //throw FileError +#endif + return convertToAbstractFileId(fid); +} + + +inline +std::int64_t getModificationTime(FileHandle fh, const Zstring& filePath) //throw FileError +{ + std::int64_t modTime = 0; +#ifdef ZEN_WIN + evalAttributeByHandle(fh, filePath, [&](const BY_HANDLE_FILE_INFORMATION& fileInfo) { modTime = filetimeToTimeT(fileInfo.ftLastWriteTime); }); //throw FileError +#elif defined ZEN_LINUX || defined ZEN_MAC + evalAttributeByHandle(fh, filePath, [&](const struct ::stat& fileInfo) { modTime = fileInfo.st_mtime; }); //throw FileError +#endif + return modTime; +} + + +inline +std::uint64_t getFileSize(FileHandle fh, const Zstring& filePath) //throw FileError +{ + std::uint64_t fileSize = 0; +#ifdef ZEN_WIN + evalAttributeByHandle(fh, filePath, [&](const BY_HANDLE_FILE_INFORMATION& fileInfo) { fileSize = get64BitUInt(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); }); //throw FileError +#elif defined ZEN_LINUX || defined ZEN_MAC + evalAttributeByHandle(fh, filePath, [&](const struct ::stat& fileInfo) { fileSize = makeUnsigned(fileInfo.st_size); }); //throw FileError +#endif + return fileSize; +} + + +void preAllocateSpace(FileHandle fh, const std::uint64_t streamSize, const Zstring& displayPath) //throw FileError +{ +#ifdef ZEN_WIN + LARGE_INTEGER fileSize = {}; + fileSize.QuadPart = streamSize; + if (!::SetFilePointerEx(fh, //__in HANDLE hFile, + fileSize, //__in LARGE_INTEGER liDistanceToMove, + nullptr, //__out_opt PLARGE_INTEGER lpNewFilePointer, + FILE_BEGIN)) //__in DWORD dwMoveMethod + throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(displayPath)), L"SetFilePointerEx", getLastError()); + + if (!::SetEndOfFile(fh)) + throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(displayPath)), L"SetEndOfFile", getLastError()); + + if (!::SetFilePointerEx(fh, {}, nullptr, FILE_BEGIN)) + throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(displayPath)), L"SetFilePointerEx", getLastError()); + +#elif defined ZEN_LINUX + if (::posix_fallocate(fh, 0, streamSize) != 0) + throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(displayPath)), L"posix_fallocate", getLastError()); + +#elif defined ZEN_MAC + struct ::fstore store = {}; + store.fst_flags = F_ALLOCATECONTIG; + store.fst_posmode = F_PEOFPOSMODE; //allocate from physical end of file + //store.fst_offset -> start of the region + store.fst_length = streamSize; + //store.fst_bytesalloc -> out: number of bytes allocated + + if (::fcntl(fh, F_PREALLOCATE, &store) == -1) //fcntl needs not return 0 on success! + { + store.fst_flags = F_ALLOCATEALL; //retry, allowing non-contiguous storage + if (::fcntl(fh, F_PREALLOCATE, &store) == -1) + throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(displayPath)), L"fcntl, F_PREALLOCATE", getLastError()); + } +#endif +} + + +struct InputStreamNative : public AbstractBaseFolder::InputStream +{ + InputStreamNative(const Zstring& filePath) : fi(filePath) {} //throw FileError, ErrorFileLocked + + size_t read(void* buffer, size_t bytesToRead) override { return fi.read(buffer, bytesToRead); } //throw FileError; returns "bytesToRead", unless end of file! + ABF::FileId getFileId () override { return ::getFileId (fi.getHandle(), fi.getFilePath()); } //throw FileError + std::int64_t getModificationTime() override { return ::getModificationTime(fi.getHandle(), fi.getFilePath()); } //throw FileError + std::uint64_t getFileSize () override { return ::getFileSize (fi.getHandle(), fi.getFilePath()); } //throw FileError + size_t optimalBlockSize() const override { return fi.optimalBlockSize(); } //non-zero block size is ABF contract! + +private: + FileInput fi; +}; + + +struct OutputStreamNative : public AbstractBaseFolder::OutputStream +{ + OutputStreamNative(const Zstring& filePath, const AbstractPathRef& abstractFilePath, const std::uint64_t* streamSize, const std::int64_t* modTime) : + OutputStream(abstractFilePath, streamSize), + fo(filePath, FileOutput::ACC_CREATE_NEW) //throw FileError, ErrorTargetExisting + { + if (modTime) modTime_ = *modTime; + + if (streamSize) //pre-allocate file space, because we can + preAllocateSpace(fo.getHandle(), *streamSize, fo.getFilePath()); //throw FileError + } + + size_t optimalBlockSize() const override { return fo.optimalBlockSize(); } //non-zero block size is ABF contract! + +private: + void writeImpl(const void* buffer, size_t bytesToWrite) override { fo.write(buffer, bytesToWrite); } //throw FileError + + ABF::FileId finalizeImpl(const std::function<void()>& onUpdateStatus) override //throw FileError + { + const ABF::FileId fileId = getFileId(fo.getHandle(), fo.getFilePath()); //throw FileError + if (onUpdateStatus) onUpdateStatus(); //throw X! + + fo.close(); //throw FileError + if (onUpdateStatus) onUpdateStatus(); //throw X! + + try + { + if (modTime_) + zen::setFileTime(fo.getFilePath(), *modTime_, ProcSymlink::FOLLOW); //throw FileError + } + catch (FileError&) + { + throw; + //Failing to set modification time is not a serious problem from synchronization + //perspective (treated like external update), except for the inconvenience. + //Support additional scenarios like writing to FTP on Linux? Keep strict handling for now. + } + + return fileId; + } + + FileOutput fo; + Opt<std::int64_t> modTime_; +}; + +//=========================================================================================================================== + +class NativeBaseFolder : public AbstractBaseFolder +{ +public: + //itemPathImpl := native full item path as used by OS APIs + + NativeBaseFolder(const Zstring& baseDirPathIn) : baseDirPath(baseDirPathIn), baseDirPathPf(appendSeparator(baseDirPathIn)) {} + + //- THREAD-SAFETY: must be thread-safe like an int! => no dangling references to this instance! + std::function<void()> /*throw FileError*/ getAsyncConnectFolder(bool allowUserInteraction) const override //noexcept + { + const Zstring dirPath = baseDirPath; //help lambda capture syntax... + + return [dirPath, allowUserInteraction]() + { +#ifdef ZEN_WIN + //login to network share, if necessary + loginNetworkShare(dirPath, allowUserInteraction); +#endif + }; + }; + //---------------------------------------------------------------------------------------------------------------- + + bool emptyBaseFolderPath() const override { return baseDirPath.empty(); }; + + std::uint64_t getFreeDiskSpace() const override { return zen::getFreeDiskSpace(baseDirPath); } //throw FileError, returns 0 if not available + + //---------------------------------------------------------------------------------------------------------------- + bool supportsRecycleBin(const std::function<void ()>& onUpdateGui) const override //throw FileError + { +#ifdef ZEN_WIN + return recycleBinExists(baseDirPath, onUpdateGui); //throw FileError + +#elif defined ZEN_LINUX || defined ZEN_MAC + return true; //truth be told: no idea!!! +#endif + } + + std::unique_ptr<RecycleSession> createRecyclerSession() const override //throw FileError, return value must be bound! + { + assert(supportsRecycleBin(nullptr)); + return make_unique<RecycleSessionNative>(baseDirPathPf); + }; + + static Zstring getItemPathImplForRecycler(const AbstractPathRef& ap) + { + if (typeid(getAbf(ap)) != typeid(NativeBaseFolder)) + throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); + return getItemPathImpl(ap); + } + +private: + bool isNativeFileSystem() const override { return true; }; + + //- THREAD-SAFETY: expect accesses from multiple threads to the same instance => synchronize internally!!! + Zstring getDisplayPath(const Zstring& itemPathImpl) const override { return itemPathImpl; } + + //- THREAD-SAFETY: expect accesses from multiple threads to the same instance => synchronize internally!!! + Zstring appendRelPathToItemPathImpl(const Zstring& itemPathImpl, const Zstring& relPath) const override { return appendPaths(itemPathImpl, relPath); } + + //- THREAD-SAFETY: expect accesses from multiple threads to the same instance => synchronize internally!!! + Zstring getBasePathImpl() const override { return baseDirPath; } + + Zstring getFileShortName(const Zstring& itemPathImpl) const override { return afterLast(itemPathImpl, FILE_NAME_SEPARATOR); /*returns the whole string if term not found*/ } + + bool lessItemPathSameAbfType(const Zstring& itemPathImplLhs, const AbstractPathRef& apRhs) const override { return LessFilePath()(itemPathImplLhs, getItemPathImpl(apRhs)); } + + bool havePathDependencySameAbfType(const AbstractBaseFolder& other) const override + { + const Zstring& lhs = baseDirPathPf; + const Zstring& rhs = static_cast<const NativeBaseFolder&>(other).baseDirPathPf; + + return EqualFilePath()(Zstring(lhs.c_str(), std::min(lhs.length(), rhs.length())), //note: this is NOT an equivalence relation! + Zstring(rhs.c_str(), std::min(lhs.length(), rhs.length()))); + } + + //---------------------------------------------------------------------------------------------------------------- + bool fileExists (const Zstring& itemPathImpl) const override { return zen::fileExists (itemPathImpl); } //noexcept + bool dirExists (const Zstring& itemPathImpl) const override { return zen::dirExists (itemPathImpl); } //noexcept + bool symlinkExists (const Zstring& itemPathImpl) const override { return zen::symlinkExists (itemPathImpl); } //noexcept + bool somethingExists(const Zstring& itemPathImpl) const override { return zen::somethingExists(itemPathImpl); } //noexcept + //---------------------------------------------------------------------------------------------------------------- + //should provide for single ATOMIC folder creation! creates recursively! + void createNewFolder(const Zstring& itemPathImpl) const override { return zen::makeNewDirectory(itemPathImpl); } //throw FileError, ErrorTargetExisting + + bool removeFile(const Zstring& itemPathImpl) const override { return zen::removeFile(itemPathImpl); } //throw FileError + + void removeFolder(const Zstring& itemPathImpl, //throw FileError + const std::function<void (const Zstring& displayPath)>& onBeforeFileDeletion, //optional; + const std::function<void (const Zstring& displayPath)>& onBeforeDirDeletion) const override { zen::removeDirectory(itemPathImpl, onBeforeFileDeletion, onBeforeDirDeletion); } + //---------------------------------------------------------------------------------------------------------------- + void setFileTime(const Zstring& itemPathImpl, std::int64_t modificationTime, SymlinkHandling procSl) const override + { zen::setFileTime(itemPathImpl, modificationTime, procSl == SymlinkHandling::DIRECT ? ProcSymlink::DIRECT : ProcSymlink::FOLLOW); } //throw FileError + + AbstractPathRef getResolvedSymlinkPath(const Zstring& itemPathImpl) const override { return makeAbstractItem(*this, getResolvedFilePath(itemPathImpl)); }; //throw FileError + + Zstring getSymlinkContentBuffer(const Zstring& itemPathImpl) const override { return getSymlinkTargetRaw(itemPathImpl); } //throw FileError + + void recycleItemDirectly(const Zstring& itemPathImpl) const override { zen::recycleOrDelete(itemPathImpl); } //throw FileError + + //---------------------------------------------------------------------------------------------------------------- + //- THREAD-SAFETY: must be thread-safe like an int! => no dangling references to this instance! + IconLoader getAsyncIconLoader(const Zstring& itemPathImpl) const override //noexcept! + { + //COM should already have been initialized by icon buffer + IconLoader wl = {}; + wl.getFileIcon = [itemPathImpl](int pixelSize) { return getFileIcon (itemPathImpl, pixelSize); }; //noexcept! + wl.getThumbnailImage = [itemPathImpl](int pixelSize) { return getThumbnailImage(itemPathImpl, pixelSize); }; // + return wl; + } + //- THREAD-SAFETY: must be thread-safe like an int! => no dangling references to this instance! + std::function<bool()> getAsyncCheckDirExists(const Zstring& itemPathImpl) const override { return [itemPathImpl] { return zen::dirExists(itemPathImpl); }; } //noexcept + //---------------------------------------------------------------------------------------------------------------- + //return value always bound: + std::unique_ptr<InputStream > getInputStream (const Zstring& itemPathImpl) const override { return make_unique<InputStreamNative >(itemPathImpl); } //throw FileError, ErrorFileLocked + std::unique_ptr<OutputStream> getOutputStream(const Zstring& itemPathImpl, //throw FileError, ErrorTargetExisting + const std::uint64_t* streamSize, //optional + const std::int64_t* modificationTime) const override // + { return make_unique<OutputStreamNative>(itemPathImpl, makeAbstractItem(*this, itemPathImpl), streamSize, modificationTime); } //throw FileError, ErrorTargetExisting + //---------------------------------------------------------------------------------------------------------------- + //- THREAD-SAFETY: expect accesses from multiple threads to the same instance => synchronize internally!!! + void traverseFolder(const Zstring& itemPathImpl, TraverserCallback& sink) const override { DirTraverser::execute(itemPathImpl, sink); } + //---------------------------------------------------------------------------------------------------------------- + + //symlink handling: follow link! + FileAttribAfterCopy copyFileForSameAbfType(const Zstring& itemPathImplSource, const AbstractPathRef& apTarget, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) const override //may be nullptr; throw X! + { + const InSyncAttributes attrNew = copyNewFile(itemPathImplSource, getItemPathImpl(apTarget), //throw FileError, ErrorTargetExisting, ErrorFileLocked + copyFilePermissions, onNotifyCopyStatus); //may be nullptr; throw X! + FileAttribAfterCopy attrOut = {}; + attrOut.fileSize = attrNew.fileSize; + attrOut.modificationTime = attrNew.modificationTime; + attrOut.sourceFileId = convertToAbstractFileId(attrNew.sourceFileId); + attrOut.targetFileId = convertToAbstractFileId(attrNew.targetFileId); + return attrOut; + } + + //symlink handling: follow link! + void copyNewFolderForSameAbfType(const Zstring& itemPathImplSource, const AbstractPathRef& apTarget, bool copyFilePermissions) const override + { zen::copyNewDirectory(itemPathImplSource, getItemPathImpl(apTarget), copyFilePermissions); } //throw FileError + + void copySymlinkForSameAbfType(const Zstring& itemPathImplSource, const AbstractPathRef& apTarget, bool copyFilePermissions) const override + { zen::copySymlink(itemPathImplSource, getItemPathImpl(apTarget), copyFilePermissions); } //throw FileError + + void renameItemForSameAbfType(const Zstring& itemPathImplSource, const AbstractPathRef& apTarget) const override + { zen::renameFile(itemPathImplSource, getItemPathImpl(apTarget));} //throw FileError, ErrorTargetExisting, ErrorDifferentVolume + + bool supportsPermissions() const override { return zen::supportsPermissions(baseDirPath); } //throw FileError + + const Zstring baseDirPath; + const Zstring baseDirPathPf; +}; + +//=========================================================================================================================== + +#ifdef ZEN_WIN +//create + returns temporary directory postfixed with file name separator +//to support later cleanup if automatic deletion fails for whatever reason +Zstring RecycleSessionNative::getOrCreateRecyclerTempDirPf() //throw FileError +{ + assert(!baseDirPathPf_.empty()); + if (baseDirPathPf_.empty()) + return Zstring(); + + if (recyclerTmpDir.empty()) + recyclerTmpDir = [&] + { + assert(endsWith(baseDirPathPf_, FILE_NAME_SEPARATOR)); + /* + -> this naming convention is too cute and confusing for end users: + + //1. generate random directory name + static std::mt19937 rng(std::time(nullptr)); //don't use std::default_random_engine which leaves the choice to the STL implementer! + //- the alternative std::random_device may not always be available and can even throw an exception! + //- seed with second precision is sufficient: collisions are handled below + + const Zstring chars(Zstr("abcdefghijklmnopqrstuvwxyz") + Zstr("1234567890")); + std::uniform_int_distribution<size_t> distrib(0, chars.size() - 1); //takes closed range + + auto generatePath = [&]() -> Zstring //e.g. C:\Source\3vkf74fq.ffs_tmp + { + Zstring path = baseDirPf; + for (int i = 0; i < 8; ++i) + path += chars[distrib(rng)]; + return path + TEMP_FILE_ENDING; + }; + */ + + //ensure unique ownership: + Zstring dirpath = baseDirPathPf_ + Zstr("RecycleBin") + ABF::TEMP_FILE_ENDING; + for (int i = 0;; ++i) + try + { + makeNewDirectory(dirpath); //throw FileError, ErrorTargetExisting + return dirpath; + } + catch (const ErrorTargetExisting&) + { + if (i == 10) throw; //avoid endless recursion in pathological cases + dirpath = baseDirPathPf_ + Zstr("RecycleBin") + Zchar('_') + numberTo<Zstring>(i) + ABF::TEMP_FILE_ENDING; + } + }(); + + //assemble temporary recycle bin directory with random name and .ffs_tmp ending + return appendSeparator(recyclerTmpDir); +} +#endif + + +bool RecycleSessionNative::recycleItem(const AbstractPathRef& ap, const Zstring& logicalRelPath) //throw FileError +{ + const Zstring itemPath = NativeBaseFolder::getItemPathImplForRecycler(ap); + assert(!startsWith(logicalRelPath, FILE_NAME_SEPARATOR)); + +#ifdef ZEN_WIN + const Zstring tmpPath = getOrCreateRecyclerTempDirPf() + logicalRelPath; //throw FileError + bool deleted = false; + + auto moveToTempDir = [&] + { + try + { + //performance optimization: Instead of moving each object into recycle bin separately, + //we rename them one by one into a temporary directory and batch-recycle this directory after sync + renameFile(itemPath, tmpPath); //throw FileError, ErrorDifferentVolume + this->toBeRecycled.push_back(tmpPath); + deleted = true; + } + catch (ErrorDifferentVolume&) //MoveFileEx() returns ERROR_PATH_NOT_FOUND *before* considering ERROR_NOT_SAME_DEVICE! => we have to create tmpParentDir anyway to find out! + { + deleted = recycleOrDelete(itemPath); //throw FileError + } + }; + + try + { + moveToTempDir(); //throw FileError, ErrorDifferentVolume + } + catch (FileError&) + { + if (somethingExists(itemPath)) + { + const Zstring tmpParentDir = beforeLast(tmpPath, FILE_NAME_SEPARATOR); //what if C:\ ? + if (!somethingExists(tmpParentDir)) + { + makeNewDirectory(tmpParentDir); //throw FileError -> may legitimately fail on Linux if permissions are missing + moveToTempDir(); //throw FileError -> this should work now! + } + else + throw; + } + } + return deleted; + +#elif defined ZEN_LINUX || defined ZEN_MAC + return recycleOrDelete(itemPath); //throw FileError +#endif +} + + +void RecycleSessionNative::tryCleanup(const std::function<void (const Zstring& displayPath)>& notifyDeletionStatus) //throw FileError +{ +#ifdef ZEN_WIN + if (!toBeRecycled.empty()) + { + //move content of temporary directory to recycle bin in a single call + recycleOrDelete(toBeRecycled, notifyDeletionStatus); //throw FileError + toBeRecycled.clear(); + } + + //clean up temp directory itself (should contain remnant empty directories only) + if (!recyclerTmpDir.empty()) + { + removeDirectory(recyclerTmpDir); //throw FileError + recyclerTmpDir.clear(); + } +#endif +} +} + + +//coordinate changes with getResolvedDirectoryPath()! +bool zen::acceptsFolderPathPhraseNative(const Zstring& folderPathPhrase) //noexcept +{ + Zstring path = folderPathPhrase; + path = expandMacros(path); //expand before trimming! + trim(path); + +#ifdef ZEN_WIN + path = removeLongPathPrefix(path); +#endif + + if (startsWith(path, Zstr("["))) //drive letter by volume name syntax + return true; + + //don't accept relative paths!!! indistinguishable from Explorer MTP paths! + +#ifdef ZEN_WIN + if (path.size() >= 3 && + std::iswalpha(path[0]) && + path[1] == L':' && + path[2] == L'\\') + return true; //path starting with drive letter + + return path.size() >= 3 && + path[0] == L'\\' && + path[1] == L'\\' && + std::iswalpha(path[2]); //UNC path + +#elif defined ZEN_LINUX || defined ZEN_MAC + return startsWith(path, Zstr("/")); +#endif +} + + +Zstring zen::getResolvedDisplayPathNative(const Zstring& folderPathPhrase) //noexcept +{ + return getResolvedDirectoryPath(folderPathPhrase); //noexcept + warn_static("get volume by name for idle HDD! => call async getFormattedDirectoryPath, but currently not thread-safe") +} + + +std::unique_ptr<AbstractBaseFolder> zen::createBaseFolderNative(const Zstring& folderPathPhrase) //noexcept +{ + return make_unique<NativeBaseFolder>(getResolvedDirectoryPath(folderPathPhrase)); +} diff --git a/FreeFileSync/Source/fs/native.h b/FreeFileSync/Source/fs/native.h new file mode 100644 index 00000000..83dc0abc --- /dev/null +++ b/FreeFileSync/Source/fs/native.h @@ -0,0 +1,20 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef FS_NATIVE_183247018532434563465 +#define FS_NATIVE_183247018532434563465 + +#include "abstract.h" + +namespace zen +{ +bool acceptsFolderPathPhraseNative(const Zstring& folderPathPhrase); //noexcept +Zstring getResolvedDisplayPathNative(const Zstring& folderPathPhrase); //noexcept + +std::unique_ptr<AbstractBaseFolder> createBaseFolderNative(const Zstring& folderPathPhrase); //noexcept +} + +#endif //FS_NATIVE_183247018532434563465 diff --git a/FreeFileSync/Source/fs/native_traverser_impl.h b/FreeFileSync/Source/fs/native_traverser_impl.h new file mode 100644 index 00000000..564cd136 --- /dev/null +++ b/FreeFileSync/Source/fs/native_traverser_impl.h @@ -0,0 +1,570 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include <zen/sys_error.h> +#include <zen/symlink_target.h> +#include <zen/int64.h> + +#ifdef ZEN_WIN + #include <zen/win_ver.h> + #include <zen/long_path_prefix.h> + #include <zen/file_access.h> + #include <zen/dll.h> + #include "../dll/FindFilePlus/find_file_plus.h" + +#elif defined ZEN_MAC + #include <zen/osx_string.h> +#endif + +#if defined ZEN_LINUX || defined ZEN_MAC + #include <cstddef> //offsetof + #include <sys/stat.h> + #include <dirent.h> +#endif + +using namespace zen; +using ABF = AbstractBaseFolder; + + +namespace +{ +inline +ABF::FileId convertToAbstractFileId(const zen::FileId& fid) +{ + if (fid == zen::FileId()) + return ABF::FileId(); + + ABF::FileId out(reinterpret_cast<const char*>(&fid.first), sizeof(fid.first)); + out.append(reinterpret_cast<const char*>(&fid.second), sizeof(fid.second)); + return out; +} + + +#ifdef ZEN_WIN +struct SymlinkTargetInfo +{ + std::uint64_t fileSize; + std::int64_t lastWriteTime; + FileId id; +}; + +SymlinkTargetInfo getInfoFromFileSymlink(const Zstring& linkName) //throw FileError +{ + //open handle to target of symbolic link + HANDLE hFile = ::CreateFile(zen::applyLongPathPrefix(linkName).c_str(), //_In_ LPCTSTR lpFileName, + 0, //_In_ DWORD dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, + nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, + OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, + FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, + //needed to open a directory -> keep it even if we expect to open a file! See comment below + nullptr); //_In_opt_ HANDLE hTemplateFile + if (hFile == INVALID_HANDLE_VALUE) + throwFileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)), L"CreateFile", getLastError()); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); + + BY_HANDLE_FILE_INFORMATION fileInfo = {}; + if (!::GetFileInformationByHandle(hFile, &fileInfo)) + throwFileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)), L"GetFileInformationByHandle", getLastError()); + + //a file symlink may incorrectly point to a directory, but both CreateFile() and GetFileInformationByHandle() will succeed and return garbage! + //- if we did not use FILE_FLAG_BACKUP_SEMANTICS above, CreateFile() would error out with an even less helpful ERROR_ACCESS_DENIED! + //- reinterpreting the link as a directory symlink would still fail during traversal, so just show an error here + //- OTOH a directory symlink that points to a file fails immediately in ::FindFirstFile() with ERROR_DIRECTORY! -> nothing to do in this case + if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)), formatSystemError(L"GetFileInformationByHandle", static_cast<DWORD>(ERROR_FILE_INVALID))); + + return { get64BitUInt(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh), + filetimeToTimeT(fileInfo.ftLastWriteTime), + extractFileId(fileInfo) }; //consider detection of moved files: allow for duplicate file ids, renaming affects symlink, not target, ... +} + + +DWORD retrieveVolumeSerial(const Zstring& pathName) //returns 0 on error or if serial is not supported! +{ + //this works for: + //- root paths "C:\", "D:\" + //- network shares: \\share\dirname + //- indirection: subst S: %USERPROFILE% + // -> GetVolumePathName() + GetVolumeInformation() OTOH incorrectly resolves "S:\Desktop\somedir" to "S:\Desktop\" - nice try... + const HANDLE hDir = ::CreateFile(zen::applyLongPathPrefix(pathName).c_str(), //_In_ LPCTSTR lpFileName, + 0, //_In_ DWORD dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, + nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, + OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, + // FILE_FLAG_OPEN_REPARSE_POINT -> no, we follow symlinks! + FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, + /*needed to open a directory*/ + nullptr); //_In_opt_ HANDLE hTemplateFile + if (hDir == INVALID_HANDLE_VALUE) + return 0; + ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); + + BY_HANDLE_FILE_INFORMATION fileInfo = {}; + if (!::GetFileInformationByHandle(hDir, &fileInfo)) + return 0; + + return fileInfo.dwVolumeSerialNumber; +} + + +const bool isXpOrLater = winXpOrLater(); //VS2010 compiled DLLs are not supported on Win 2000 and show popup dialog "DecodePointer not found" + +#define DEF_DLL_FUN(name) const auto name = isXpOrLater ? DllFun<findplus::FunType_##name>(findplus::getDllName(), findplus::funName_##name) : DllFun<findplus::FunType_##name>(); +DEF_DLL_FUN(openDir); // +DEF_DLL_FUN(readDir); //load at startup: avoid pre C++11 static initialization MT issues +DEF_DLL_FUN(closeDir); // +#undef DEF_DLL_FUN + +/* +Common C-style interface for Win32 FindFirstFile(), FindNextFile() and FileFilePlus openDir(), closeDir(): +struct TraverserPolicy //see "policy based design" +{ +typedef ... DirHandle; +typedef ... FindData; + +static DirHandle create(const Zstring& directory); //throw FileError - don't follow FindFirstFile() design: open handle only, *no* return of data! +static void destroy(DirHandle hnd); //throw() + +static bool getEntry(DirHandle hnd, const Zstring& directory, FindData& fileInfo) //throw FileError, NeedFallbackToWin32Traverser -> fallback to FindFirstFile()/FindNextFile() + +//FindData "member" functions +static const wchar_t* getItemName(const FindData& fileInfo); +static std::uint64_t getFileSize(const FindData& fileInfo); +static std::int64_t getModTime (const FindData& fileInfo); +static FileId getFileId (const FindData& fileInfo, DWORD volumeSerial); //volumeSerial may be 0 if not available! +static bool isDirectory(const FindData& fileInfo); +static bool isSymlink (const FindData& fileInfo); +} + +Note: Win32 FindFirstFile(), FindNextFile() is a weaker abstraction than FileFilePlus openDir(), readDir(), closeDir() and Unix opendir(), closedir(), stat() +*/ + + +struct Win32Traverser +{ + struct DirHandle + { + DirHandle(HANDLE hnd, const WIN32_FIND_DATA& d) : searchHandle(hnd), haveData(true), data(d) {} + explicit DirHandle(HANDLE hnd) : searchHandle(hnd), haveData(false) {} + + HANDLE searchHandle; + bool haveData; + WIN32_FIND_DATA data; + }; + + typedef WIN32_FIND_DATA FindData; + + static DirHandle create(const Zstring& dirPath) //throw FileError + { + const Zstring& dirPathPf = appendSeparator(dirPath); + + WIN32_FIND_DATA fileData = {}; + HANDLE hnd = ::FindFirstFile(applyLongPathPrefix(dirPathPf + L'*').c_str(), &fileData); + //no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH + if (hnd == INVALID_HANDLE_VALUE) + { + const DWORD lastError = ::GetLastError(); //copy before making other system calls! + if (lastError == ERROR_FILE_NOT_FOUND) + { + //1. directory may not exist *or* 2. it is completely empty: not all directories contain "., .." entries, e.g. a drive's root directory; NetDrive + // -> FindFirstFile() is a nice example of violation of API design principle of single responsibility + if (dirExists(dirPath)) //yes, a race-condition, still the best we can do + return DirHandle(hnd); + } + throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirPath)), L"FindFirstFile", lastError); + } + return DirHandle(hnd, fileData); + } + + static void destroy(const DirHandle& hnd) { ::FindClose(hnd.searchHandle); } //throw() + + static bool getEntry(DirHandle& hnd, const Zstring& dirPath, FindData& fileInfo) //throw FileError + { + if (hnd.searchHandle == INVALID_HANDLE_VALUE) //handle special case of "truly empty directories" + return false; + + if (hnd.haveData) + { + hnd.haveData = false; + ::memcpy(&fileInfo, &hnd.data, sizeof(fileInfo)); + return true; + } + + if (!::FindNextFile(hnd.searchHandle, &fileInfo)) + { + const DWORD lastError = ::GetLastError(); //copy before making other system calls! + if (lastError == ERROR_NO_MORE_FILES) //not an error situation + return false; + //else we have a problem... report it: + throwFileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"FindNextFile", lastError); + } + return true; + } + + static const wchar_t* getItemName(const FindData& fileInfo) { return fileInfo.cFileName; } + static std::uint64_t getFileSize(const FindData& fileInfo) { return get64BitUInt(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); } + static std::int64_t getModTime (const FindData& fileInfo) { return filetimeToTimeT(fileInfo.ftLastWriteTime); } + static FileId getFileId (const FindData& fileInfo, DWORD volumeSerial) { return FileId(); } + static bool isDirectory(const FindData& fileInfo) { return (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; } + static bool isSymlink (const FindData& fileInfo) { return zen::isSymlink(fileInfo); } //[!] keep namespace +}; + + +class NeedFallbackToWin32Traverser {}; //special exception class + + +struct FilePlusTraverser +{ + struct DirHandle + { + explicit DirHandle(findplus::FindHandle hnd) : searchHandle(hnd) {} + + findplus::FindHandle searchHandle; + }; + + typedef findplus::FileInformation FindData; + + static DirHandle create(const Zstring& dirPath) //throw FileError + { + const findplus::FindHandle hnd = ::openDir(applyLongPathPrefix(dirPath).c_str()); + if (!hnd) + throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirPath)), L"openDir", getLastError()); + + return DirHandle(hnd); + } + + static void destroy(DirHandle hnd) { ::closeDir(hnd.searchHandle); } //throw() + + static bool getEntry(DirHandle hnd, const Zstring& dirPath, FindData& fileInfo) //throw FileError, NeedFallbackToWin32Traverser + { + if (!::readDir(hnd.searchHandle, fileInfo)) + { + const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + if (lastError == ERROR_NO_MORE_FILES) //not an error situation + return false; + + /* + fallback to default directory query method, if FileIdBothDirectoryInformation is not properly implemented + this is required for NetDrive mounted Webdav, e.g. www.box.net and NT4, 2000 remote drives, et al. + */ + if (lastError == ERROR_NOT_SUPPORTED) + throw NeedFallbackToWin32Traverser(); + //fallback should apply to whole directory sub-tree! => client needs to handle duplicate file notifications! + + //else we have a problem... report it: + throwFileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"readDir", lastError); + } + + return true; + } + + static const wchar_t* getItemName(const FindData& fileInfo) { return fileInfo.shortName; } + static std::uint64_t getFileSize(const FindData& fileInfo) { return fileInfo.fileSize; } + static std::int64_t getModTime (const FindData& fileInfo) { return filetimeToTimeT(fileInfo.lastWriteTime); } + static FileId getFileId (const FindData& fileInfo, DWORD volumeSerial) { return extractFileId(volumeSerial, fileInfo.fileId); } + static bool isDirectory(const FindData& fileInfo) { return (fileInfo.fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; } + static bool isSymlink (const FindData& fileInfo) { return zen::isSymlink(fileInfo.fileAttributes, fileInfo.reparseTag); } //[!] keep namespace +}; + + +class DirTraverser +{ +public: + static void execute(const Zstring& baseDirectory, ABF::TraverserCallback& sink) + { + DirTraverser(baseDirectory, sink); + } + +private: + DirTraverser(const Zstring& baseDirectory, ABF::TraverserCallback& sink); + DirTraverser (const DirTraverser&) = delete; + DirTraverser& operator=(const DirTraverser&) = delete; + + template <class Trav> + void traverse(const Zstring& dirPath, ABF::TraverserCallback& sink, DWORD volumeSerial); + + template <class Trav> + void traverseWithException(const Zstring& dirPath, ABF::TraverserCallback& sink, DWORD volumeSerial /*may be 0!*/); //throw FileError, NeedFallbackToWin32Traverser +}; + + +template <> inline +void DirTraverser::traverse<Win32Traverser>(const Zstring& dirPath, ABF::TraverserCallback& sink, DWORD volumeSerial) +{ + tryReportingDirError([&] + { + traverseWithException<Win32Traverser>(dirPath, sink, 0); //throw FileError + }, sink); +} + + +template <> inline +void DirTraverser::traverse<FilePlusTraverser>(const Zstring& dirPath, ABF::TraverserCallback& sink, DWORD volumeSerial) +{ + try + { + tryReportingDirError([&] + { + traverseWithException<FilePlusTraverser>(dirPath, sink, volumeSerial); //throw FileError, NeedFallbackToWin32Traverser + }, sink); + } + catch (NeedFallbackToWin32Traverser&) { traverse<Win32Traverser>(dirPath, sink, 0); } +} + + +inline +DirTraverser::DirTraverser(const Zstring& baseDirectory, ABF::TraverserCallback& sink) +{ + try //traversing certain folders with restricted permissions requires this privilege! (but copying these files may still fail) + { + activatePrivilege(SE_BACKUP_NAME); //throw FileError + } + catch (FileError&) {} //don't cause issues in user mode + + if (::openDir && ::readDir && ::closeDir) + traverse<FilePlusTraverser>(baseDirectory, sink, retrieveVolumeSerial(baseDirectory)); //retrieveVolumeSerial returns 0 on error + else //fallback + traverse<Win32Traverser>(baseDirectory, sink, 0); +} + + +template <class Trav> +void DirTraverser::traverseWithException(const Zstring& dirPath, ABF::TraverserCallback& sink, DWORD volumeSerial /*may be 0!*/) //throw FileError, NeedFallbackToWin32Traverser +{ + //no need to check for endless recursion: Windows seems to have an internal path limit of about 700 chars + + typename Trav::DirHandle searchHandle = Trav::create(dirPath); //throw FileError + ZEN_ON_SCOPE_EXIT(Trav::destroy(searchHandle)); + + typename Trav::FindData findData = {}; + + while (Trav::getEntry(searchHandle, dirPath, findData)) //throw FileError, NeedFallbackToWin32Traverser + //don't retry but restart dir traversal on error! http://blogs.msdn.com/b/oldnewthing/archive/2014/06/12/10533529.aspx + { + //skip "." and ".." + const Zchar* const shortName = Trav::getItemName(findData); + + if (shortName[0] == 0) throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"Trav::getEntry: Data corruption, found item without name."); //show error instead of endless recursion!!! + if (shortName[0] == L'.' && + (shortName[1] == 0 || (shortName[1] == L'.' && shortName[2] == 0))) + continue; + + const Zstring& itempath = appendSeparator(dirPath) + shortName; + + if (Trav::isSymlink(findData)) //check first! + { + const ABF::TraverserCallback::SymlinkInfo linkInfo = { shortName, Trav::getModTime(findData) }; + + switch (sink.onSymlink(linkInfo)) + { + case ABF::TraverserCallback::LINK_FOLLOW: + if (Trav::isDirectory(findData)) + { + if (ABF::TraverserCallback* trav = sink.onDir({ shortName })) + { + ZEN_ON_SCOPE_EXIT(sink.releaseDirTraverser(trav)); + traverse<Trav>(itempath, *trav, retrieveVolumeSerial(itempath)); //symlink may link to different volume => redetermine volume serial! + } + } + else //a file + { + SymlinkTargetInfo targetInfo= {}; + const bool validLink = tryReportingItemError([&] //try to resolve symlink (and report error on failure!!!) + { + targetInfo = getInfoFromFileSymlink(itempath); //throw FileError + }, sink, shortName); + + if (validLink) + { + ABF::TraverserCallback::FileInfo fi = { shortName, targetInfo.fileSize, targetInfo.lastWriteTime, convertToAbstractFileId(targetInfo.id), &linkInfo }; + sink.onFile(fi); + } + // else //broken symlink -> ignore: it's client's responsibility to handle error! + } + break; + + case ABF::TraverserCallback::LINK_SKIP: + break; + } + } + else if (Trav::isDirectory(findData)) + { + if (ABF::TraverserCallback* trav = sink.onDir({ shortName })) + { + ZEN_ON_SCOPE_EXIT(sink.releaseDirTraverser(trav)); + traverse<Trav>(itempath, *trav, volumeSerial); + } + } + else //a file + { + ABF::TraverserCallback::FileInfo fi = { shortName, Trav::getFileSize(findData), Trav::getModTime(findData), convertToAbstractFileId(Trav::getFileId(findData, volumeSerial)), nullptr }; + sink.onFile(fi); //init "fi" on separate line: VC 2013 compiler crash when using temporary!!! + } + } +} + + +#elif defined ZEN_LINUX || defined ZEN_MAC +class DirTraverser +{ +public: + static void execute(const Zstring& baseDirectory, ABF::TraverserCallback& sink) + { + DirTraverser(baseDirectory, sink); + } + +private: + DirTraverser(const Zstring& baseDirectory, ABF::TraverserCallback& sink) + { + /* quote: "Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede + that field within the dirent structure, portable applications that use readdir_r() should allocate + the buffer whose address is passed in entry as follows: + len = offsetof(struct dirent, d_name) + pathconf(dirPath, _PC_NAME_MAX) + 1 + entryp = malloc(len); */ + const size_t nameMax = std::max<long>(::pathconf(baseDirectory.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1) + buffer.resize(offsetof(struct ::dirent, d_name) + nameMax + 1); + + traverse(baseDirectory, sink); + } + + DirTraverser (const DirTraverser&) = delete; + DirTraverser& operator=(const DirTraverser&) = delete; + + void traverse(const Zstring& dirPath, ABF::TraverserCallback& sink) + { + tryReportingDirError([&] + { + traverseWithException(dirPath, sink); //throw FileError + }, sink); + } + + void traverseWithException(const Zstring& dirPath, ABF::TraverserCallback& sink) //throw FileError + { + //no need to check for endless recursion: Linux has a fixed limit on the number of symbolic links in a path + + DIR* dirObj = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/" + if (!dirObj) + throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirPath)), L"opendir", getLastError()); + ZEN_ON_SCOPE_EXIT(::closedir(dirObj)); //never close nullptr handles! -> crash + + for (;;) + { + struct ::dirent* dirEntry = nullptr; + if (::readdir_r(dirObj, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0) + throwFileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"readdir_r", getLastError()); + //don't retry but restart dir traversal on error! http://blogs.msdn.com/b/oldnewthing/archive/2014/06/12/10533529.aspx + + if (!dirEntry) //no more items + return; + + //don't return "." and ".." + const char* shortName = dirEntry->d_name; //evaluate dirEntry *before* going into recursion => we use a single "buffer"! + + if (shortName[0] == 0) throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"readdir_r: Data corruption, found item without name."); + if (shortName[0] == '.' && + (shortName[1] == 0 || (shortName[1] == '.' && shortName[2] == 0))) + continue; +#ifdef ZEN_MAC + //some file system abstraction layers fail to properly return decomposed UTF8: http://developer.apple.com/library/mac/#qa/qa1173/_index.html + //so we need to do it ourselves; perf: ~600 ns per conversion + //note: it's not sufficient to apply this in z_impl::compareFilenamesNoCase: if UTF8 forms differ, FFS assumes a rename in case sensitivity and + // will try to propagate the rename => this won't work if target drive reports a particular UTF8 form only! + if (CFStringRef cfStr = osx::createCFString(shortName)) + { + ZEN_ON_SCOPE_EXIT(::CFRelease(cfStr)); + + CFIndex lenMax = ::CFStringGetMaximumSizeOfFileSystemRepresentation(cfStr); //"could be much larger than the actual space required" => don't store in Zstring + if (lenMax > 0) + { + bufferUtfDecomposed.resize(lenMax); + if (::CFStringGetFileSystemRepresentation(cfStr, &bufferUtfDecomposed[0], lenMax)) //get decomposed UTF form (verified!) despite ambiguous documentation + shortName = &bufferUtfDecomposed[0]; //attention: => don't access "shortName" after recursion in "traverse"! + } + } + //const char* sampleDecomposed = "\x6f\xcc\x81.txt"; + //const char* samplePrecomposed = "\xc3\xb3.txt"; +#endif + const Zstring& itempath = appendSeparator(dirPath) + shortName; + + struct ::stat statData = {}; + if (!tryReportingItemError([&] + { + if (::lstat(itempath.c_str(), &statData) != 0) //lstat() does not resolve symlinks + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(itempath)), L"lstat", getLastError()); + }, sink, shortName)) + continue; //ignore error: skip file + + if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks! + { + const ABF::TraverserCallback::SymlinkInfo linkInfo = { shortName, statData.st_mtime }; + + switch (sink.onSymlink(linkInfo)) + { + case ABF::TraverserCallback::LINK_FOLLOW: + { + //try to resolve symlink (and report error on failure!!!) + struct ::stat statDataTrg = {}; + bool validLink = tryReportingItemError([&] + { + if (::stat(itempath.c_str(), &statDataTrg) != 0) + throwFileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(itempath)), L"stat", getLastError()); + }, sink, shortName); + + if (validLink) + { + if (S_ISDIR(statDataTrg.st_mode)) //a directory + { + if (ABF::TraverserCallback* trav = sink.onDir({ shortName })) + { + ZEN_ON_SCOPE_EXIT(sink.releaseDirTraverser(trav)); + traverse(itempath, *trav); + } + } + else //a file or named pipe, ect. + { + ABF::TraverserCallback::FileInfo fi = { shortName, makeUnsigned(statDataTrg.st_size), statDataTrg.st_mtime, convertToAbstractFileId(extractFileId(statDataTrg)), &linkInfo }; + sink.onFile(fi); + } + } + // else //broken symlink -> ignore: it's client's responsibility to handle error! + } + break; + + case ABF::TraverserCallback::LINK_SKIP: + break; + } + } + else if (S_ISDIR(statData.st_mode)) //a directory + { + if (ABF::TraverserCallback* trav = sink.onDir({ shortName })) + { + ZEN_ON_SCOPE_EXIT(sink.releaseDirTraverser(trav)); + traverse(itempath, *trav); + } + } + else //a file or named pipe, ect. + { + ABF::TraverserCallback::FileInfo fi = { shortName, makeUnsigned(statData.st_size), statData.st_mtime, convertToAbstractFileId(extractFileId(statData)), nullptr }; + sink.onFile(fi); + } + /* + It may be a good idea to not check "S_ISREG(statData.st_mode)" explicitly and to not issue an error message on other types to support these scenarios: + - RTS setup watch (essentially wants to read directories only) + - removeDirectory (wants to delete everything; pipes can be deleted just like files via "unlink") + + However an "open" on a pipe will block (https://sourceforge.net/p/freefilesync/bugs/221/), so the copy routines need to be smarter!! + */ + } + } + + std::vector<char> buffer; +#ifdef ZEN_MAC + std::vector<char> bufferUtfDecomposed; +#endif +}; +#endif +} diff --git a/FreeFileSync/Source/lib/binary.cpp b/FreeFileSync/Source/lib/binary.cpp index 6b7277ce..9dec8ea7 100644 --- a/FreeFileSync/Source/lib/binary.cpp +++ b/FreeFileSync/Source/lib/binary.cpp @@ -11,7 +11,7 @@ #include <boost/thread/tss.hpp> using namespace zen; - +using ABF = AbstractBaseFolder; namespace { @@ -37,7 +37,7 @@ buffer MB/s class BufferSize { public: - BufferSize() : bufSize(BUFFER_SIZE_START) {} + BufferSize(size_t initialSize) : bufSize(initialSize) {} void inc() { @@ -54,9 +54,8 @@ public: size_t get() const { return bufSize; } private: - static const size_t BUFFER_SIZE_MIN = 64 * 1024; - static const size_t BUFFER_SIZE_START = 128 * 1024; //initial buffer size - static const size_t BUFFER_SIZE_MAX = 16 * 1024 * 1024; + static const size_t BUFFER_SIZE_MIN = 8 * 1024; //slow FTP transfer! + static const size_t BUFFER_SIZE_MAX = 1024 * 1024 * 1024; size_t bufSize; }; @@ -74,7 +73,7 @@ const std::int64_t TICKS_PER_SEC = ticksPerSec(); } -bool zen::filesHaveSameContent(const Zstring& filepath1, const Zstring& filepath2, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError +bool zen::filesHaveSameContent(const AbstractPathRef& filePath1, const AbstractPathRef& filePath2, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError { static boost::thread_specific_ptr<std::vector<char>> cpyBuf1; static boost::thread_specific_ptr<std::vector<char>> cpyBuf2; @@ -86,22 +85,23 @@ bool zen::filesHaveSameContent(const Zstring& filepath1, const Zstring& filepath std::vector<char>& memory1 = *cpyBuf1; std::vector<char>& memory2 = *cpyBuf2; - FileInput file1(filepath1); //throw FileError - FileInput file2(filepath2); // + const std::unique_ptr<ABF::InputStream> inStream1 = ABF::getInputStream(filePath1); //throw FileError, (ErrorFileLocked) + const std::unique_ptr<ABF::InputStream> inStream2 = ABF::getInputStream(filePath2); // - BufferSize bufferSize; + BufferSize bufferSize(std::min(inStream1->optimalBlockSize(), + inStream2->optimalBlockSize())); TickVal lastDelayViolation = getTicks(); - do + for (;;) { setMinSize(memory1, bufferSize.get()); setMinSize(memory2, bufferSize.get()); const TickVal startTime = getTicks(); - const size_t length1 = file1.read(&memory1[0], bufferSize.get()); //throw FileError - const size_t length2 = file2.read(&memory2[0], bufferSize.get()); //returns actual number of bytes read + const size_t length1 = inStream1->read(&memory1[0], bufferSize.get()); //throw FileError + const size_t length2 = inStream2->read(&memory2[0], bufferSize.get()); //returns actual number of bytes read //send progress updates immediately after reading to reliably allow speed calculations for our clients! if (onUpdateStatus) onUpdateStatus(std::max(length1, length2)); @@ -130,11 +130,8 @@ bool zen::filesHaveSameContent(const Zstring& filepath1, const Zstring& filepath } } //------------------------------------------------------------------------------------------------ - } - while (!file1.eof()); - if (!file2.eof()) //unlikely, but possible - return false; - - return true; + if (length1 != bufferSize.get()) //end of file + return true; + } } diff --git a/FreeFileSync/Source/lib/binary.h b/FreeFileSync/Source/lib/binary.h index 279f69d3..44ded84b 100644 --- a/FreeFileSync/Source/lib/binary.h +++ b/FreeFileSync/Source/lib/binary.h @@ -8,13 +8,13 @@ #define BINARY_H_INCLUDED_3941281398513241134 #include <functional> -#include <zen/zstring.h> #include <zen/file_error.h> +#include "../fs/abstract.h" namespace zen { -bool filesHaveSameContent(const Zstring& filepath1, //throw FileError - const Zstring& filepath2, +bool filesHaveSameContent(const AbstractPathRef& filePath1, //throw FileError + const AbstractPathRef& filePath2, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus); //may be nullptr } diff --git a/FreeFileSync/Source/lib/db_file.cpp b/FreeFileSync/Source/lib/db_file.cpp index feac429b..bcc30f3e 100644 --- a/FreeFileSync/Source/lib/db_file.cpp +++ b/FreeFileSync/Source/lib/db_file.cpp @@ -5,12 +5,7 @@ // ************************************************************************** #include "db_file.h" -#include <unordered_set> -#include <zen/file_access.h> -#include <zen/scope_guard.h> #include <zen/guid.h> -#include <zen/utf.h> -#include <zen/serialize.h> #include <wx+/zlib_wrap.h> #ifdef ZEN_WIN @@ -26,18 +21,21 @@ namespace //------------------------------------------------------------------------------------------------------------------------------- const char FILE_FORMAT_DESCR[] = "FreeFileSync"; const int DB_FORMAT_CONTAINER = 9; -const int DB_FORMAT_STREAM = 1; +const int DB_FORMAT_STREAM = 2; //since 2015-05-02 //------------------------------------------------------------------------------------------------------------------------------- typedef std::string UniqueId; -typedef std::map<UniqueId, BinaryStream> DbStreams; //list of streams ordered by session UUID +typedef std::map<UniqueId, ByteArray> DbStreams; //list of streams ordered by session UUID + +using MemStreamOut = MemoryStreamOut<ByteArray>; +using MemStreamIn = MemoryStreamIn <ByteArray>; //----------------------------------------------------------------------------------- //| ensure 32/64 bit portability: use fixed size data types only e.g. std::uint32_t | //----------------------------------------------------------------------------------- template <SelectedSide side> inline -Zstring getDatabaseFilePath(const BaseDirPair& baseDirObj, bool tempfile = false) +AbstractPathRef getDatabaseFilePath(const BaseDirPair& baseDirObj, bool tempfile = false) { //Linux and Windows builds are binary incompatible: different file id?, problem with case sensitivity? are UTC file times really compatible? //what about endianess!? @@ -45,63 +43,81 @@ Zstring getDatabaseFilePath(const BaseDirPair& baseDirObj, bool tempfile = false //Give db files different names. //make sure they end with ".ffs_db". These files will be excluded from comparison #ifdef ZEN_WIN - Zstring dbname = Zstring(Zstr("sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; + const Zstring dbname = Zstr("sync"); #elif defined ZEN_LINUX || defined ZEN_MAC - //files beginning with dots are hidden e.g. in Nautilus - Zstring dbname = Zstring(Zstr(".sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; + const Zstring dbname = Zstr(".sync"); //files beginning with dots are hidden e.g. in Nautilus #endif - return baseDirObj.getBaseDirPf<side>() + dbname; + const Zstring dbFileName = dbname + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; + + return baseDirObj.getABF<side>().getAbstractPath(dbFileName); } //####################################################################################################################################### -void saveStreams(const DbStreams& streamList, const Zstring& filepath, const std::function<void(std::int64_t bytesDelta)>& onUpdateSaveStatus) //throw FileError +void saveStreams(const DbStreams& streamList, const AbstractPathRef& dbPath, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError { - BinStreamOut streamOut; + //perf? instead of writing to a file stream directly, collect data into memory first, then write to file block-wise + MemStreamOut memStreamOut; //write FreeFileSync file identifier - writeArray(streamOut, FILE_FORMAT_DESCR, sizeof(FILE_FORMAT_DESCR)); + writeArray(memStreamOut, FILE_FORMAT_DESCR, sizeof(FILE_FORMAT_DESCR)); //save file format version - writeNumber<std::int32_t>(streamOut, DB_FORMAT_CONTAINER); + writeNumber<std::int32_t>(memStreamOut, DB_FORMAT_CONTAINER); //save stream list - writeNumber<std::uint32_t>(streamOut, static_cast<std::uint32_t>(streamList.size())); //number of streams, one for each sync-pair + writeNumber<std::uint32_t>(memStreamOut, static_cast<std::uint32_t>(streamList.size())); //number of streams, one for each sync-pair for (const auto& stream : streamList) { - writeContainer<std::string >(streamOut, stream.first ); - writeContainer<BinaryStream>(streamOut, stream.second); + writeContainer<std::string>(memStreamOut, stream.first ); + writeContainer<ByteArray> (memStreamOut, stream.second); } - assert(!somethingExists(filepath)); //orphan tmp files should have been cleaned up at this point! - saveBinStream(filepath, streamOut.get(), onUpdateSaveStatus); //throw FileError + assert(!ABF::somethingExists(dbPath)); //orphan tmp files should have been cleaned up at this point! + + //save memory stream to file (as a transaction!) + { + MemoryStreamIn<ByteArray> memStreamIn(memStreamOut.ref()); + const std::uint64_t streamSize = memStreamOut.ref().size(); + const std::unique_ptr<ABF::OutputStream> fileStreamOut = ABF::getOutputStream(dbPath, &streamSize, nullptr /*modificationTime*/); //throw FileError, ErrorTargetExisting + if (onUpdateStatus) onUpdateStatus(0); + copyStream(memStreamIn, *fileStreamOut, fileStreamOut->optimalBlockSize(), onUpdateStatus); //throw FileError + fileStreamOut->finalize([&] { if (onUpdateStatus) onUpdateStatus(0); }); //throw FileError + //commit and close stream + } #ifdef ZEN_WIN - //be careful to avoid CreateFile() + CREATE_ALWAYS on a hidden file -> see file_io.cpp - ::SetFileAttributes(applyLongPathPrefix(filepath).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide database file + if (Opt<Zstring> nativeFilePath = ABF::getNativeItemPath(dbPath)) + ::SetFileAttributes(applyLongPathPrefix(*nativeFilePath).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide database file #endif } -DbStreams loadStreams(const Zstring& filepath) //throw FileError, FileErrorDatabaseNotExisting +DbStreams loadStreams(const AbstractPathRef& dbPath, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError, FileErrorDatabaseNotExisting { try { - warn_static("TODO: implement loadBinStream callback") + //load memory stream from file + MemoryStreamOut<ByteArray> memStreamOut; + { + const std::unique_ptr<ABF::InputStream> fileStreamIn = ABF::getInputStream(dbPath); //throw FileError, ErrorFileLocked + if (onUpdateStatus) onUpdateStatus(0); + copyStream(*fileStreamIn, memStreamOut, fileStreamIn->optimalBlockSize(), onUpdateStatus); //throw FileError + } //close file handle - BinStreamIn streamIn = loadBinStream<BinaryStream>(filepath, nullptr); //throw FileError + MemStreamIn streamIn(memStreamOut.ref()); //read FreeFileSync file identifier char formatDescr[sizeof(FILE_FORMAT_DESCR)] = {}; readArray(streamIn, formatDescr, sizeof(formatDescr)); //throw UnexpectedEndOfStreamError if (!std::equal(FILE_FORMAT_DESCR, FILE_FORMAT_DESCR + sizeof(FILE_FORMAT_DESCR), formatDescr)) - throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filepath))); + throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(ABF::getDisplayPath(dbPath)))); const int version = readNumber<std::int32_t>(streamIn); //throw UnexpectedEndOfStreamError if (version != DB_FORMAT_CONTAINER) //read file format version number - throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filepath))); + throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(ABF::getDisplayPath(dbPath)))); DbStreams output; @@ -110,8 +126,8 @@ DbStreams loadStreams(const Zstring& filepath) //throw FileError, FileErrorDatab while (dbCount-- != 0) { //DB id of partner databases - std::string sessionID = readContainer<std::string >(streamIn); //throw UnexpectedEndOfStreamError - BinaryStream stream = readContainer<BinaryStream>(streamIn); // + std::string sessionID = readContainer<std::string>(streamIn); //throw UnexpectedEndOfStreamError + ByteArray stream = readContainer<ByteArray> (streamIn); // output[sessionID] = std::move(stream); } @@ -119,18 +135,18 @@ DbStreams loadStreams(const Zstring& filepath) //throw FileError, FileErrorDatab } catch (FileError&) { - if (!somethingExists(filepath)) //a benign(?) race condition with FileError + if (!ABF::somethingExists(dbPath)) //a benign(?) race condition with FileError throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + - replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtFileName(filepath))); + replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtFileName(ABF::getDisplayPath(dbPath)))); throw; } catch (UnexpectedEndOfStreamError&) { - throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filepath)); + throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(ABF::getDisplayPath(dbPath))); } catch (const std::bad_alloc& e) //still required? { - throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filepath), + throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(ABF::getDisplayPath(dbPath)), _("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what())); } } @@ -141,10 +157,10 @@ class StreamGenerator //for db-file back-wards compatibility we stick with two o { public: static void execute(const InSyncDir& dir, //throw FileError - const Zstring& filepathL, //used for diagnostics only - const Zstring& filepathR, - BinaryStream& streamL, - BinaryStream& streamR) + const Zstring& displayFilePathL, //used for diagnostics only + const Zstring& displayFilePathR, + ByteArray& streamL, + ByteArray& streamR) { StreamGenerator generator; @@ -152,7 +168,7 @@ public: generator.recurse(dir); //PERF_STOP - auto compStream = [](const BinaryStream& stream, const Zstring& filepath) -> BinaryStream //throw FileError + auto compStream = [](const ByteArray& stream, const Zstring& displayFilePath) -> ByteArray //throw FileError { try { @@ -172,16 +188,16 @@ public: } catch (ZlibInternalError&) { - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filepath)), L"zlib internal error"); + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(displayFilePath)), L"zlib internal error"); } }; - const BinaryStream tmpL = compStream(generator.outputLeft .get(), filepathL); - const BinaryStream tmpR = compStream(generator.outputRight.get(), filepathR); - const BinaryStream tmpB = compStream(generator.outputBoth .get(), filepathL + Zstr("/") + filepathR); + const ByteArray tmpL = compStream(generator.outputLeft .ref(), displayFilePathL); + const ByteArray tmpR = compStream(generator.outputRight.ref(), displayFilePathR); + const ByteArray tmpB = compStream(generator.outputBoth .ref(), displayFilePathL + Zstr("/") + displayFilePathR); - BinStreamOut outL; - BinStreamOut outR; + MemStreamOut outL; + MemStreamOut outR; //save format version writeNumber<std::int32_t>(outL, DB_FORMAT_STREAM); writeNumber<std::int32_t>(outR, DB_FORMAT_STREAM); @@ -200,11 +216,11 @@ public: writeArray(outR, &*tmpB.begin() + size1stPart, size2ndPart); //write streams corresponding to one side only - writeContainer<BinaryStream>(outL, tmpL); - writeContainer<BinaryStream>(outR, tmpR); + writeContainer<ByteArray>(outL, tmpL); + writeContainer<ByteArray>(outR, tmpR); - streamL = outL.get(); - streamR = outR.get(); + streamL = outL.ref(); + streamR = outR.ref(); } private: @@ -241,37 +257,35 @@ private: } } - static void writeUtf8(BinStreamOut& output, const Zstring& str) { writeContainer(output, utfCvrtTo<Zbase<char>>(str)); } + static void writeUtf8(MemStreamOut& output, const Zstring& str) { writeContainer(output, utfCvrtTo<Zbase<char>>(str)); } - static void writeFile(BinStreamOut& output, const InSyncDescrFile& descr) + static void writeFile(MemStreamOut& output, const InSyncDescrFile& descr) { writeNumber<std:: int64_t>(output, descr.lastWriteTimeRaw); - writeNumber<std::uint64_t>(output, descr.fileId.first); - writeNumber<std::uint64_t>(output, descr.fileId.second); - static_assert(sizeof(descr.fileId.first ) <= sizeof(std::uint64_t), ""); - static_assert(sizeof(descr.fileId.second) <= sizeof(std::uint64_t), ""); + writeContainer(output, descr.fileId); + static_assert(IsSameType<decltype(descr.fileId), Zbase<char>>::value, ""); } - static void writeLink(BinStreamOut& output, const InSyncDescrLink& descr) + static void writeLink(MemStreamOut& output, const InSyncDescrLink& descr) { writeNumber<std::int64_t>(output, descr.lastWriteTimeRaw); } - BinStreamOut outputLeft; //data related to one side only - BinStreamOut outputRight; // - BinStreamOut outputBoth; //data concerning both sides + MemStreamOut outputLeft; //data related to one side only + MemStreamOut outputRight; // + MemStreamOut outputBoth; //data concerning both sides }; class StreamParser { public: - static std::shared_ptr<InSyncDir> execute(const BinaryStream& streamL, //throw FileError - const BinaryStream& streamR, - const Zstring& filepathL, //used for diagnostics only - const Zstring& filepathR) + static std::shared_ptr<InSyncDir> execute(const ByteArray& streamL, //throw FileError + const ByteArray& streamR, + const Zstring& displayFilePathL, //used for diagnostics only + const Zstring& displayFilePathR) { - auto decompStream = [](const BinaryStream& stream, const Zstring& filepath) -> BinaryStream //throw FileError + auto decompStream = [](const ByteArray& stream, const Zstring& displayFilePath) -> ByteArray //throw FileError { try { @@ -279,65 +293,71 @@ public: } catch (ZlibInternalError&) { - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filepath)), L"zlib internal error"); + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(displayFilePath)), L"zlib internal error"); } }; try { - BinStreamIn inL(streamL); - BinStreamIn inR(streamR); + MemStreamIn inL(streamL); + MemStreamIn inR(streamR); const int streamVersionL = readNumber<std::int32_t>(inL); //throw UnexpectedEndOfStreamError const int streamVersionR = readNumber<std::int32_t>(inR); // - if (streamVersionL != DB_FORMAT_STREAM) - throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filepathL)), L"unknown stream format"); - if (streamVersionR != DB_FORMAT_STREAM) - throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filepathR)), L"unknown stream format"); + if (streamVersionL != streamVersionR) + throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(displayFilePathL) + L"\n" + fmtFileName(displayFilePathR), L"different stream formats"); + + warn_static("remove check for stream version 1 after migration! 2015-05-02") + if (streamVersionL != 1 && + streamVersionL != DB_FORMAT_STREAM) + throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(displayFilePathL)), L"unknown stream format"); const bool has1stPartL = readNumber<std::int8_t>(inL) != 0; //throw UnexpectedEndOfStreamError const bool has1stPartR = readNumber<std::int8_t>(inR) != 0; // if (has1stPartL == has1stPartR) - throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filepathL) + L"\n" + fmtFileName(filepathR), L"second part missing"); + throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(displayFilePathL) + L"\n" + fmtFileName(displayFilePathR), L"second part missing"); - BinStreamIn& in1stPart = has1stPartL ? inL : inR; - BinStreamIn& in2ndPart = has1stPartL ? inR : inL; + MemStreamIn& in1stPart = has1stPartL ? inL : inR; + MemStreamIn& in2ndPart = has1stPartL ? inR : inL; const size_t size1stPart = static_cast<size_t>(readNumber<std::uint64_t>(in1stPart)); const size_t size2ndPart = static_cast<size_t>(readNumber<std::uint64_t>(in2ndPart)); - BinaryStream tmpB; + ByteArray tmpB; tmpB.resize(size1stPart + size2ndPart); //throw bad_alloc readArray(in1stPart, &*tmpB.begin(), size1stPart); //stream always non-empty readArray(in2ndPart, &*tmpB.begin() + size1stPart, size2ndPart); // - const BinaryStream tmpL = readContainer<BinaryStream>(inL); - const BinaryStream tmpR = readContainer<BinaryStream>(inR); + const ByteArray tmpL = readContainer<ByteArray>(inL); + const ByteArray tmpR = readContainer<ByteArray>(inR); auto output = std::make_shared<InSyncDir>(InSyncDir::DIR_STATUS_IN_SYNC); - StreamParser parser(decompStream(tmpL, filepathL), - decompStream(tmpR, filepathR), - decompStream(tmpB, filepathL + Zstr("/") + filepathR)); + StreamParser parser(streamVersionL, + decompStream(tmpL, displayFilePathL), + decompStream(tmpR, displayFilePathR), + decompStream(tmpB, displayFilePathL + Zstr("/") + displayFilePathR)); parser.recurse(*output); //throw UnexpectedEndOfStreamError return output; } catch (const UnexpectedEndOfStreamError&) { - throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filepathL) + L"\n" + fmtFileName(filepathR)); + throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(displayFilePathL) + L"\n" + fmtFileName(displayFilePathR)); } catch (const std::bad_alloc& e) { - throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filepathL) + L"\n" + fmtFileName(filepathR), + throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(displayFilePathL) + L"\n" + fmtFileName(displayFilePathR), _("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what())); } } private: - StreamParser(const BinaryStream& bufferL, - const BinaryStream& bufferR, - const BinaryStream& bufferB) : + StreamParser(int streamVersion, + const ByteArray& bufferL, + const ByteArray& bufferR, + const ByteArray& bufferB) : + streamVersion_(streamVersion), inputLeft (bufferL), inputRight(bufferR), inputBoth (bufferB) {} @@ -376,26 +396,42 @@ private: } } - static Zstring readUtf8(BinStreamIn& input) { return utfCvrtTo<Zstring>(readContainer<Zbase<char>>(input)); } //throw UnexpectedEndOfStreamError + static Zstring readUtf8(MemStreamIn& input) { return utfCvrtTo<Zstring>(readContainer<Zbase<char>>(input)); } //throw UnexpectedEndOfStreamError - static InSyncDescrFile readFile(BinStreamIn& input) + InSyncDescrFile readFile(MemStreamIn& input) const { //attention: order of function argument evaluation is undefined! So do it one after the other... auto lastWriteTimeRaw = readNumber<std::int64_t>(input); //throw UnexpectedEndOfStreamError - auto devId = static_cast<DeviceId >(readNumber<std::uint64_t>(input)); // - auto fileIdx = static_cast<FileIndex>(readNumber<std::uint64_t>(input)); //silence "loss of precision" compiler warnings - return InSyncDescrFile(lastWriteTimeRaw, FileId(devId, fileIdx)); + + ABF::FileId fileId; + warn_static("remove after migration! 2015-05-02") + if (streamVersion_ == 1) + { + auto devId = static_cast<DeviceId >(readNumber<std::uint64_t>(input)); // + auto fileIdx = static_cast<FileIndex>(readNumber<std::uint64_t>(input)); //silence "loss of precision" compiler warnings + if (devId != 0 || fileIdx != 0) + { + fileId.append(reinterpret_cast<const char*>(&devId), sizeof(devId)); + fileId.append(reinterpret_cast<const char*>(&fileIdx), sizeof(fileIdx)); + } + } + else + + fileId = readContainer<Zbase<char>>(input); + + return InSyncDescrFile(lastWriteTimeRaw, fileId); } - static InSyncDescrLink readLink(BinStreamIn& input) + static InSyncDescrLink readLink(MemStreamIn& input) { auto lastWriteTimeRaw = readNumber<std::int64_t>(input); return InSyncDescrLink(lastWriteTimeRaw); } - BinStreamIn inputLeft; //data related to one side only - BinStreamIn inputRight; // - BinStreamIn inputBoth; //data concerning both sides + const int streamVersion_; + MemStreamIn inputLeft; //data related to one side only + MemStreamIn inputRight; // + MemStreamIn inputBoth; //data concerning both sides }; //####################################################################################################################################### @@ -633,24 +669,25 @@ private: //####################################################################################################################################### -std::shared_ptr<InSyncDir> zen::loadLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! +std::shared_ptr<InSyncDir> zen::loadLastSynchronousState(const BaseDirPair& baseDirObj, //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! + const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) { - const Zstring filepathLeft = getDatabaseFilePath<LEFT_SIDE >(baseDirObj); - const Zstring filepathRight = getDatabaseFilePath<RIGHT_SIDE>(baseDirObj); + const AbstractPathRef dbPathLeft = getDatabaseFilePath<LEFT_SIDE >(baseDirObj); + const AbstractPathRef dbPathRight = getDatabaseFilePath<RIGHT_SIDE>(baseDirObj); if (!baseDirObj.isExisting<LEFT_SIDE >() || !baseDirObj.isExisting<RIGHT_SIDE>()) { //avoid race condition with directory existence check: reading sync.ffs_db may succeed although first dir check had failed => conflicts! //https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3531351&group_id=234430 - const Zstring filepath = !baseDirObj.isExisting<LEFT_SIDE>() ? filepathLeft : filepathRight; + const AbstractPathRef filePath = !baseDirObj.isExisting<LEFT_SIDE>() ? dbPathLeft : dbPathRight; throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + //it could be due to a to-be-created target directory not yet existing => FileErrorDatabaseNotExisting - replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtFileName(filepath))); + replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtFileName(ABF::getDisplayPath(filePath)))); } //read file data: list of session ID + DirInfo-stream - const DbStreams streamsLeft = ::loadStreams(filepathLeft); //throw FileError, FileErrorDatabaseNotExisting - const DbStreams streamsRight = ::loadStreams(filepathRight); // + const DbStreams streamsLeft = ::loadStreams(dbPathLeft, onUpdateStatus); //throw FileError, FileErrorDatabaseNotExisting + const DbStreams streamsRight = ::loadStreams(dbPathRight, onUpdateStatus); // //find associated session: there can be at most one session within intersection of left and right ids for (const auto& streamLeft : streamsLeft) @@ -660,8 +697,8 @@ std::shared_ptr<InSyncDir> zen::loadLastSynchronousState(const BaseDirPair& base { return StreamParser::execute(streamLeft.second, //throw FileError itRight->second, - filepathLeft, - filepathRight); + ABF::getDisplayPath(dbPathLeft), + ABF::getDisplayPath(dbPathRight)); } } throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + @@ -669,26 +706,30 @@ std::shared_ptr<InSyncDir> zen::loadLastSynchronousState(const BaseDirPair& base } -void zen::saveLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileError +void zen::saveLastSynchronousState(const BaseDirPair& baseDirObj, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError { //transactional behaviour! write to tmp files first - const Zstring dbNameLeftTmp = getDatabaseFilePath<LEFT_SIDE >(baseDirObj, true); - const Zstring dbNameRightTmp = getDatabaseFilePath<RIGHT_SIDE>(baseDirObj, true); + const AbstractPathRef dbPathLeft = getDatabaseFilePath<LEFT_SIDE >(baseDirObj); + const AbstractPathRef dbPathRight = getDatabaseFilePath<RIGHT_SIDE>(baseDirObj); - const Zstring dbNameLeft = getDatabaseFilePath<LEFT_SIDE >(baseDirObj); - const Zstring dbNameRight = getDatabaseFilePath<RIGHT_SIDE>(baseDirObj); + const AbstractPathRef dbPathLeftTmp = getDatabaseFilePath<LEFT_SIDE >(baseDirObj, true); + const AbstractPathRef dbPathRightTmp = getDatabaseFilePath<RIGHT_SIDE>(baseDirObj, true); //delete old tmp file, if necessary -> throws if deletion fails! - removeFile(dbNameLeftTmp); // - removeFile(dbNameRightTmp); //throw FileError + ABF::removeFile(dbPathLeftTmp); // + ABF::removeFile(dbPathRightTmp); //throw FileError //(try to) load old database files... DbStreams streamsLeft; //list of session ID + DirInfo-stream DbStreams streamsRight; - try { streamsLeft = ::loadStreams(dbNameLeft ); } + //std::function<void(std::int64_t bytesDelta)> onUpdateLoadStatus; + //if (onUpdateStatus) + // onUpdateLoadStatus = [&](std::int64_t bytesDelta) { onUpdateStatus(0); }; + + try { streamsLeft = ::loadStreams(dbPathLeft, onUpdateStatus); } catch (FileError&) {} - try { streamsRight = ::loadStreams(dbNameRight); } + try { streamsRight = ::loadStreams(dbPathRight, onUpdateStatus); } catch (FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing! @@ -714,8 +755,8 @@ void zen::saveLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileEr { lastSyncState = StreamParser::execute(itStreamLeftOld ->second, //throw FileError itStreamRightOld->second, - dbNameLeft, - dbNameRight); + ABF::getDisplayPath(dbPathLeft), + ABF::getDisplayPath(dbPathRight)); } catch (FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing! @@ -723,13 +764,13 @@ void zen::saveLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileEr UpdateLastSynchronousState::execute(baseDirObj, *lastSyncState); //serialize again - BinaryStream updatedStreamLeft; - BinaryStream updatedStreamRight; - StreamGenerator::execute(*lastSyncState, - dbNameLeft, - dbNameRight, + ByteArray updatedStreamLeft; + ByteArray updatedStreamRight; + StreamGenerator::execute(*lastSyncState, //throw FileError + ABF::getDisplayPath(dbPathLeft), + ABF::getDisplayPath(dbPathRight), updatedStreamLeft, - updatedStreamRight); //throw FileError + updatedStreamRight); //check if there is some work to do at all if (itStreamLeftOld != streamsLeft .end() && updatedStreamLeft == itStreamLeftOld ->second && @@ -748,23 +789,15 @@ void zen::saveLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileEr streamsLeft [sessionID] = std::move(updatedStreamLeft); streamsRight[sessionID] = std::move(updatedStreamRight); - warn_static("TODO: implement saveStreams callback") - - //write (temp-) files... - zen::ScopeGuard guardTempFileLeft = zen::makeGuard([&] {zen::removeFile(dbNameLeftTmp); }); - saveStreams(streamsLeft, dbNameLeftTmp, nullptr); //throw FileError - - zen::ScopeGuard guardTempFileRight = zen::makeGuard([&] {zen::removeFile(dbNameRightTmp); }); - saveStreams(streamsRight, dbNameRightTmp, nullptr); //throw FileError + //write (temp-) files as a transaction + saveStreams(streamsLeft, dbPathLeftTmp, onUpdateStatus); //throw FileError + saveStreams(streamsRight, dbPathRightTmp, onUpdateStatus); // //operation finished: rename temp files -> this should work transactionally: //if there were no write access, creation of temp files would have failed - removeFile(dbNameLeft); // - renameFile(dbNameLeftTmp, dbNameLeft); //throw FileError - - removeFile(dbNameRight); // - renameFile(dbNameRightTmp, dbNameRight); // + ABF::removeFile(dbPathLeft); //throw FileError + ABF::renameItem(dbPathLeftTmp, dbPathLeft); //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) - guardTempFileLeft. dismiss(); //no need to delete temp files anymore - guardTempFileRight.dismiss(); // + ABF::removeFile(dbPathRight); // + ABF::renameItem(dbPathRightTmp, dbPathRight); // } diff --git a/FreeFileSync/Source/lib/db_file.h b/FreeFileSync/Source/lib/db_file.h index 9ceacf51..d2bd43c8 100644 --- a/FreeFileSync/Source/lib/db_file.h +++ b/FreeFileSync/Source/lib/db_file.h @@ -16,12 +16,12 @@ const Zchar SYNC_DB_FILE_ENDING[] = Zstr(".ffs_db"); //don't use Zstring as glob struct InSyncDescrFile //subset of FileDescriptor { - InSyncDescrFile(std::int64_t lastWriteTimeRawIn, const FileId& idIn) : + InSyncDescrFile(std::int64_t lastWriteTimeRawIn, const ABF::FileId& idIn) : lastWriteTimeRaw(lastWriteTimeRawIn), fileId(idIn) {} std::int64_t lastWriteTimeRaw; - FileId fileId; // == file id: optional! (however, always set on Linux, and *generally* available on Windows) + ABF::FileId fileId; // == file id: optional! (however, always set on Linux, and *generally* available on Windows) }; struct InSyncDescrLink @@ -64,9 +64,9 @@ struct InSyncDir InSyncStatus status; //------------------------------------------------------------------ - typedef std::map<Zstring, InSyncDir, LessFilename> DirList; // - typedef std::map<Zstring, InSyncFile, LessFilename> FileList; // key: shortName - typedef std::map<Zstring, InSyncSymlink, LessFilename> LinkList; // + typedef std::map<Zstring, InSyncDir, LessFilePath> DirList; // + typedef std::map<Zstring, InSyncFile, LessFilePath> FileList; // key: shortName + typedef std::map<Zstring, InSyncSymlink, LessFilePath> LinkList; // //------------------------------------------------------------------ DirList dirs; @@ -93,9 +93,11 @@ struct InSyncDir DEFINE_NEW_FILE_ERROR(FileErrorDatabaseNotExisting); -std::shared_ptr<InSyncDir> loadLastSynchronousState(const BaseDirPair& baseDirObj); //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! +std::shared_ptr<InSyncDir> loadLastSynchronousState(const BaseDirPair& baseDirObj, //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! + const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus); -void saveLastSynchronousState(const BaseDirPair& baseDirObj); //throw FileError +void saveLastSynchronousState(const BaseDirPair& baseDirObj, //throw FileError + const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus); } #endif //DBFILE_H_834275398588021574 diff --git a/FreeFileSync/Source/lib/deep_file_traverser.cpp b/FreeFileSync/Source/lib/deep_file_traverser.cpp deleted file mode 100644 index d95cd1a2..00000000 --- a/FreeFileSync/Source/lib/deep_file_traverser.cpp +++ /dev/null @@ -1,208 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "deep_file_traverser.h" -#include <zen/sys_error.h> -#include <zen/symlink_target.h> -#include <zen/int64.h> -#include <cstddef> //offsetof -#include <sys/stat.h> -#include <dirent.h> - -using namespace zen; - - -namespace -{ -//implement "retry" in a generic way: - -template <class Command> inline //function object expecting to throw FileError if operation fails -bool tryReportingDirError(Command cmd, zen::TraverseCallback& callback) //return "true" on success, "false" if error was ignored -{ - for (size_t retryNumber = 0;; ++retryNumber) - try - { - cmd(); //throw FileError - return true; - } - catch (const FileError& e) - { - switch (callback.reportDirError(e.toString(), retryNumber)) - { - case TraverseCallback::ON_ERROR_RETRY: - break; - case TraverseCallback::ON_ERROR_IGNORE: - return false; - } - } -} - -template <class Command> inline //function object expecting to throw FileError if operation fails -bool tryReportingItemError(Command cmd, zen::TraverseCallback& callback, const Zchar* shortName) //return "true" on success, "false" if error was ignored -{ - for (size_t retryNumber = 0;; ++retryNumber) - try - { - cmd(); //throw FileError - return true; - } - catch (const FileError& e) - { - switch (callback.reportItemError(e.toString(), retryNumber, shortName)) - { - case TraverseCallback::ON_ERROR_RETRY: - break; - case TraverseCallback::ON_ERROR_IGNORE: - return false; - } - } -} - - -class DirTraverser -{ -public: - static void execute(const Zstring& baseDirectory, TraverseCallback& sink) - { - DirTraverser(baseDirectory, sink); - } - -private: - DirTraverser(const Zstring& baseDirectory, zen::TraverseCallback& sink) - { - const Zstring directoryFormatted = //remove trailing slash - baseDirectory.size() > 1 && endsWith(baseDirectory, FILE_NAME_SEPARATOR) ? //exception: allow '/' - beforeLast(baseDirectory, FILE_NAME_SEPARATOR) : - baseDirectory; - - /* quote: "Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede - that field within the dirent structure, portable applications that use readdir_r() should allocate - the buffer whose address is passed in entry as follows: - len = offsetof(struct dirent, d_name) + pathconf(dirPath, _PC_NAME_MAX) + 1 - entryp = malloc(len); */ - const size_t nameMax = std::max<long>(::pathconf(directoryFormatted.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1) - buffer.resize(offsetof(struct ::dirent, d_name) + nameMax + 1); - - traverse(directoryFormatted, sink); - } - - DirTraverser (const DirTraverser&) = delete; - DirTraverser& operator=(const DirTraverser&) = delete; - - void traverse(const Zstring& dirPath, TraverseCallback& sink) - { - tryReportingDirError([&] - { - traverseWithException(dirPath, sink); //throw FileError - }, sink); - } - - void traverseWithException(const Zstring& dirPath, TraverseCallback& sink) //throw FileError - { - //no need to check for endless recursion: Linux has a fixed limit on the number of symbolic links in a path - - DIR* dirObj = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/" - if (!dirObj) - throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirPath)), L"opendir", getLastError()); - ZEN_ON_SCOPE_EXIT(::closedir(dirObj)); //never close nullptr handles! -> crash - - for (;;) - { - struct ::dirent* dirEntry = nullptr; - if (::readdir_r(dirObj, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0) - throwFileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"readdir_r", getLastError()); - //don't retry but restart dir traversal on error! http://blogs.msdn.com/b/oldnewthing/archive/2014/06/12/10533529.aspx - - if (!dirEntry) //no more items - return; - - //don't return "." and ".." - const char* shortName = dirEntry->d_name; //evaluate dirEntry *before* going into recursion => we use a single "buffer"! - - if (shortName[0] == 0) throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"Data corruption: Found item without name."); - if (shortName[0] == '.' && - (shortName[1] == 0 || (shortName[1] == '.' && shortName[2] == 0))) - continue; - - const Zstring& itempath = appendSeparator(dirPath) + shortName; - - struct ::stat statData = {}; - if (!tryReportingItemError([&] - { - if (::lstat(itempath.c_str(), &statData) != 0) //lstat() does not resolve symlinks - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(itempath)), L"lstat", getLastError()); - }, sink, shortName)) - continue; //ignore error: skip file - - if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks! - { - const TraverseCallback::SymlinkInfo linkInfo = { shortName, itempath, statData.st_mtime }; - - switch (sink.onSymlink(linkInfo)) - { - case TraverseCallback::LINK_FOLLOW: - { - //try to resolve symlink (and report error on failure!!!) - struct ::stat statDataTrg = {}; - bool validLink = tryReportingItemError([&] - { - if (::stat(itempath.c_str(), &statDataTrg) != 0) - throwFileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(itempath)), L"stat", getLastError()); - }, sink, shortName); - - if (validLink) - { - if (S_ISDIR(statDataTrg.st_mode)) //a directory - { - if (TraverseCallback* trav = sink.onDir({ shortName, itempath })) - { - ZEN_ON_SCOPE_EXIT(sink.releaseDirTraverser(trav)); - traverse(itempath, *trav); - } - } - else //a file or named pipe, ect. - { - TraverseCallback::FileInfo fi = { shortName, itempath, makeUnsigned(statDataTrg.st_size), statDataTrg.st_mtime, extractFileId(statDataTrg), &linkInfo }; - sink.onFile(fi); - } - } - // else //broken symlink -> ignore: it's client's responsibility to handle error! - } - break; - - case TraverseCallback::LINK_SKIP: - break; - } - } - else if (S_ISDIR(statData.st_mode)) //a directory - { - if (TraverseCallback* trav = sink.onDir({ shortName, itempath })) - { - ZEN_ON_SCOPE_EXIT(sink.releaseDirTraverser(trav)); - traverse(itempath, *trav); - } - } - else //a file or named pipe, ect. - { - TraverseCallback::FileInfo fi = { shortName, itempath, makeUnsigned(statData.st_size), statData.st_mtime, extractFileId(statData), nullptr }; - sink.onFile(fi); - } - /* - It may be a good idea to not check "S_ISREG(statData.st_mode)" explicitly and to not issue an error message on other types to support these scenarios: - - RTS setup watch (essentially wants to read directories only) - - removeDirectory (wants to delete everything; pipes can be deleted just like files via "unlink") - - However an "open" on a pipe will block (https://sourceforge.net/p/freefilesync/bugs/221/), so the copy routines need to be smarter!! - */ - } - } - - std::vector<char> buffer; -}; -} - - -void zen::deepTraverseFolder(const Zstring& dirPath, TraverseCallback& sink) { DirTraverser::execute(dirPath, sink); } diff --git a/FreeFileSync/Source/lib/deep_file_traverser.h b/FreeFileSync/Source/lib/deep_file_traverser.h deleted file mode 100644 index 50740e8e..00000000 --- a/FreeFileSync/Source/lib/deep_file_traverser.h +++ /dev/null @@ -1,73 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef DEEP_FILE_TRAVERSER_H_INCLUDED_342765897342151 -#define DEEP_FILE_TRAVERSER_H_INCLUDED_342765897342151 - -#include <cstdint> -#include <zen/zstring.h> -#include <zen/file_id_def.h> - -//advanced file traverser returning metadata and hierarchical information on files and directories - -namespace zen -{ -struct TraverseCallback -{ - virtual ~TraverseCallback() {} - - struct SymlinkInfo - { - const Zchar* shortName; - const Zstring& fullPath; - std::int64_t lastWriteTime; //number of seconds since Jan. 1st 1970 UTC - }; - - struct FileInfo - { - const Zchar* shortName; - const Zstring& fullPath; - std::uint64_t fileSize; //unit: bytes! - std::int64_t lastWriteTime; //number of seconds since Jan. 1st 1970 UTC - const FileId& id; //optional: initial if not supported! - const SymlinkInfo* symlinkInfo; //only filled if file is a followed symlink - }; - - struct DirInfo - { - const Zchar* shortName; - const Zstring& fullPath; - }; - - enum HandleLink - { - LINK_FOLLOW, //dereferences link, then calls "onDir()" or "onFile()" - LINK_SKIP - }; - - enum HandleError - { - ON_ERROR_RETRY, - ON_ERROR_IGNORE - }; - - virtual void onFile (const FileInfo& fi) = 0; - virtual TraverseCallback* onDir (const DirInfo& di) = 0; - virtual HandleLink onSymlink(const SymlinkInfo& li) = 0; - //nullptr: ignore directory, non-nullptr: traverse into using the (new) callback => implement releaseDirTraverser() if necessary! - virtual void releaseDirTraverser(TraverseCallback* trav) {} - - virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) = 0; //failed directory traversal -> consider directory data at current level as incomplete! - virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) = 0; //failed to get data for single file/dir/symlink only! -}; - -//custom traverser with detail information about files -//- client needs to handle duplicate file reports! (FilePlusTraverser fallback, retrying to read directory contents, ...) -//- directory may end with PATH_SEPARATOR -void deepTraverseFolder(const Zstring& dirpath, TraverseCallback& sink); //noexcept -} - -#endif //DEEP_FILE_TRAVERSER_H_INCLUDED_342765897342151 diff --git a/FreeFileSync/Source/lib/dir_exist_async.h b/FreeFileSync/Source/lib/dir_exist_async.h index 0d8340b2..0341a216 100644 --- a/FreeFileSync/Source/lib/dir_exist_async.h +++ b/FreeFileSync/Source/lib/dir_exist_async.h @@ -8,10 +8,9 @@ #define DIR_EXIST_HEADER_08173281673432158067342132467183267 #include <zen/thread.h> -#include <zen/file_access.h> #include <zen/file_error.h> +#include "../fs/abstract.h" #include "../process_callback.h" -#include "resolve_path.h" namespace zen { @@ -20,62 +19,103 @@ namespace //directory existence checking may hang for non-existent network drives => run asynchronously and update UI! //- check existence of all directories in parallel! (avoid adding up search times if multiple network drives are not reachable) //- add reasonable time-out time! -//- avoid checking duplicate entries by design: set<Zstring, LessFilename> +//- avoid checking duplicate entries by design: std::set struct DirectoryStatus { - std::set<Zstring, LessFilename> existing; - std::set<Zstring, LessFilename> missing; + std::set<const ABF*, ABF::LessItemPath> existingBaseFolder; + std::set<const ABF*, ABF::LessItemPath> missingBaseFolder; + std::map<const ABF*, FileError, ABF::LessItemPath> failedChecks; +}; + + +struct DirCheckResult +{ + bool exists; + std::unique_ptr<FileError> error; + DirCheckResult(bool existsIn, std::unique_ptr<FileError>&& errorIn) : exists(existsIn), error(std::move(errorIn)) {} + + DirCheckResult (DirCheckResult&& tmp) : exists(tmp.exists), error(std::move(tmp.error)) {} //= default with C++14 + DirCheckResult& operator=(DirCheckResult&& tmp) //= default with C++14 + { + exists = tmp.exists; + error = std::move(tmp.error); + return *this; + } }; -DirectoryStatus getExistingDirsUpdating(const std::set<Zstring, LessFilename>& dirpaths, - bool allowUserInteraction, - ProcessCallback& procCallback) + +DirectoryStatus checkFolderExistenceUpdating(const std::set<const ABF*, ABF::LessItemPath>& baseFolders, bool allowUserInteraction, ProcessCallback& procCallback) { using namespace zen; DirectoryStatus output; - std::list<std::pair<Zstring, boost::unique_future<bool>>> futureInfo; - for (const Zstring& dirpath : dirpaths) - if (!dirpath.empty()) //skip empty dirs - futureInfo.emplace_back(dirpath, async([=]() -> bool + std::list<std::pair<const ABF*, boost::unique_future<DirCheckResult>>> futureInfo; + + for (const ABF* baseFolder : baseFolders) + if (!baseFolder->emptyBaseFolderPath()) //skip empty dirs { -#ifdef ZEN_WIN - //1. login to network share, if necessary - loginNetworkShare(dirpath, allowUserInteraction); -#endif - //2. check dir existence - return dirExists(dirpath); - })); - - //don't wait (almost) endlessly like win32 would on not existing network shares: + AbstractPathRef apr = baseFolder->getAbstractPath(Zstring()); + + std::function<void()> connectFolder /*throw FileError*/ = baseFolder->getAsyncConnectFolder(allowUserInteraction); //noexcept + std::function<bool()> dirExists /*noexcept*/ = ABF::getAsyncCheckDirExists(apr); //noexcept + + futureInfo.emplace_back(baseFolder, async([connectFolder, dirExists] + { + //1. login to network share, open FTP connection, ect. + if (connectFolder) + try + { + connectFolder(); //throw FileError + } + catch (const FileError& e) + { + return DirCheckResult(false, make_unique<FileError>(e)); + } + + //2. check dir existence + assert(dirExists); + return DirCheckResult(dirExists ? dirExists() : false, nullptr); + })); + } + + //don't wait (almost) endlessly like win32 would on non-existing network shares: const boost::system_time endTime = boost::get_system_time() + boost::posix_time::seconds(20); //consider CD-rom insert or hard disk spin up time from sleep for (auto& fi : futureInfo) { - procCallback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", fmtFileName(fi.first), false)); //may throw! + const std::wstring& displayPathFmt = fmtFileName(ABF::getDisplayPath(fi.first->getAbstractPath(Zstring()))); + + procCallback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", displayPathFmt)); //may throw! while (boost::get_system_time() < endTime && !fi.second.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2))) procCallback.requestUiRefresh(); //may throw! - if (fi.second.is_ready() && fi.second.get()) - output.existing.insert(fi.first); + if (fi.second.is_ready()) + { + if (fi.second.get().error) + output.failedChecks.emplace(fi.first, *fi.second.get().error); + else if (fi.second.get().exists) + output.existingBaseFolder.insert(fi.first); + else + output.missingBaseFolder.insert(fi.first); + } else - output.missing.insert(fi.first); + output.failedChecks.emplace(fi.first, FileError(replaceCpy(_("Time out while searching for folder %x."), L"%x", displayPathFmt))); } + return output; } } inline //also silences Clang "unused function" for compilation units depending from getExistingDirsUpdating() only -bool dirExistsUpdating(const Zstring& dirpath, bool allowUserInteraction, ProcessCallback& procCallback) +bool folderExistsUpdating(const ABF& baseFolder, bool allowUserInteraction, ProcessCallback& procCallback) { - if (dirpath.empty()) return false; - const DirectoryStatus dirStatus = getExistingDirsUpdating({ dirpath }, allowUserInteraction, procCallback); - assert(dirStatus.existing.empty() != dirStatus.missing.empty()); - return dirStatus.existing.find(dirpath) != dirStatus.existing.end(); + std::set<const ABF*, ABF::LessItemPath> baseFolders{ &baseFolder }; + const DirectoryStatus status = checkFolderExistenceUpdating(baseFolders, allowUserInteraction, procCallback); + return status.existingBaseFolder.find(&baseFolder) != status.existingBaseFolder.end(); } } diff --git a/FreeFileSync/Source/lib/dir_lock.cpp b/FreeFileSync/Source/lib/dir_lock.cpp index fd2a1f26..17db26c3 100644 --- a/FreeFileSync/Source/lib/dir_lock.cpp +++ b/FreeFileSync/Source/lib/dir_lock.cpp @@ -44,6 +44,9 @@ const int DETECT_ABANDONED_INTERVAL = 30; //assume abandoned lock; unit: [s] const char LOCK_FORMAT_DESCR[] = "FreeFileSync"; const int LOCK_FORMAT_VER = 2; //lock file format version + +using MemStreamOut = MemoryStreamOut<ByteArray>; +using MemStreamIn = MemoryStreamIn <ByteArray>; } //worker thread @@ -318,7 +321,7 @@ struct LockInformation //throw FileError sessionId = *sessionIdTmp; } - explicit LockInformation(BinStreamIn& stream) //throw UnexpectedEndOfStreamError + explicit LockInformation(MemStreamIn& stream) //throw UnexpectedEndOfStreamError { char tmp[sizeof(LOCK_FORMAT_DESCR)] = {}; readArray(stream, &tmp, sizeof(tmp)); //file format header @@ -335,7 +338,7 @@ struct LockInformation //throw FileError processId = static_cast<ProcessId>(readNumber<std::uint64_t>(stream)); //[!] conversion } - void toStream(BinStreamOut& stream) const //throw () + void toStream(MemStreamOut& stream) const //throw () { writeArray(stream, LOCK_FORMAT_DESCR, sizeof(LOCK_FORMAT_DESCR)); writeNumber<boost::int32_t>(stream, LOCK_FORMAT_VER); @@ -367,7 +370,7 @@ struct LockInformation //throw FileError LockInformation retrieveLockInfo(const Zstring& lockfilepath) //throw FileError { - BinStreamIn streamIn = loadBinStream<BinaryStream>(lockfilepath, nullptr); //throw FileError + MemStreamIn streamIn = loadBinStream<ByteArray>(lockfilepath, nullptr); //throw FileError try { return LockInformation(streamIn); //throw UnexpectedEndOfStreamError @@ -568,11 +571,11 @@ bool tryLock(const Zstring& lockfilepath) //throw FileError #endif //write housekeeping info: user, process info, lock GUID - BinaryStream binStream = [&] + ByteArray binStream = [&] { - BinStreamOut streamOut; + MemStreamOut streamOut; LockInformation(FromCurrentProcess()).toStream(streamOut); - return streamOut.get(); + return streamOut.ref(); }(); if (!binStream.empty()) @@ -661,7 +664,7 @@ private: LockAdmin& operator=(const LockAdmin&) = delete; typedef std::string UniqueId; - typedef std::map<Zstring, UniqueId, LessFilename> FileToGuidMap; //n:1 handle uppper/lower case correctly + typedef std::map<Zstring, UniqueId, LessFilePath> FileToGuidMap; //n:1 handle uppper/lower case correctly typedef std::map<UniqueId, std::weak_ptr<SharedDirLock>> GuidToLockMap; //1:1 std::shared_ptr<SharedDirLock> getActiveLock(const UniqueId& lockId) //returns null if none found diff --git a/FreeFileSync/Source/lib/ffs_paths.cpp b/FreeFileSync/Source/lib/ffs_paths.cpp index 7b5b6363..1c58ce28 100644 --- a/FreeFileSync/Source/lib/ffs_paths.cpp +++ b/FreeFileSync/Source/lib/ffs_paths.cpp @@ -107,9 +107,9 @@ Zstring zen::getConfigDir() if (!dirExists(userDirectory)) try { - makeDirectory(userDirectory); //throw FileError + makeNewDirectory(userDirectory); //throw FileError } - catch (const FileError&) {} + catch (const FileError&) { assert(false); } return appendSeparator(userDirectory); } diff --git a/FreeFileSync/Source/lib/generate_logfile.h b/FreeFileSync/Source/lib/generate_logfile.h index e77aa11e..6bae0a1c 100644 --- a/FreeFileSync/Source/lib/generate_logfile.h +++ b/FreeFileSync/Source/lib/generate_logfile.h @@ -8,10 +8,11 @@ #define GEN_LOGFILE_H_93172643216748973216458732165415 #include <zen/error_log.h> -#include <zen/file_io.h> +//#include <zen/file_io.h> #include <zen/serialize.h> #include <zen/format_unit.h> #include "ffs_paths.h" +#include "../fs/abstract.h" namespace zen @@ -43,26 +44,20 @@ Zstring getLastSyncsLogfilePath(); struct OnUpdateLogfileStatusNoThrow { - OnUpdateLogfileStatusNoThrow(ProcessCallback& pc, const Zstring& logfilePath) : pc_(pc), logfilePath_(logfilePath), bytesWritten(), - msg(replaceCpy(_("Saving file %x..."), L"%x", fmtFileName(logfilePath_))) {} + OnUpdateLogfileStatusNoThrow(ProcessCallback& pc, const Zstring& logfileDisplayPath) : pc_(pc), logfileDisplayPath_(logfileDisplayPath), + msg(replaceCpy(_("Saving file %x..."), L"%x", fmtFileName(logfileDisplayPath_))) {} void operator()(std::int64_t bytesDelta) { bytesWritten += bytesDelta; - - if (updateUiIsAllowed()) //test if specific time span between ui updates is over - try - { - pc_.reportStatus(msg + L" (" + filesizeToShortString(bytesWritten) + L")"); //throw? - pc_.forceUiRefresh(); //throw? - } - catch (...) {} + try { pc_.reportStatus(msg + L" (" + filesizeToShortString(bytesWritten) + L")"); /*throw X*/ } + catch (...) {} } private: ProcessCallback& pc_; - const Zstring logfilePath_; - std::int64_t bytesWritten; + const Zstring logfileDisplayPath_; + std::int64_t bytesWritten = 0; const std::wstring msg; }; @@ -126,24 +121,34 @@ std::wstring generateLogHeader(const SummaryInfo& s) inline void saveLogToFile(const SummaryInfo& summary, //throw FileError const ErrorLog& log, - FileOutput& fileOut, + ABF::OutputStream& streamOut, const std::function<void(std::int64_t bytesDelta)>& onUpdateSaveStatus) { - Utf8String header = utfCvrtTo<Utf8String>(generateLogHeader(summary)); - replace(header, '\n', LINE_BREAK); //don't replace line break any earlier - header += LINE_BREAK; //make sure string is not empty! + //write log items in blocks instead of creating one big string: memory allocation might fail; think 1 million entries! + const size_t blockSize = streamOut.optimalBlockSize(); + Utf8String msgBuffer; - fileOut.write(&*header.begin(), header.size()); //throw FileError + auto flushToFile = [&] + { + streamOut.write(&*msgBuffer.begin(), msgBuffer.size()); //throw FileError + if (onUpdateSaveStatus) + onUpdateSaveStatus(msgBuffer.size()); + msgBuffer.clear(); + }; + + msgBuffer += replaceCpy(utfCvrtTo<Utf8String>(generateLogHeader(summary)), '\n', LINE_BREAK); //don't replace line break any earlier + msgBuffer += LINE_BREAK; - //write log items one after the other instead of creating one big string: memory allocation might fail; think 1 million entries! for (const LogEntry& entry : log) { - Utf8String msg = replaceCpy(utfCvrtTo<Utf8String>(formatMessage<std::wstring>(entry)), '\n', LINE_BREAK); - msg += LINE_BREAK; //make sure string is not empty! + msgBuffer += replaceCpy(utfCvrtTo<Utf8String>(formatMessage<std::wstring>(entry)), '\n', LINE_BREAK); + msgBuffer += LINE_BREAK; //=> string is not empty! - fileOut.write(&*msg.begin(), msg.size()); //throw FileError - if (onUpdateSaveStatus) onUpdateSaveStatus(msg.size()); + if (msgBuffer.size() > blockSize) + flushToFile(); } + if (!msgBuffer.empty()) + flushToFile(); } @@ -152,7 +157,7 @@ Zstring getLastSyncsLogfilePath() { return getConfigDir() + Zstr("LastSyncs.log" inline -void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError +void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError const ErrorLog& log, size_t maxBytesToWrite, //log may be *huge*, e.g. 1 million items; LastSyncs.log *must not* create performance problems! const std::function<void(std::int64_t bytesDelta)>& onUpdateSaveStatus) @@ -180,14 +185,14 @@ void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError //fill up the rest of permitted space by appending old log if (newStream.size() < maxBytesToWrite) { - std::function<void(std::int64_t bytesDelta)> onUpdateLoadStatus; - if (onUpdateSaveStatus) - onUpdateLoadStatus = [&](std::int64_t bytesDelta) { onUpdateSaveStatus(0); }; + //std::function<void(std::int64_t bytesDelta)> onUpdateLoadStatus; + //if (onUpdateSaveStatus) + // onUpdateLoadStatus = [&](std::int64_t bytesDelta) { onUpdateSaveStatus(0); }; Utf8String oldStream; try { - oldStream = loadBinStream<Utf8String>(filepath, onUpdateLoadStatus); //throw FileError + oldStream = loadBinStream<Utf8String>(filepath, onUpdateSaveStatus); //throw FileError } catch (FileError&) {} diff --git a/FreeFileSync/Source/lib/hard_filter.cpp b/FreeFileSync/Source/lib/hard_filter.cpp index ad8398ae..d74f349e 100644 --- a/FreeFileSync/Source/lib/hard_filter.cpp +++ b/FreeFileSync/Source/lib/hard_filter.cpp @@ -336,10 +336,8 @@ bool NameFilter::passDirFilter(const Zstring& relDirPath, bool* subObjMightMatch bool NameFilter::isNull(const Zstring& includePhrase, const Zstring& excludePhrase) { - Zstring include = includePhrase; - Zstring exclude = excludePhrase; - trim(include); - trim(exclude); + const Zstring include = trimCpy(includePhrase); + const Zstring exclude = trimCpy(excludePhrase); return include == Zstr("*") && exclude.empty(); //return NameFilter(includePhrase, excludePhrase).isNull(); -> very expensive for huge lists diff --git a/FreeFileSync/Source/lib/icon_buffer.cpp b/FreeFileSync/Source/lib/icon_buffer.cpp index f6c72f79..b9e8e761 100644 --- a/FreeFileSync/Source/lib/icon_buffer.cpp +++ b/FreeFileSync/Source/lib/icon_buffer.cpp @@ -5,26 +5,18 @@ // ************************************************************************** #include "icon_buffer.h" -#include <queue> #include <set> #include <zen/thread.h> //includes <boost/thread.hpp> #include <zen/scope_guard.h> #include <wx+/image_resources.h> +#include "icon_loader.h" #ifdef ZEN_WIN - #include <zen/dll.h> #include <zen/win_ver.h> - #include <wx/image.h> - #include "../dll/Thumbnail/thumbnail.h" - -#elif defined ZEN_LINUX - #include <gtk/gtk.h> - -#elif defined ZEN_MAC - #include "osx_file_icon.h" #endif using namespace zen; +using ABF = AbstractBaseFolder; namespace @@ -36,239 +28,32 @@ const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to hold in buffer: #endif #ifdef ZEN_WIN -const bool isXpOrLater = winXpOrLater(); //VS2010 compiled DLLs are not supported on Win 2000: Popup dialog "DecodePointer not found" - -#define DEF_DLL_FUN(name) const auto name = isXpOrLater ? DllFun<thumb::FunType_##name>(thumb::getDllName(), thumb::funName_##name) : DllFun<thumb::FunType_##name>(); -DEF_DLL_FUN(getIconByIndex); // -DEF_DLL_FUN(getThumbnail); //let's spare the boost::call_once hustle and allocate statically -DEF_DLL_FUN(releaseImageData); // -#undef DEF_DLL_FUN - -#elif defined ZEN_LINUX -wxImage getImageFromPixBuf(const GdkPixbuf* pixbuf) -{ - //see: https://developer.gnome.org/gdk-pixbuf/stable/gdk-pixbuf-The-GdkPixbuf-Structure.html - if (pixbuf && - ::gdk_pixbuf_get_colorspace(pixbuf) == GDK_COLORSPACE_RGB && - ::gdk_pixbuf_get_bits_per_sample(pixbuf) == 8) - { - const int width = ::gdk_pixbuf_get_width (pixbuf); - const int height = ::gdk_pixbuf_get_height(pixbuf); - const int channels = ::gdk_pixbuf_get_n_channels(pixbuf); - const int stride = ::gdk_pixbuf_get_rowstride(pixbuf); - const unsigned char* rgbaSrc = ::gdk_pixbuf_get_pixels(pixbuf); - wxImage image(width, height); - unsigned char* rgb = image.GetData(); - - if (channels == 3) - { - assert(!::gdk_pixbuf_get_has_alpha(pixbuf)); - - for (int y = 0; y < height; y++) - { - const unsigned char* srcLine = rgbaSrc + y * stride; - for (int x = 0; x < width; x++) - { - *rgb++ = *srcLine++; - *rgb++ = *srcLine++; - *rgb++ = *srcLine++; - } - } - return image; - } - else if (channels == 4) - { - assert(::gdk_pixbuf_get_has_alpha(pixbuf)); - image.SetAlpha(); - unsigned char* alpha = image.GetAlpha(); - - for (int y = 0; y < height; y++) - { - const unsigned char* srcLine = rgbaSrc + y * stride; - for (int x = 0; x < width; x++) - { - *rgb++ = *srcLine++; - *rgb++ = *srcLine++; - *rgb++ = *srcLine++; - *alpha++ = *srcLine++; - } - } - return image; - } - } - assert(false); - return wxImage(); -} -#endif - - -class IconHolder //handle HICON/GdkPixbuf ownership supporting thread-safe usage (in contrast to wxIcon/wxBitmap) -{ -public: -#ifdef ZEN_WIN - typedef const thumb::ImageData* HandleType; -#elif defined ZEN_LINUX - typedef GdkPixbuf* HandleType; -#elif defined ZEN_MAC - typedef osx::ImageData* HandleType; -#endif - - explicit IconHolder(HandleType handle = nullptr) : handle_(handle) {} //take ownership! - - IconHolder(IconHolder&& tmp) : handle_(tmp.release()) {} - - IconHolder& operator=(IconHolder other) //unifying assignment - { - other.swap(*this); - return *this; - } - - ~IconHolder() - { - if (handle_ != nullptr) -#ifdef ZEN_WIN - releaseImageData(handle_); //should be checked already before creating IconHolder! -#elif defined ZEN_LINUX - ::g_object_unref(handle_); //superseedes "::gdk_pixbuf_unref"! -#elif defined ZEN_MAC - delete handle_; -#endif - } - - HandleType release() - { - ZEN_ON_SCOPE_EXIT(handle_ = nullptr); - return handle_; - } - - void swap(IconHolder& other) { std::swap(handle_, other.handle_); } //throw() - - //destroys raw icon! Call from GUI thread only! - wxBitmap extractWxBitmap() - { - ZEN_ON_SCOPE_EXIT(assert(!*this)); - assert(boost::this_thread::get_id() == mainThreadId); - - if (!handle_) - return wxNullBitmap; - -#ifdef ZEN_WIN - ZEN_ON_SCOPE_EXIT(IconHolder().swap(*this)); //destroy after extraction - - //let wxImage reference data without taking ownership: - wxImage fileIcon(handle_->width, handle_->height, handle_->rgb, true); - fileIcon.SetAlpha(handle_->alpha, true); - return wxBitmap(fileIcon); - -#elif defined ZEN_LINUX - //ZEN_ON_SCOPE_EXIT(IconHolder().swap(*this)); //destroy after extraction - //return getImageFromPixBuf(handle_); - - return wxBitmap(release()); //ownership passed! - -#elif defined ZEN_MAC - ZEN_ON_SCOPE_EXIT(IconHolder().swap(*this)); //destroy after extraction - - //let wxImage reference data without taking ownership: - assert(!handle_->rgb.empty() && !handle_->alpha.empty()); - if (!handle_->rgb.empty()) - { - wxImage fileIcon(handle_->width, handle_->height, &handle_->rgb[0], true); - if (!handle_->alpha.empty()) - fileIcon.SetAlpha(&handle_->alpha[0], true); - return wxBitmap(fileIcon); - } - return wxBitmap(); + const bool wereVistaOrLater = vistaOrLater(); #endif - } -private: - HandleType handle_; - IconHolder(const IconHolder& other); //move semantics! - struct ConversionToBool { int dummy; }; -public: - //use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20) - operator int ConversionToBool::* () const { return handle_ != nullptr ? &ConversionToBool::dummy : nullptr; } -}; - - -#if defined ZEN_WIN || defined ZEN_LINUX -Zstring getFileExtension(const Zstring& filepath) +//destroys raw icon! Call from GUI thread only! +wxBitmap extractWxBitmap(ImageHolder&& ih) { - const Zstring shortName = afterLast(filepath, FILE_NAME_SEPARATOR); //returns the whole string if term not found - - return contains(shortName, Zchar('.')) ? - afterLast(filepath, Zchar('.')) : - Zstring(); -} + assert(boost::this_thread::get_id() == mainThreadId); +#ifndef NDEBUG + auto check = [&] { assert(!ih); }; //work around V120_XP compilation issue + ZEN_ON_SCOPE_EXIT(check()); #endif + if (!ih.getRgb()) + return wxNullBitmap; -#ifdef ZEN_WIN -const bool wereVistaOrLater = vistaOrLater(); - - -thumb::IconSizeType getThumbSizeType(IconBuffer::IconSize sz) -{ - //coordinate with IconBuffer::getSize()! - using namespace thumb; - switch (sz) - { - case IconBuffer::SIZE_SMALL: - return ICON_SIZE_16; - case IconBuffer::SIZE_MEDIUM: - if (!wereVistaOrLater) return ICON_SIZE_32; //48x48 doesn't look sharp on XP - return ICON_SIZE_48; - case IconBuffer::SIZE_LARGE: - return ICON_SIZE_128; - } - return ICON_SIZE_16; + //let wxImage take ownership: + wxImage img(ih.getWidth(), ih.getHeight(), ih.releaseRgb(), false /*static_data*/); + if (ih.getAlpha()) + img.SetAlpha(ih.releaseAlpha(), false); + return wxBitmap(img); } -IconHolder getIconByAttribute(LPCWSTR pszPath, DWORD dwFileAttributes, IconBuffer::IconSize sz) -{ - //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! - SHFILEINFO fileInfo = {}; //initialize hIcon - DWORD_PTR imgList = ::SHGetFileInfo(pszPath, //Windows 7 doesn't like this parameter to be an empty string - dwFileAttributes, - &fileInfo, - sizeof(fileInfo), - SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES); - if (!imgList) //no need to IUnknown::Release() imgList! - return IconHolder(); - - if (getIconByIndex && releaseImageData) - return IconHolder(getIconByIndex(fileInfo.iIcon, getThumbSizeType(sz))); - - return IconHolder(); -} - - -IconHolder getAssociatedIconByExt(const Zstring& extension, IconBuffer::IconSize sz) -{ - //no read-access to disk! determine icon by extension - return getIconByAttribute((L"dummy." + extension).c_str(), FILE_ATTRIBUTE_NORMAL, sz); -} - -#elif defined ZEN_LINUX -IconHolder iconHolderFromGicon(GIcon* gicon, IconBuffer::IconSize sz) -{ - if (gicon) - if (GtkIconTheme* defaultTheme = ::gtk_icon_theme_get_default()) //not owned! - if (GtkIconInfo* iconInfo = ::gtk_icon_theme_lookup_by_gicon(defaultTheme, gicon, IconBuffer::getSize(sz), GTK_ICON_LOOKUP_USE_BUILTIN)) //this may fail if icon is not installed on system - { - ZEN_ON_SCOPE_EXIT(::gtk_icon_info_free(iconInfo);) - if (GdkPixbuf* pixBuf = ::gtk_icon_info_load_icon(iconInfo, nullptr)) - return IconHolder(pixBuf); //pass ownership - } - return IconHolder(); -} -#endif - #ifdef ZEN_WIN -std::set<Zstring, LessFilename> customIconExt //function-scope statics are not (yet) thread-safe in VC12 +std::set<Zstring, LessFilePath> customIconExt //function-scope statics are not (yet) thread-safe in VC12 { L"ani", L"cur", @@ -277,7 +62,7 @@ std::set<Zstring, LessFilename> customIconExt //function-scope statics are not ( L"msc", L"scr" }; -std::set<Zstring, LessFilename> linkExt +std::set<Zstring, LessFilePath> linkExt { L"lnk", L"pif", @@ -286,107 +71,20 @@ std::set<Zstring, LessFilename> linkExt }; //test for extension for non-thumbnail icons that can have a stock icon which does not have to be physically read from disc -bool isStandardIconExtension(const Zstring& extension) +inline +bool hasStandardIconExtension(const Zstring& filePath) { + const Zstring extension(getFileExtension(filePath)); + return customIconExt.find(extension) == customIconExt.end() && linkExt.find(extension) == linkExt.end(); } #endif } -bool zen::hasLinkExtension(const Zstring& filepath) -{ -#ifdef ZEN_WIN - const Zstring& extension = getFileExtension(filepath); - return linkExt.find(extension) != linkExt.end(); -#elif defined ZEN_LINUX - const Zstring& extension = getFileExtension(filepath); - return extension == "desktop"; -#elif defined ZEN_MAC - return false; //alias files already get their arrow icon via "NSWorkspace::iconForFile" -#endif -} - //################################################################################################################################################ -IconHolder getThumbnailImage(const Zstring& filepath, int requestedSize) //return 0 on failure -{ -#ifdef ZEN_WIN - if (getThumbnail && releaseImageData) - return IconHolder(getThumbnail(filepath.c_str(), requestedSize)); - -#elif defined ZEN_LINUX - struct ::stat fileInfo = {}; - if (::stat(filepath.c_str(), &fileInfo) == 0) - if (!S_ISFIFO(fileInfo.st_mode)) //skip named pipes: else gdk_pixbuf_get_file_info() would hang forever! - { - gint width = 0; - gint height = 0; - if (GdkPixbufFormat* fmt = ::gdk_pixbuf_get_file_info(filepath.c_str(), &width, &height)) - { - (void)fmt; - if (width > 0 && height > 0 && requestedSize > 0) - { - int trgWidth = width; - int trgHeight = height; - - const int maxExtent = std::max(width, height); //don't stretch small images, but shrink large ones instead! - if (requestedSize < maxExtent) - { - trgWidth = width * requestedSize / maxExtent; - trgHeight = height * requestedSize / maxExtent; - } - if (GdkPixbuf* pixBuf = ::gdk_pixbuf_new_from_file_at_size(filepath.c_str(), trgWidth, trgHeight, nullptr)) - return IconHolder(pixBuf); //pass ownership - } - } - } - -#elif defined ZEN_MAC - try - { - return IconHolder(new osx::ImageData(osx::getThumbnail(filepath.c_str(), requestedSize))); //throw SysError - } - catch (zen::SysError&) {} -#endif - return IconHolder(); -} - - -IconHolder getGenericFileIcon(IconBuffer::IconSize sz) -{ - //we're called by getAssociatedIcon()! -> avoid endless recursion! -#ifdef ZEN_WIN - return getIconByAttribute(L"dummy", FILE_ATTRIBUTE_NORMAL, sz); - -#elif defined ZEN_LINUX - const char* mimeFileIcons[] = - { - "application-x-zerosize", //Kubuntu: /usr/share/icons/oxygen/48x48/mimetypes - "text-x-generic", //http://live.gnome.org/GnomeArt/Tutorials/IconThemes - "empty", //Ubuntu: /usr/share/icons/Humanity/mimes/48 - GTK_STOCK_FILE, //"gtk-file", - "gnome-fs-regular", // - }; - - if (GtkIconTheme* defaultTheme = gtk_icon_theme_get_default()) //not owned! - for (auto it = std::begin(mimeFileIcons); it != std::end(mimeFileIcons); ++it) - if (GdkPixbuf* pixBuf = gtk_icon_theme_load_icon(defaultTheme, *it, IconBuffer::getSize(sz), GTK_ICON_LOOKUP_USE_BUILTIN, nullptr)) - return IconHolder(pixBuf); //pass ownership - return IconHolder(); - -#elif defined ZEN_MAC - try - { - return IconHolder(new osx::ImageData(osx::getDefaultFileIcon(IconBuffer::getSize(sz)))); //throw SysError - } - catch (zen::SysError&) {} - return IconHolder(); -#endif -} - - -IconHolder getAssociatedIcon(const Zstring& filepath, IconBuffer::IconSize sz) +ImageHolder getDisplayIcon(const ABF::IconLoader& iconLoader, const Zstring& templateName, IconBuffer::IconSize sz) { //1. try to load thumbnails switch (sz) @@ -395,109 +93,91 @@ IconHolder getAssociatedIcon(const Zstring& filepath, IconBuffer::IconSize sz) break; case IconBuffer::SIZE_MEDIUM: case IconBuffer::SIZE_LARGE: - if (IconHolder ico = getThumbnailImage(filepath, IconBuffer::getSize(sz))) - return ico; + if (iconLoader.getThumbnailImage) + if (ImageHolder img = iconLoader.getThumbnailImage(IconBuffer::getSize(sz))) + return img; //else: fallback to non-thumbnail icon break; } //2. retrieve file icons #ifdef ZEN_WIN - //perf: optimize fallback case for SIZE_MEDIUM and SIZE_LARGE: - const Zstring& extension = getFileExtension(filepath); - if (isStandardIconExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - return getAssociatedIconByExt(extension, sz); - //SIZE_MEDIUM or SIZE_LARGE: result will buffered under full filepath, not extension; this is okay: failure to load thumbnail is independent from extension in general! - - SHFILEINFO fileInfo = {}; - if (DWORD_PTR imgList = ::SHGetFileInfo(filepath.c_str(), //_In_ LPCTSTR pszPath, -> note: ::SHGetFileInfo() can't handle \\?\-prefix! - 0, //DWORD dwFileAttributes, - &fileInfo, //_Inout_ SHFILEINFO *psfi, - sizeof(fileInfo), //UINT cbFileInfo, - SHGFI_SYSICONINDEX /*| SHGFI_ATTRIBUTES*/)) //UINT uFlags - { - (void)imgList; - //imgList->Release(); //empiric study: crash on XP if we release this! Seems we do not own it... -> also no GDI leak on Win7 -> okay - //another comment on http://msdn.microsoft.com/en-us/library/bb762179(v=VS.85).aspx describes exact same behavior on Win7/XP - - //Quote: "The IImageList pointer type, such as that returned in the ppv parameter, can be cast as an HIMAGELIST as needed; - // for example, for use in a list view. Conversely, an HIMAGELIST can be cast as a pointer to an IImageList." - //http://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx - - //Check for link icon type (= shell links and symlinks): SHGetFileInfo + SHGFI_ATTRIBUTES: - //const bool isLink = (fileInfo.dwAttributes & SFGAO_LINK) != 0; - - if (getIconByIndex && releaseImageData) - if (const thumb::ImageData* imgData = getIconByIndex(fileInfo.iIcon, getThumbSizeType(sz))) - return IconHolder(imgData); - } - -#elif defined ZEN_LINUX - GFile* file = ::g_file_new_for_path(filepath.c_str()); //documented to "never fail" - ZEN_ON_SCOPE_EXIT(::g_object_unref(file)); + //result will be buffered under full filepath, not extension; this is okay: failure to load thumbnail is independent from extension in general! + if (!hasStandardIconExtension(templateName)) //"pricey" extensions are stored with full path and are read from disk, while cheap ones require just the extension +#endif + if (iconLoader.getFileIcon) + if (ImageHolder img = iconLoader.getFileIcon(IconBuffer::getSize(sz))) + return img; - if (GFileInfo* fileInfo = ::g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_ICON, G_FILE_QUERY_INFO_NONE, nullptr, nullptr)) - { - ZEN_ON_SCOPE_EXIT(::g_object_unref(fileInfo)); - if (GIcon* gicon = ::g_file_info_get_icon(fileInfo)) //not owned! - return iconHolderFromGicon(gicon, sz); - } - //need fallback: icon lookup may fail because some icons are currently not present on system + //3. fallbacks + if (ImageHolder img = getIconByTemplatePath(templateName, IconBuffer::getSize(sz))) + return img; -#elif defined ZEN_MAC - try - { - return IconHolder(new osx::ImageData(osx::getFileIcon(filepath.c_str(), IconBuffer::getSize(sz)))); //throw SysError - } - catch (zen::SysError&) { assert(false); } -#endif - return ::getGenericFileIcon(sz); //make sure this does not internally call getAssociatedIcon("someDefaultFile.txt")!!! => endless recursion! + return genericFileIcon(IconBuffer::getSize(sz)); } //################################################################################################################################################ //---------------------- Shared Data ------------------------- +struct WorkItem +{ + WorkItem(const AbstractPathRef::ItemId& id, const ABF::IconLoader& iconLoader, const Zstring& fileName) : id_(id), iconLoader_(iconLoader), fileName_(fileName) {} + + AbstractPathRef::ItemId id_; //async icon loading => avoid any dangling references!!! + ABF::IconLoader iconLoader_; //THREAD-SAFETY: thread-safe like an int! + Zstring fileName_; //template name for use as fallback icon +}; + + class WorkLoad { public: - Zstring extractNextFile() //context of worker thread, blocking + WorkItem extractNextFile() //context of worker thread, blocking { assert(boost::this_thread::get_id() != mainThreadId); boost::unique_lock<boost::mutex> dummy(lockFiles); - while (filesToLoad.empty()) - conditionNewFiles.timed_wait(dummy, boost::posix_time::milliseconds(100)); //interruption point! + while (workLoad.empty()) + conditionNewWork.timed_wait(dummy, boost::posix_time::milliseconds(100)); //interruption point! - Zstring filepath = filesToLoad.back(); // - filesToLoad.pop_back(); // - return filepath; //yes, not std::bad_alloc exception-safe, but bad_alloc is not relevant for us + WorkItem workItem = workLoad.back(); // + workLoad.pop_back(); //yes, not std::bad_alloc exception-safe, but bad_alloc is not relevant for us + return workItem; // } - void setWorkload(const std::vector<Zstring>& newLoad) //context of main thread + void setWorkload(const std::vector<AbstractPathRef>& newLoad) //context of main thread { assert(boost::this_thread::get_id() == mainThreadId); { boost::lock_guard<boost::mutex> dummy(lockFiles); - filesToLoad = newLoad; + + workLoad.clear(); + for (const AbstractPathRef& filePath : newLoad) + workLoad.emplace_back(filePath.getUniqueId(), + ABF::getAsyncIconLoader(filePath), //noexcept! + ABF::getFileShortName(filePath)); // } - conditionNewFiles.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 + conditionNewWork.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref } - void addToWorkload(const Zstring& newEntry) //context of main thread + void addToWorkload(const AbstractPathRef& filePath) //context of main thread { assert(boost::this_thread::get_id() == mainThreadId); { boost::lock_guard<boost::mutex> dummy(lockFiles); - filesToLoad.push_back(newEntry); //set as next item to retrieve + + workLoad.emplace_back(filePath.getUniqueId(), //set as next item to retrieve + ABF::getAsyncIconLoader(filePath), //noexcept! + ABF::getFileShortName(filePath)); // } - conditionNewFiles.notify_all(); + conditionNewWork.notify_all(); } private: - std::vector<Zstring> filesToLoad; //processes last elements of vector first! + std::vector<WorkItem> workLoad; //processes last elements of vector first! boost::mutex lockFiles; - boost::condition_variable conditionNewFiles; //signal event: data for processing available + boost::condition_variable conditionNewWork; //signal event: data for processing available }; @@ -507,19 +187,19 @@ public: Buffer() : firstInsertPos(iconList.end()), lastInsertPos(iconList.end()) {} //called by main and worker thread: - bool hasIcon(const Zstring& filepath) const + bool hasIcon(const AbstractPathRef::ItemId& id) const { boost::lock_guard<boost::mutex> dummy(lockIconList); - return iconList.find(filepath) != iconList.end(); + return iconList.find(id) != iconList.end(); } //must be called by main thread only! => wxBitmap is NOT thread-safe like an int (non-atomic ref-count!!!) - Opt<wxBitmap> retrieve(const Zstring& filepath) + Opt<wxBitmap> retrieve(const AbstractPathRef::ItemId& id) { assert(boost::this_thread::get_id() == mainThreadId); boost::lock_guard<boost::mutex> dummy(lockIconList); - auto it = iconList.find(filepath); + auto it = iconList.find(id); if (it == iconList.end()) return NoValue(); @@ -528,21 +208,21 @@ public: IconData& idata = refData(it); if (idata.iconRaw) //if not yet converted... { - idata.iconFmt = make_unique<wxBitmap>(idata.iconRaw.extractWxBitmap()); //convert in main thread! + idata.iconFmt = make_unique<wxBitmap>(extractWxBitmap(std::move(idata.iconRaw))); //convert in main thread! assert(!idata.iconRaw); } return idata.iconFmt ? *idata.iconFmt : wxNullBitmap; //idata.iconRaw may be inserted as empty from worker thread! } //called by main and worker thread: - void insert(const Zstring& entryName, IconHolder&& icon) + void insert(const AbstractPathRef::ItemId& id, ImageHolder&& icon) { boost::lock_guard<boost::mutex> dummy(lockIconList); - //thread safety: moving IconHolder is free from side effects, but ~wxBitmap() is NOT! => do NOT delete items from iconList here! - auto rc = iconList.emplace(entryName, makeValueObject()); - assert(rc.second); - if (rc.second) //if insertion took place + //thread safety: moving ImageHolder is free from side effects, but ~wxBitmap() is NOT! => do NOT delete items from iconList here! + auto rc = iconList.emplace(id, makeValueObject()); + assert(rc.second); //insertion took place + if (rc.second) { refData(rc.first).iconRaw = std::move(icon); priorityListPushBack(rc.first); @@ -568,11 +248,11 @@ private: struct IconData; #ifdef __clang__ //workaround libc++ limitation for incomplete types: http://llvm.org/bugs/show_bug.cgi?id=17701 - typedef std::map<Zstring, std::unique_ptr<IconData>, LessFilename> FileIconMap; + typedef std::map<AbstractPathRef::ItemId, std::unique_ptr<IconData>> FileIconMap; static IconData& refData(FileIconMap::iterator it) { return *(it->second); } static std::unique_ptr<IconData> makeValueObject() { return make_unique<IconData>(); } #else - typedef std::map<Zstring, IconData, LessFilename> FileIconMap; + typedef std::map<AbstractPathRef::ItemId, IconData> FileIconMap; IconData& refData(FileIconMap::iterator it) { return it->second; } static IconData makeValueObject() { return IconData(); } #endif @@ -639,7 +319,7 @@ private: IconData() {} IconData(IconData&& tmp) : iconRaw(std::move(tmp.iconRaw)), iconFmt(std::move(tmp.iconFmt)), prev_(tmp.prev_), next_(tmp.next_) {} - IconHolder iconRaw; //native icon representation: may be used by any thread + ImageHolder iconRaw; //native icon representation: may be used by any thread std::unique_ptr<wxBitmap> iconFmt; //use ONLY from main thread! //wxBitmap is NOT thread-safe: non-atomic ref-count just to begin with... @@ -678,36 +358,45 @@ private: }; -void WorkerThread::operator()() //thread entry +class RunOnStartup { - //failure to initialize COM for each thread is a source of hard to reproduce bugs: https://sourceforge.net/tracker/?func=detail&aid=3160472&group_id=234430&atid=1093080 +public: + RunOnStartup() + { #ifdef ZEN_WIN - //Prerequisites, see thumbnail.h + //failure to initialize COM for each thread is a source of hard to reproduce bugs: https://sourceforge.net/tracker/?func=detail&aid=3160472&group_id=234430&atid=1093080 + //Prerequisites, see thumbnail.h: Initialize system image list + + typedef BOOL (WINAPI* FileIconInitFun)(BOOL fRestoreCache); + const SysDllFun<FileIconInitFun> fileIconInit(L"Shell32.dll", reinterpret_cast<LPCSTR>(660)); //MS requires and documents this magic number + assert(fileIconInit); + if (fileIconInit) + fileIconInit(false); //TRUE to restore the system image cache from disk; FALSE otherwise. +#endif + } +} dummy; + +void WorkerThread::operator()() //thread entry +{ +#ifdef ZEN_WIN //1. Initialize COM - if (FAILED(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) + if (FAILED(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) //may return S_FALSE, which is NOT failure, see MSDN { assert(false); return; } ZEN_ON_SCOPE_EXIT(::CoUninitialize()); - - //2. Initialize system image list - typedef BOOL (WINAPI* FileIconInitFun)(BOOL fRestoreCache); - const SysDllFun<FileIconInitFun> fileIconInit(L"Shell32.dll", reinterpret_cast<LPCSTR>(660)); //MS requires and documents this magic number - assert(fileIconInit); - if (fileIconInit) - fileIconInit(false); //TRUE to restore the system image cache from disk; FALSE otherwise. #endif while (true) { boost::this_thread::interruption_point(); - const Zstring filepath = workload_->extractNextFile(); //start work: blocks until next icon to load is retrieved + const WorkItem workItem = workload_->extractNextFile(); //start work: blocks until next icon to load is retrieved - if (!buffer_->hasIcon(filepath)) //perf: workload may contain duplicate entries? - buffer_->insert(filepath, getAssociatedIcon(filepath, iconSizeType)); + if (!buffer_->hasIcon(workItem.id_)) //perf: workload may contain duplicate entries? + buffer_->insert(workItem.id_, getDisplayIcon(workItem.iconLoader_, workItem.fileName_, iconSizeType)); } } @@ -734,15 +423,16 @@ IconBuffer::IconBuffer(IconSize sz) : pimpl(make_unique<Pimpl>()), iconSizeType( IconBuffer::~IconBuffer() { - setWorkload(std::vector<Zstring>()); //make sure interruption point is always reached! + setWorkload({}); //make sure interruption point is always reached! pimpl->worker.interrupt(); pimpl->worker.join(); //we assume precondition "worker.joinable()"!!! } -int IconBuffer::getSize(IconSize icoSize) +int IconBuffer::getSize(IconSize sz) { - switch (icoSize) + //coordinate with getThumbSizeType() and linkOverlayIcon()! + switch (sz) { case IconBuffer::SIZE_SMALL: #if defined ZEN_WIN || defined ZEN_MAC @@ -764,50 +454,38 @@ int IconBuffer::getSize(IconSize icoSize) } -bool IconBuffer::readyForRetrieval(const Zstring& filepath) +bool IconBuffer::readyForRetrieval(const AbstractPathRef& filePath) { #ifdef ZEN_WIN if (iconSizeType == IconBuffer::SIZE_SMALL) - if (isStandardIconExtension(getFileExtension(filepath))) + if (hasStandardIconExtension(ABF::getFileShortName(filePath))) return true; #endif - return pimpl->buffer->hasIcon(filepath); + return pimpl->buffer->hasIcon(filePath.getUniqueId()); } -Opt<wxBitmap> IconBuffer::retrieveFileIcon(const Zstring& filepath) +Opt<wxBitmap> IconBuffer::retrieveFileIcon(const AbstractPathRef& filePath) { #ifdef ZEN_WIN //perf: let's read icons which don't need file access right away! No async delay justified! + const Zstring fileName = ABF::getFileShortName(filePath); if (iconSizeType == IconBuffer::SIZE_SMALL) //non-thumbnail view, we need file type icons only! - { - const Zstring& extension = getFileExtension(filepath); - if (isStandardIconExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - { - if (Opt<wxBitmap> ico = pimpl->buffer->retrieve(extension)) - return ico; - - //make sure icon is in buffer, even if icon needs not be retrieved! - pimpl->buffer->insert(extension, getAssociatedIconByExt(extension, iconSizeType)); - - Opt<wxBitmap> ico = pimpl->buffer->retrieve(extension); - assert(ico); - return ico; - } - } + if (hasStandardIconExtension(fileName)) + return this->getIconByExtension(fileName); //buffered!!! #endif - if (Opt<wxBitmap> ico = pimpl->buffer->retrieve(filepath)) + if (Opt<wxBitmap> ico = pimpl->buffer->retrieve(filePath.getUniqueId())) return ico; //since this icon seems important right now, we don't want to wait until next setWorkload() to start retrieving - pimpl->workload->addToWorkload(filepath); + pimpl->workload->addToWorkload(filePath); pimpl->buffer->limitSize(); return NoValue(); } -void IconBuffer::setWorkload(const std::vector<Zstring>& load) +void IconBuffer::setWorkload(const std::vector<AbstractPathRef>& load) { assert(load.size() < BUFFER_SIZE_MAX / 2); @@ -816,32 +494,33 @@ void IconBuffer::setWorkload(const std::vector<Zstring>& load) } +wxBitmap IconBuffer::getIconByExtension(const Zstring& filePath) +{ + const Zstring& extension = getFileExtension(filePath); + const AbstractPathRef::ItemId extId(nullptr, extension); + + if (Opt<wxBitmap> ico = pimpl->buffer->retrieve(extId)) + return *ico; + + const Zstring& templateName(extension.empty() ? Zstr("file") : Zstr("file.") + extension); + //don't pass actual file name to getIconByTemplatePath(), e.g. "AUTHORS" has own mime type on Linux!!! + pimpl->buffer->insert(extId, getIconByTemplatePath(templateName, IconBuffer::getSize(iconSizeType))); + + Opt<wxBitmap> ico = pimpl->buffer->retrieve(extId); + assert(ico); + return *ico; +} + + wxBitmap IconBuffer::genericFileIcon(IconSize sz) { - return ::getGenericFileIcon(sz).extractWxBitmap(); + return extractWxBitmap(zen::genericFileIcon(IconBuffer::getSize(sz))); } wxBitmap IconBuffer::genericDirIcon(IconSize sz) { - return [sz] - { -#ifdef ZEN_WIN - return getIconByAttribute(L"dummy", //Windows 7 doesn't like this parameter to be an empty string! - FILE_ATTRIBUTE_DIRECTORY, sz); -#elif defined ZEN_LINUX - if (GIcon* dirIcon = ::g_content_type_get_icon("inode/directory")) //should contain fallback to GTK_STOCK_DIRECTORY ("gtk-directory") - return iconHolderFromGicon(dirIcon, sz); - return IconHolder(); - -#elif defined ZEN_MAC - try - { - return IconHolder(new osx::ImageData(osx::getDefaultFolderIcon(IconBuffer::getSize(sz)))); //throw SysError - } - catch (zen::SysError&) { return IconHolder(); } -#endif - }().extractWxBitmap(); + return extractWxBitmap(zen::genericDirIcon(IconBuffer::getSize(sz))); } @@ -850,24 +529,28 @@ wxBitmap IconBuffer::linkOverlayIcon(IconSize sz) //coordinate with IconBuffer::getSize()! return getResourceImage([sz] { - switch (sz) - { - case IconBuffer::SIZE_SMALL: -#if defined ZEN_WIN || defined ZEN_MAC - return L"link_16"; -#elif defined ZEN_LINUX - return L"link_24"; -#endif - case IconBuffer::SIZE_MEDIUM: -#ifdef ZEN_WIN - if (!wereVistaOrLater) return L"link_32"; -#endif - return L"link_48"; + const int pixelSize = IconBuffer::getSize(sz); - case IconBuffer::SIZE_LARGE: - return L"link_128"; - } - assert(false); + if (pixelSize >= 128) return L"link_128"; + if (pixelSize >= 48) return L"link_48"; + if (pixelSize >= 32) return L"link_32"; + if (pixelSize >= 24) return L"link_24"; return L"link_16"; }()); } + + +bool zen::hasLinkExtension(const Zstring& filepath) +{ +#ifdef ZEN_WIN + const Zstring& extension = getFileExtension(filepath); + return linkExt.find(extension) != linkExt.end(); + +#elif defined ZEN_LINUX + const Zstring& extension = getFileExtension(filepath); + return extension == "desktop"; + +#elif defined ZEN_MAC + return false; //alias files already get their arrow icon via "NSWorkspace::iconForFile" +#endif +} diff --git a/FreeFileSync/Source/lib/icon_buffer.h b/FreeFileSync/Source/lib/icon_buffer.h index ff049343..39cee425 100644 --- a/FreeFileSync/Source/lib/icon_buffer.h +++ b/FreeFileSync/Source/lib/icon_buffer.h @@ -12,6 +12,7 @@ #include <zen/zstring.h> #include <zen/optional.h> #include <wx/bitmap.h> +#include "../fs/abstract.h" namespace zen { @@ -31,10 +32,11 @@ public: static int getSize(IconSize sz); //expected and *maximum* icon size in pixel int getSize() const { return getSize(iconSizeType); } // - bool readyForRetrieval(const Zstring& filepath); - Opt<wxBitmap> retrieveFileIcon(const Zstring& filepath); //... and mark as hot + bool readyForRetrieval(const AbstractPathRef& filePath); + Opt<wxBitmap> retrieveFileIcon (const AbstractPathRef& filePath); //... and mark as hot + void setWorkload (const std::vector<AbstractPathRef>& load); //(re-)set new workload of icons to be retrieved; - void setWorkload(const std::vector<Zstring>& load); //(re-)set new workload of icons to be retrieved; + wxBitmap getIconByExtension(const Zstring& filePath); //...and add to buffer static wxBitmap genericFileIcon(IconSize sz); static wxBitmap genericDirIcon (IconSize sz); diff --git a/FreeFileSync/Source/lib/icon_holder.h b/FreeFileSync/Source/lib/icon_holder.h new file mode 100644 index 00000000..442f01c5 --- /dev/null +++ b/FreeFileSync/Source/lib/icon_holder.h @@ -0,0 +1,63 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef IMAGE_HOLDER_284578426342567457 +#define IMAGE_HOLDER_284578426342567457 + +#include <memory> + +//used by fs_abstract.h => check carefully before adding dependencies! + +namespace zen +{ +struct ImageHolder //prepare conversion to wxImage as much as possible while staying thread-safe (in contrast to wxIcon/wxBitmap) +{ + ImageHolder() {} + + ImageHolder(int w, int h, bool withAlpha) : //init with allocated memory + width(w), height(h), + rgb(static_cast<unsigned char*>(::malloc(width * height * 3))), + alpha(withAlpha ? static_cast<unsigned char*>(::malloc(width * height)) : nullptr) {} + + ImageHolder (const ImageHolder&) = delete; //move semantics only! + ImageHolder& operator=(const ImageHolder&) = delete; // + + ImageHolder(ImageHolder&& tmp) : width(tmp.width), //= default in C++14 + height(tmp.height), + rgb(tmp.rgb.release()), + alpha(tmp.alpha.release()) {} + + ImageHolder& operator=(ImageHolder&& tmp) //= default in C++14 + { + std::swap(width, tmp.width); + std::swap(height, tmp.height); + std::swap(rgb, tmp.rgb); + std::swap(alpha, tmp.alpha); + return *this; + } + + explicit operator bool() const { return rgb.get() != nullptr; } + + int getWidth () const { return width; } + int getHeight() const { return height; } + + unsigned char* getRgb () { return rgb .get(); } + unsigned char* getAlpha() { return alpha.get(); } + + unsigned char* releaseRgb () { return rgb .release(); } + unsigned char* releaseAlpha() { return alpha.release(); } + + struct CLibFree { void operator()(unsigned char* p) const { ::free(p); } }; //use malloc/free to allow direct move into wxImage! + +private: + int width = 0; + int height = 0; + std::unique_ptr<unsigned char, CLibFree> rgb; //optional + std::unique_ptr<unsigned char, CLibFree> alpha; // +}; +} + +#endif //IMAGE_HOLDER_284578426342567457
\ No newline at end of file diff --git a/FreeFileSync/Source/lib/icon_loader.cpp b/FreeFileSync/Source/lib/icon_loader.cpp new file mode 100644 index 00000000..80836b52 --- /dev/null +++ b/FreeFileSync/Source/lib/icon_loader.cpp @@ -0,0 +1,368 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "icon_loader.h" +#include <zen/scope_guard.h> + +#ifdef ZEN_WIN + #include <zen/dll.h> + #include <zen/win_ver.h> + #include "../dll/Thumbnail/thumbnail.h" + +#elif defined ZEN_LINUX + #include <gtk/gtk.h> + #include <sys/stat.h> + +#elif defined ZEN_MAC + #include "osx_file_icon.h" +#endif + +using namespace zen; + + +namespace +{ +#ifdef ZEN_WIN + const bool isXpOrLater = winXpOrLater(); //VS2010 compiled DLLs are not supported on Win 2000: Popup dialog "DecodePointer not found" + + #define DEF_DLL_FUN(name) const auto name = isXpOrLater ? DllFun<thumb::FunType_##name>(thumb::getDllName(), thumb::funName_##name) : DllFun<thumb::FunType_##name>(); + DEF_DLL_FUN(getIconByIndex); // + DEF_DLL_FUN(getThumbnail); //let's spare the boost::call_once hustle and allocate statically + DEF_DLL_FUN(releaseImageData); // + #undef DEF_DLL_FUN +#endif + + +#ifdef ZEN_WIN +ImageHolder copyToImageHolder(const thumb::ImageData& img) +{ + assert(img.rgb && img.alpha); + if (!img.rgb) + return ImageHolder(); + + ImageHolder out(img.width, img.height, img.alpha != nullptr); + ::memcpy(out.getRgb(), img.rgb, img.width * img.height * 3); + + if (img.alpha) + ::memcpy(out.getAlpha(), img.alpha, img.width * img.height); + + return out; +} + +#elif defined ZEN_LINUX +ImageHolder copyToImageHolder(const GdkPixbuf* pixbuf) +{ + //see: https://developer.gnome.org/gdk-pixbuf/stable/gdk-pixbuf-The-GdkPixbuf-Structure.html + if (pixbuf && + ::gdk_pixbuf_get_colorspace(pixbuf) == GDK_COLORSPACE_RGB && + ::gdk_pixbuf_get_bits_per_sample(pixbuf) == 8) + { + const int channels = ::gdk_pixbuf_get_n_channels(pixbuf); + if (channels == 3 || channels == 4) + { + const int stride = ::gdk_pixbuf_get_rowstride(pixbuf); + const unsigned char* rgbaSrc = ::gdk_pixbuf_get_pixels(pixbuf); + + if (channels == 3) + { + assert(!::gdk_pixbuf_get_has_alpha(pixbuf)); + + ImageHolder out(::gdk_pixbuf_get_width(pixbuf), ::gdk_pixbuf_get_height(pixbuf), false /*withAlpha*/); + unsigned char* rgbTrg = out.getRgb(); + + for (int y = 0; y < out.getHeight(); ++y) + { + const unsigned char* srcLine = rgbaSrc + y * stride; + for (int x = 0; x < out.getWidth(); ++x) + { + *rgbTrg++ = *srcLine++; + *rgbTrg++ = *srcLine++; + *rgbTrg++ = *srcLine++; + } + } + return out; + } + else if (channels == 4) + { + assert(::gdk_pixbuf_get_has_alpha(pixbuf)); + + ImageHolder out(::gdk_pixbuf_get_width(pixbuf), ::gdk_pixbuf_get_height(pixbuf), true /*withAlpha*/); + unsigned char* rgbTrg = out.getRgb(); + unsigned char* alphaTrg = out.getAlpha(); + + for (int y = 0; y < out.getHeight(); ++y) + { + const unsigned char* srcLine = rgbaSrc + y * stride; + for (int x = 0; x < out.getWidth(); ++x) + { + *rgbTrg++ = *srcLine++; + *rgbTrg++ = *srcLine++; + *rgbTrg++ = *srcLine++; + *alphaTrg++ = *srcLine++; + } + } + return out; + } + } + } + return ImageHolder(); +} + +#elif defined ZEN_MAC +ImageHolder copyToImageHolder(const osx::ImageData& img) +{ + assert(!img.rgb.empty() && !img.alpha.empty()); + if (img.rgb.empty()) + return ImageHolder(); + + ImageHolder out(img.width, img.height, !img.alpha.empty() /*withAlpha*/); + ::memcpy(out.getRgb(), &img.rgb[0], img.width * img.height * 3); + + if (!img.alpha.empty()) + ::memcpy(out.getAlpha(), &img.alpha[0], img.width * img.height); + return out; +} +#endif + + +#ifdef ZEN_WIN +thumb::IconSizeType getThumbSizeType(int pixelSize) +{ + //coordinate with IconBuffer::getSize()! + using namespace thumb; + if (pixelSize >= 256) return ICON_SIZE_256; + if (pixelSize >= 128) return ICON_SIZE_128; + if (pixelSize >= 48) return ICON_SIZE_48; + if (pixelSize >= 32) return ICON_SIZE_32; + return ICON_SIZE_16; +} + + +ImageHolder getIconByAttribute(LPCWSTR pszPath, DWORD dwFileAttributes, int pixelSize) +{ + //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! + SHFILEINFO fileInfo = {}; //initialize hIcon + DWORD_PTR imgList = ::SHGetFileInfo(::wcslen(pszPath) == 0 ? L"dummy" : pszPath, //Windows 7 doesn't like this parameter to be an empty string! + dwFileAttributes, + &fileInfo, + sizeof(fileInfo), + SHGFI_USEFILEATTRIBUTES | //== no disk access: http://blogs.msdn.com/b/oldnewthing/archive/2004/06/01/145428.aspx + SHGFI_SYSICONINDEX); + if (!imgList) //no need to IUnknown::Release() imgList! + return ImageHolder(); + + if (getIconByIndex && releaseImageData) + if (const thumb::ImageData* img = getIconByIndex(fileInfo.iIcon, getThumbSizeType(pixelSize))) + { + ZEN_ON_SCOPE_EXIT(releaseImageData(img)); + return copyToImageHolder(*img); + } + + return ImageHolder(); +} + +#elif defined ZEN_LINUX +ImageHolder imageHolderFromGicon(GIcon* gicon, int pixelSize) +{ + if (gicon) + if (GtkIconTheme* defaultTheme = ::gtk_icon_theme_get_default()) //not owned! + if (GtkIconInfo* iconInfo = ::gtk_icon_theme_lookup_by_gicon(defaultTheme, gicon, pixelSize, GTK_ICON_LOOKUP_USE_BUILTIN)) //this may fail if icon is not installed on system + { + ZEN_ON_SCOPE_EXIT(::gtk_icon_info_free(iconInfo)); + if (GdkPixbuf* pixBuf = ::gtk_icon_info_load_icon(iconInfo, nullptr)) + { + ZEN_ON_SCOPE_EXIT(::g_object_unref(pixBuf)); //superseedes "::gdk_pixbuf_unref"! + return copyToImageHolder(pixBuf); + } + } + return ImageHolder(); +} +#endif +} + + +ImageHolder zen::getIconByTemplatePath(const Zstring& templatePath, int pixelSize) +{ +#ifdef ZEN_WIN + //no read-access to disk! determine icon by extension + return getIconByAttribute(templatePath.c_str(), FILE_ATTRIBUTE_NORMAL, pixelSize); + +#elif defined ZEN_LINUX + //uses full file name, e.g. "AUTHORS" has own mime type on Linux: + if (gchar* contentType = ::g_content_type_guess(templatePath.c_str(), //const gchar* filename, + nullptr, //const guchar* data, + 0, //gsize data_size, + nullptr)) //gboolean* result_uncertain + { + ZEN_ON_SCOPE_EXIT(::g_free(contentType)); + if (GIcon* dirIcon = ::g_content_type_get_icon(contentType)) + { + ZEN_ON_SCOPE_EXIT(::g_object_unref(dirIcon)); + return imageHolderFromGicon(dirIcon, pixelSize); + } + } + return ImageHolder(); + +#elif defined ZEN_MAC + try + { + const osx::ImageData& img = osx::getIconByExtension(getFileExtension(templatePath).c_str(), pixelSize); //throw SysError + return copyToImageHolder(img); + } + catch (SysError&) { return ImageHolder(); } +#endif +} + + +ImageHolder zen::genericFileIcon(int pixelSize) +{ + //we're called by getDisplayIcon()! -> avoid endless recursion! +#ifdef ZEN_WIN + return getIconByAttribute(L"", FILE_ATTRIBUTE_NORMAL, pixelSize); + +#elif defined ZEN_LINUX + if (GIcon* fileIcon = ::g_content_type_get_icon("text/plain")) + { + ZEN_ON_SCOPE_EXIT(::g_object_unref(fileIcon)); + return imageHolderFromGicon(fileIcon, pixelSize); + } + return ImageHolder(); + +#elif defined ZEN_MAC + try + { + return copyToImageHolder(osx::getDefaultFileIcon(pixelSize)); //throw SysError + } + catch (SysError&) { return ImageHolder(); } +#endif +} + + +ImageHolder zen::genericDirIcon(int pixelSize) +{ +#ifdef ZEN_WIN + return getIconByAttribute(L"", FILE_ATTRIBUTE_DIRECTORY, pixelSize); + +#elif defined ZEN_LINUX + if (GIcon* dirIcon = ::g_content_type_get_icon("inode/directory")) //should contain fallback to GTK_STOCK_DIRECTORY ("gtk-directory") + { + ZEN_ON_SCOPE_EXIT(::g_object_unref(dirIcon)); + return imageHolderFromGicon(dirIcon, pixelSize); + } + return ImageHolder(); + +#elif defined ZEN_MAC + try + { + return copyToImageHolder(osx::getDefaultFolderIcon(pixelSize)); //throw SysError + } + catch (SysError&) { return ImageHolder(); } +#endif +} + + +ImageHolder zen::getFileIcon(const Zstring& filePath, int pixelSize) +{ + //2. retrieve file icons +#ifdef ZEN_WIN + SHFILEINFO fileInfo = {}; + if (DWORD_PTR imgList = ::SHGetFileInfo(filePath.c_str(), //_In_ LPCTSTR pszPath, -> note: ::SHGetFileInfo() can't handle \\?\-prefix! + 0, //DWORD dwFileAttributes, + &fileInfo, //_Inout_ SHFILEINFO *psfi, + sizeof(fileInfo), //UINT cbFileInfo, + SHGFI_SYSICONINDEX /*| SHGFI_ATTRIBUTES*/)) //UINT uFlags + { + (void)imgList; + //imgList->Release(); //empiric study: crash on XP if we release this! Seems we do not own it... -> also no GDI leak on Win7 -> okay + //another comment on http://msdn.microsoft.com/en-us/library/bb762179(v=VS.85).aspx describes exact same behavior on Win7/XP + + //Quote: "The IImageList pointer type, such as that returned in the ppv parameter, can be cast as an HIMAGELIST as needed; + // for example, for use in a list view. Conversely, an HIMAGELIST can be cast as a pointer to an IImageList." + //http://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx + + //Check for link icon type (= shell links and symlinks): SHGetFileInfo + SHGFI_ATTRIBUTES: + //const bool isLink = (fileInfo.dwAttributes & SFGAO_LINK) != 0; + + if (getIconByIndex && releaseImageData) + if (const thumb::ImageData* img = getIconByIndex(fileInfo.iIcon, getThumbSizeType(pixelSize))) + { + ZEN_ON_SCOPE_EXIT(releaseImageData(img)); + return copyToImageHolder(*img); + } + } + +#elif defined ZEN_LINUX + GFile* file = ::g_file_new_for_path(filePath.c_str()); //documented to "never fail" + ZEN_ON_SCOPE_EXIT(::g_object_unref(file)); + + if (GFileInfo* fileInfo = ::g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_ICON, G_FILE_QUERY_INFO_NONE, nullptr, nullptr)) + { + ZEN_ON_SCOPE_EXIT(::g_object_unref(fileInfo)); + if (GIcon* gicon = ::g_file_info_get_icon(fileInfo)) //not owned! + return imageHolderFromGicon(gicon, pixelSize); + } + //need fallback: icon lookup may fail because some icons are currently not present on system + +#elif defined ZEN_MAC + try + { + return copyToImageHolder(osx::getFileIcon(filePath.c_str(), pixelSize)); //throw SysError + } + catch (SysError&) { assert(false); } +#endif + return ImageHolder(); +} + + +ImageHolder zen::getThumbnailImage(const Zstring& filePath, int pixelSize) //return null icon on failure +{ +#ifdef ZEN_WIN + if (getThumbnail && releaseImageData) + if (const thumb::ImageData* img = getThumbnail(filePath.c_str(), pixelSize)) + { + ZEN_ON_SCOPE_EXIT(releaseImageData(img)); + return copyToImageHolder(*img); + } + +#elif defined ZEN_LINUX + struct ::stat fileInfo = {}; + if (::stat(filePath.c_str(), &fileInfo) == 0) + if (!S_ISFIFO(fileInfo.st_mode)) //skip named pipes: else gdk_pixbuf_get_file_info() would hang forever! + { + gint width = 0; + gint height = 0; + if (GdkPixbufFormat* fmt = ::gdk_pixbuf_get_file_info(filePath.c_str(), &width, &height)) + { + (void)fmt; + if (width > 0 && height > 0 && pixelSize > 0) + { + int trgWidth = width; + int trgHeight = height; + + const int maxExtent = std::max(width, height); //don't stretch small images, shrink large ones only! + if (pixelSize < maxExtent) + { + trgWidth = width * pixelSize / maxExtent; + trgHeight = height * pixelSize / maxExtent; + } + if (GdkPixbuf* pixBuf = ::gdk_pixbuf_new_from_file_at_size(filePath.c_str(), trgWidth, trgHeight, nullptr)) + { + ZEN_ON_SCOPE_EXIT(::g_object_unref(pixBuf)); + return copyToImageHolder(pixBuf); + } + } + } + } + +#elif defined ZEN_MAC + try + { + return copyToImageHolder(osx::getThumbnail(filePath.c_str(), pixelSize)); //throw SysError + } + catch (SysError&) {} +#endif + return ImageHolder(); +} diff --git a/FreeFileSync/Source/lib/icon_loader.h b/FreeFileSync/Source/lib/icon_loader.h new file mode 100644 index 00000000..70407ce0 --- /dev/null +++ b/FreeFileSync/Source/lib/icon_loader.h @@ -0,0 +1,26 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ICON_LOADER_1348701985713445 +#define ICON_LOADER_1348701985713445 + +#include <zen/zstring.h> +#include "icon_holder.h" + +namespace zen +{ +//=> all functions are safe to call from multiple threads! +//!!!Note: init COM + system image list before loading icons!!! + +//return null icon on failure: +ImageHolder getIconByTemplatePath(const Zstring& templatePath, int pixelSize); +ImageHolder genericFileIcon(int pixelSize); +ImageHolder genericDirIcon(int pixelSize); +ImageHolder getFileIcon(const Zstring& filePath, int pixelSize); +ImageHolder getThumbnailImage(const Zstring& filePath, int pixelSize); +} + +#endif //ICON_LOADER_1348701985713445 diff --git a/FreeFileSync/Source/lib/localization.cpp b/FreeFileSync/Source/lib/localization.cpp index dba8d6e5..b3c87d9d 100644 --- a/FreeFileSync/Source/lib/localization.cpp +++ b/FreeFileSync/Source/lib/localization.cpp @@ -171,7 +171,7 @@ ExistingTranslations::ExistingTranslations() traverseFolder(zen::getResourceDir() + Zstr("Languages"), [&](const FileInfo& fi) { - if (endsWith(fi.fullPath, Zstr(".lng"))) + if (pathEndsWith(fi.fullPath, Zstr(".lng"))) lngFiles.push_back(fi.fullPath); }, nullptr, nullptr, [&](const std::wstring& errorMsg) { assert(false); }); //errors are not really critical in this context diff --git a/FreeFileSync/Source/lib/lock_holder.h b/FreeFileSync/Source/lib/lock_holder.h index 93588310..9d110ed3 100644 --- a/FreeFileSync/Source/lib/lock_holder.h +++ b/FreeFileSync/Source/lib/lock_holder.h @@ -17,14 +17,12 @@ const Zchar LOCK_FILE_ENDING[] = Zstr(".ffs_lock"); //don't use Zstring as glob class LockHolder { public: - LockHolder(const std::set<Zstring, LessFilename>& dirpathsExisting, //resolved dirpaths ending with path separator + LockHolder(const std::set<Zstring, LessFilePath>& dirpathsExisting, //resolved paths bool& warningDirectoryLockFailed, ProcessCallback& procCallback) { - for (const Zstring& dirpathFmt : dirpathsExisting) + for (const Zstring& dirpath : dirpathsExisting) { - assert(endsWith(dirpathFmt, FILE_NAME_SEPARATOR)); //this is really the contract, formatting does other things as well, e.g. macro substitution - class WaitOnLockHandler : public DirLockCallback { public: @@ -38,11 +36,11 @@ public: try { //lock file creation is synchronous and may block noticeably for very slow devices (usb sticks, mapped cloud storages) - lockHolder.emplace_back(dirpathFmt + Zstr("sync") + LOCK_FILE_ENDING, &callback); //throw FileError + lockHolder.emplace_back(appendSeparator(dirpath) + Zstr("sync") + LOCK_FILE_ENDING, &callback); //throw FileError } catch (const FileError& e) { - const std::wstring msg = replaceCpy(_("Cannot set directory lock for %x."), L"%x", fmtFileName(dirpathFmt)) + L"\n\n" + e.toString(); + const std::wstring msg = replaceCpy(_("Cannot set directory lock for %x."), L"%x", fmtFileName(dirpath)) + L"\n\n" + e.toString(); procCallback.reportWarning(msg, warningDirectoryLockFailed); //may throw! } } diff --git a/FreeFileSync/Source/lib/parallel_scan.cpp b/FreeFileSync/Source/lib/parallel_scan.cpp index 7a78c644..4d9aa69b 100644 --- a/FreeFileSync/Source/lib/parallel_scan.cpp +++ b/FreeFileSync/Source/lib/parallel_scan.cpp @@ -10,7 +10,6 @@ #include <zen/scope_guard.h> #include <zen/fixed_list.h> #include <boost/detail/atomic_count.hpp> -#include "deep_file_traverser.h" #include "db_file.h" #include "lock_holder.h" @@ -279,30 +278,33 @@ struct TraverserShared { public: TraverserShared(long threadID, - SymLinkHandling handleSymlinks, + const ABF& abf, const HardFilter::FilterRef& filter, - std::map<Zstring, std::wstring, LessFilename>& failedDirReads, - std::map<Zstring, std::wstring, LessFilename>& failedItemReads, + SymLinkHandling handleSymlinks, + std::map<Zstring, std::wstring, LessFilePath>& failedDirReads, + std::map<Zstring, std::wstring, LessFilePath>& failedItemReads, AsyncCallback& acb) : - handleSymlinks_(handleSymlinks), + abstractBaseFolder(abf), filterInstance(filter), + handleSymlinks_(handleSymlinks), failedDirReads_ (failedDirReads), failedItemReads_(failedItemReads), acb_(acb), threadID_(threadID) {} - const SymLinkHandling handleSymlinks_; + const ABF& abstractBaseFolder; const HardFilter::FilterRef filterInstance; //always bound! + const SymLinkHandling handleSymlinks_; - std::map<Zstring, std::wstring, LessFilename>& failedDirReads_; - std::map<Zstring, std::wstring, LessFilename>& failedItemReads_; + std::map<Zstring, std::wstring, LessFilePath>& failedDirReads_; + std::map<Zstring, std::wstring, LessFilePath>& failedItemReads_; AsyncCallback& acb_; const long threadID_; }; -class DirCallback : public zen::TraverseCallback +class DirCallback : public ABF::TraverserCallback { public: DirCallback(TraverserShared& config, @@ -313,10 +315,10 @@ public: output_(output) {} virtual void onFile (const FileInfo& fi) override; - virtual TraverseCallback* onDir (const DirInfo& di) override; + virtual TraverserCallback* onDir (const DirInfo& di) override; virtual HandleLink onSymlink(const SymlinkInfo& li) override; - void releaseDirTraverser(TraverseCallback* trav) override; + void releaseDirTraverser(TraverserCallback* trav) override; HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override; HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) override; @@ -339,12 +341,14 @@ void DirCallback::onFile(const FileInfo& fi) endsWith(fileNameShort, LOCK_FILE_ENDING)) return; + const Zstring relFilePath = relNameParentPf_ + fileNameShort; + //update status information no matter whether object is excluded or not! - cfg.acb_.reportCurrentFile(fi.fullPath, cfg.threadID_); + cfg.acb_.reportCurrentFile(ABF::getDisplayPath(cfg.abstractBaseFolder.getAbstractPath(relFilePath)), cfg.threadID_); //------------------------------------------------------------------------------------ //apply filter before processing (use relative name!) - if (!cfg.filterInstance->passFileFilter(relNameParentPf_ + fileNameShort)) + if (!cfg.filterInstance->passFileFilter(relFilePath)) return; // std::string fileId = details.fileSize >= 1024 * 1024U ? util::retrieveFileID(filepath) : std::string(); @@ -364,19 +368,19 @@ void DirCallback::onFile(const FileInfo& fi) } -TraverseCallback* DirCallback::onDir(const DirInfo& di) +ABF::TraverserCallback* DirCallback::onDir(const DirInfo& di) { boost::this_thread::interruption_point(); + const Zstring& relDirPath = relNameParentPf_ + di.shortName; + //update status information no matter whether object is excluded or not! - cfg.acb_.reportCurrentFile(di.fullPath, cfg.threadID_); + cfg.acb_.reportCurrentFile(ABF::getDisplayPath(cfg.abstractBaseFolder.getAbstractPath(relDirPath)), cfg.threadID_); //------------------------------------------------------------------------------------ - const Zstring& relPath = relNameParentPf_ + di.shortName; - //apply filter before processing (use relative name!) bool subObjMightMatch = true; - const bool passFilter = cfg.filterInstance->passDirFilter(relPath, &subObjMightMatch); + const bool passFilter = cfg.filterInstance->passDirFilter(relDirPath, &subObjMightMatch); if (!passFilter && !subObjMightMatch) return nullptr; //do NOT traverse subdirs //else: attention! ensure directory filtering is applied later to exclude actually filtered directories @@ -385,7 +389,7 @@ TraverseCallback* DirCallback::onDir(const DirInfo& di) if (passFilter) cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator - return new DirCallback(cfg, relPath + FILE_NAME_SEPARATOR, subDir); //releaseDirTraverser() is guaranteed to be called in any case + return new DirCallback(cfg, relDirPath + FILE_NAME_SEPARATOR, subDir); //releaseDirTraverser() is guaranteed to be called in any case } @@ -393,8 +397,10 @@ DirCallback::HandleLink DirCallback::onSymlink(const SymlinkInfo& si) { boost::this_thread::interruption_point(); + const Zstring& relLinkPath = relNameParentPf_ + si.shortName; + //update status information no matter whether object is excluded or not! - cfg.acb_.reportCurrentFile(si.fullPath, cfg.threadID_); + cfg.acb_.reportCurrentFile(ABF::getDisplayPath(cfg.abstractBaseFolder.getAbstractPath(relLinkPath)), cfg.threadID_); switch (cfg.handleSymlinks_) { @@ -402,7 +408,7 @@ DirCallback::HandleLink DirCallback::onSymlink(const SymlinkInfo& si) return LINK_SKIP; case SYMLINK_DIRECT: - if (cfg.filterInstance->passFileFilter(relNameParentPf_ + si.shortName)) //always use file filter: Link type may not be "stable" on Linux! + if (cfg.filterInstance->passFileFilter(relLinkPath)) //always use file filter: Link type may not be "stable" on Linux! { output_.addSubLink(si.shortName, LinkDescriptor(si.lastWriteTime)); cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator @@ -412,10 +418,10 @@ DirCallback::HandleLink DirCallback::onSymlink(const SymlinkInfo& si) case SYMLINK_FOLLOW: //filter symlinks before trying to follow them: handle user-excluded broken symlinks! //since we don't know what type the symlink will resolve to, only do this when both variants agree: - if (!cfg.filterInstance->passFileFilter(relNameParentPf_ + si.shortName)) + if (!cfg.filterInstance->passFileFilter(relLinkPath)) { bool subObjMightMatch = true; - if (!cfg.filterInstance->passDirFilter(relNameParentPf_ + si.shortName, &subObjMightMatch)) + if (!cfg.filterInstance->passDirFilter(relLinkPath, &subObjMightMatch)) if (!subObjMightMatch) return LINK_SKIP; } @@ -427,9 +433,9 @@ DirCallback::HandleLink DirCallback::onSymlink(const SymlinkInfo& si) } -void DirCallback::releaseDirTraverser(TraverseCallback* trav) +void DirCallback::releaseDirTraverser(TraverserCallback* trav) { - TraverseCallback::releaseDirTraverser(trav); //no-op; introduce compile-time coupling + TraverserCallback::releaseDirTraverser(trav); //no-op; introduce compile-time coupling delete trav; } @@ -486,11 +492,14 @@ public: acb_->incActiveWorker(); ZEN_ON_SCOPE_EXIT(acb_->decActiveWorker();); - acb_->reportCurrentFile(dirKey_.dirpath_, threadID_); //just in case first directory access is blocking + const AbstractPathRef& baseFolderItem = dirKey_.baseFolder_->getAbstractPath(); + + acb_->reportCurrentFile(ABF::getDisplayPath(baseFolderItem), threadID_); //just in case first directory access is blocking TraverserShared travCfg(threadID_, - dirKey_.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy + *dirKey_.baseFolder_, dirKey_.filter_, + dirKey_.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy dirOutput_.failedDirReads, dirOutput_.failedItemReads, *acb_); @@ -499,8 +508,7 @@ public: Zstring(), dirOutput_.dirCont); - //get all files and folders from directoryPostfixed (and subdirectories) - deepTraverseFolder(dirKey_.dirpath_, traverser); //exceptions may be thrown! + ABF::traverseFolder(baseFolderItem, traverser); //throw X } private: diff --git a/FreeFileSync/Source/lib/parallel_scan.h b/FreeFileSync/Source/lib/parallel_scan.h index e0c8d15e..5a93b804 100644 --- a/FreeFileSync/Source/lib/parallel_scan.h +++ b/FreeFileSync/Source/lib/parallel_scan.h @@ -17,14 +17,14 @@ namespace zen { struct DirectoryKey { - DirectoryKey(const Zstring& dirpath, + DirectoryKey(const ABF& baseFolder, const HardFilter::FilterRef& filter, SymLinkHandling handleSymlinks) : - dirpath_(dirpath), + baseFolder_(&baseFolder), filter_(filter), handleSymlinks_(handleSymlinks) {} - Zstring dirpath_; + const ABF* baseFolder_; //always bound! HardFilter::FilterRef filter_; //filter interface: always bound by design! SymLinkHandling handleSymlinks_; }; @@ -35,9 +35,10 @@ bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs) if (lhs.handleSymlinks_ != rhs.handleSymlinks_) return lhs.handleSymlinks_ < rhs.handleSymlinks_; - const int cmpName = cmpFileName(lhs.dirpath_, rhs.dirpath_); - if (cmpName != 0) - return cmpName < 0; + if (ABF::LessItemPath()(lhs.baseFolder_, rhs.baseFolder_)) + return true; + if (ABF::LessItemPath()(rhs.baseFolder_, lhs.baseFolder_)) + return false; return *lhs.filter_ < *rhs.filter_; } @@ -47,10 +48,10 @@ struct DirectoryValue { DirContainer dirCont; //relative names (or empty string for root) for directories that could not be read (completely), e.g. access denied, or temporal network drop - std::map<Zstring, std::wstring, LessFilename> failedDirReads; //with corresponding error message + std::map<Zstring, std::wstring, LessFilePath> failedDirReads; //with corresponding error message //relative names (never empty) for failure to read single file/dir/symlink with corresponding error message - std::map<Zstring, std::wstring, LessFilename> failedItemReads; + std::map<Zstring, std::wstring, LessFilePath> failedItemReads; }; diff --git a/FreeFileSync/Source/lib/process_xml.cpp b/FreeFileSync/Source/lib/process_xml.cpp index 9a9c5f64..75c0cf77 100644 --- a/FreeFileSync/Source/lib/process_xml.cpp +++ b/FreeFileSync/Source/lib/process_xml.cpp @@ -177,8 +177,7 @@ void writeText(const CompareVariant& value, std::string& output) template <> inline bool readText(const std::string& input, CompareVariant& value) { - std::string tmp = input; - zen::trim(tmp); + const std::string tmp = trimCpy(input); if (tmp == "TimeAndSize") value = zen::CMP_BY_TIME_SIZE; else if (tmp == "Content") @@ -209,8 +208,7 @@ void writeText(const SyncDirection& value, std::string& output) template <> inline bool readText(const std::string& input, SyncDirection& value) { - std::string tmp = input; - zen::trim(tmp); + const std::string tmp = trimCpy(input); if (tmp == "left") value = SyncDirection::LEFT; else if (tmp == "right") @@ -243,8 +241,7 @@ void writeText(const OnError& value, std::string& output) template <> inline bool readText(const std::string& input, OnError& value) { - std::string tmp = input; - zen::trim(tmp); + const std::string tmp = trimCpy(input); if (tmp == "Ignore") value = ON_ERROR_IGNORE; else if (tmp == "Popup") @@ -274,8 +271,7 @@ void writeText(const OnGuiError& value, std::string& output) template <> inline bool readText(const std::string& input, OnGuiError& value) { - std::string tmp = input; - zen::trim(tmp); + const std::string tmp = trimCpy(input); if (tmp == "Ignore") value = ON_GUIERROR_IGNORE; else if (tmp == "Popup") @@ -307,8 +303,7 @@ void writeText(const FileIconSize& value, std::string& output) template <> inline bool readText(const std::string& input, FileIconSize& value) { - std::string tmp = input; - zen::trim(tmp); + const std::string tmp = trimCpy(input); if (tmp == "Small") value = ICON_SIZE_SMALL; else if (tmp == "Medium") @@ -341,8 +336,7 @@ void writeText(const DeletionPolicy& value, std::string& output) template <> inline bool readText(const std::string& input, DeletionPolicy& value) { - std::string tmp = input; - zen::trim(tmp); + const std::string tmp = trimCpy(input); if (tmp == "Permanent") value = DELETE_PERMANENTLY; else if (tmp == "RecycleBin") @@ -375,8 +369,7 @@ void writeText(const SymLinkHandling& value, std::string& output) template <> inline bool readText(const std::string& input, SymLinkHandling& value) { - std::string tmp = input; - zen::trim(tmp); + const std::string tmp = trimCpy(input); if (tmp == "Exclude") value = SYMLINK_EXCLUDE; else if (tmp == "Direct") @@ -421,8 +414,7 @@ void writeText(const ColumnTypeRim& value, std::string& output) template <> inline bool readText(const std::string& input, ColumnTypeRim& value) { - std::string tmp = input; - zen::trim(tmp); + const std::string tmp = trimCpy(input); if (tmp == "Base") value = COL_TYPE_BASE_DIRECTORY; else if (tmp == "Full") @@ -463,8 +455,7 @@ void writeText(const ColumnTypeNavi& value, std::string& output) template <> inline bool readText(const std::string& input, ColumnTypeNavi& value) { - std::string tmp = input; - zen::trim(tmp); + const std::string tmp = trimCpy(input); if (tmp == "Bytes") value = COL_TYPE_NAVI_BYTES; else if (tmp == "Tree") @@ -500,8 +491,7 @@ void writeText(const UnitSize& value, std::string& output) template <> inline bool readText(const std::string& input, UnitSize& value) { - std::string tmp = input; - zen::trim(tmp); + const std::string tmp = trimCpy(input); if (tmp == "None") value = USIZE_NONE; else if (tmp == "Byte") @@ -541,8 +531,7 @@ void writeText(const UnitTime& value, std::string& output) template <> inline bool readText(const std::string& input, UnitTime& value) { - std::string tmp = input; - zen::trim(tmp); + const std::string tmp = trimCpy(input); if (tmp == "None") value = UTIME_NONE; else if (tmp == "Today") @@ -575,8 +564,7 @@ void writeText(const VersioningStyle& value, std::string& output) template <> inline bool readText(const std::string& input, VersioningStyle& value) { - std::string tmp = input; - zen::trim(tmp); + const std::string tmp = trimCpy(input); if (tmp == "Replace") value = VER_STYLE_REPLACE; else if (tmp == "TimeStamp") @@ -610,8 +598,7 @@ void writeText(const DirectionConfig::Variant& value, std::string& output) template <> inline bool readText(const std::string& input, DirectionConfig::Variant& value) { - std::string tmp = input; - zen::trim(tmp); + const std::string tmp = trimCpy(input); if (tmp == "TwoWay") value = DirectionConfig::TWOWAY; else if (tmp == "Mirror") @@ -759,9 +746,7 @@ namespace void readConfig(const XmlIn& in, CompConfig& cmpConfig) { in["Variant" ](cmpConfig.compareVar); - warn_static("remove check after migration?") - if (in["TimeShift"]) //-> 27.2.2014 - in["TimeShift"](cmpConfig.optTimeShiftHours); + in["TimeShift"](cmpConfig.optTimeShiftHours); in["Symlinks" ](cmpConfig.handleSymlinks); } @@ -787,7 +772,7 @@ void readConfig(const XmlIn& in, SyncConfig& syncCfg) readConfig(in, syncCfg.directionCfg); in["DeletionPolicy" ](syncCfg.handleDeletion); - in["VersioningFolder"](syncCfg.versioningDirectory); + in["VersioningFolder"](syncCfg.versioningFolderPhrase); in["VersioningFolder"].attribute("Style", syncCfg.versioningStyle); } @@ -905,18 +890,9 @@ void readConfig(const XmlIn& in, xmlAccess::XmlBatchConfig& config) //read GUI specific config data XmlIn inBatchCfg = in["BatchConfig"]; - inBatchCfg["HandleError" ](config.handleError); - - warn_static("remove after migration?") - if (inBatchCfg["ShowProgress"]) //2014-2-17 -> obsolete name - { - inBatchCfg["ShowProgress"](config.runMinimized); - config.runMinimized = !config.runMinimized; - } - else - inBatchCfg["RunMinimized" ](config.runMinimized); - - inBatchCfg["LogfileFolder"](config.logFileDirectory); + inBatchCfg["HandleError" ](config.handleError); + inBatchCfg["RunMinimized" ](config.runMinimized); + inBatchCfg["LogfileFolder"](config.logFolderPathPhrase); inBatchCfg["LogfileFolder"].attribute("Limit", config.logfilesCountLimit); } @@ -1023,7 +999,7 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) //last update check inGui["LastOnlineCheck" ](config.gui.lastUpdateCheck); - inGui["LastOnlineVersion"](config.gui.lastOnlineVersion); + inGui["LastOnlineVersion"](config.gui.lastOnlineVersion); //batch specific global settings //XmlIn inBatch = in["Batch"]; @@ -1189,7 +1165,7 @@ void writeConfig(const SyncConfig& syncCfg, XmlOut& out) writeConfig(syncCfg.directionCfg, out); out["DeletionPolicy" ](syncCfg.handleDeletion); - out["VersioningFolder"](syncCfg.versioningDirectory); + out["VersioningFolder"](syncCfg.versioningFolderPhrase); out["VersioningFolder"].attribute("Style", syncCfg.versioningStyle); } @@ -1298,7 +1274,7 @@ void writeConfig(const XmlBatchConfig& config, XmlOut& out) outBatchCfg["HandleError" ](config.handleError); outBatchCfg["RunMinimized" ](config.runMinimized); - outBatchCfg["LogfileFolder"](config.logFileDirectory); + outBatchCfg["LogfileFolder"](config.logFolderPathPhrase); outBatchCfg["LogfileFolder"].attribute("Limit", config.logfilesCountLimit); } @@ -1398,7 +1374,7 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) //last update check outGui["LastOnlineCheck" ](config.gui.lastUpdateCheck); - outGui["LastOnlineVersion"](config.gui.lastOnlineVersion); + outGui["LastOnlineVersion"](config.gui.lastOnlineVersion); //batch specific global settings //XmlOut outBatch = out["Batch"]; diff --git a/FreeFileSync/Source/lib/process_xml.h b/FreeFileSync/Source/lib/process_xml.h index 17089d8c..e28b68c9 100644 --- a/FreeFileSync/Source/lib/process_xml.h +++ b/FreeFileSync/Source/lib/process_xml.h @@ -77,7 +77,7 @@ struct XmlBatchConfig zen::MainConfiguration mainCfg; bool runMinimized; - Zstring logFileDirectory; + Zstring logFolderPathPhrase; int logfilesCountLimit; //max logfiles; 0 := don't save logfiles; < 0 := no limit OnError handleError; //reaction on error situation during synchronization }; @@ -263,7 +263,7 @@ struct XmlGlobalSettings FileIconSize iconSize; long lastUpdateCheck; //time of last update check - wxString lastOnlineVersion; + wxString lastOnlineVersion; ViewFilterDefault viewFilterDefault; wxString guiPerspectiveLast; //used by wxAuiManager diff --git a/FreeFileSync/Source/lib/resolve_path.cpp b/FreeFileSync/Source/lib/resolve_path.cpp index 4faaa34f..8c01b8c3 100644 --- a/FreeFileSync/Source/lib/resolve_path.cpp +++ b/FreeFileSync/Source/lib/resolve_path.cpp @@ -28,7 +28,7 @@ using namespace zen; namespace { -Zstring resolveRelativePath(const Zstring& relativePath) //note: ::GetFullPathName() is documented to be not thread-safe! +Zstring resolveRelativePath(const Zstring& relativePath) //note: ::GetFullPathName() is documented to be NOT thread-safe! { #ifdef ZEN_WIN //- don't use long path prefix here! does not work with relative paths "." and ".." @@ -86,7 +86,7 @@ Zstring resolveRelativePath(const Zstring& relativePath) //note: ::GetFullPathNa class CsidlConstants { public: - typedef std::map<Zstring, Zstring, LessFilename> CsidlToDirMap; //case-insensitive comparison + typedef std::map<Zstring, Zstring, LessFilePath> CsidlToDirMap; //case-insensitive comparison static const CsidlToDirMap& get() { @@ -208,7 +208,7 @@ auto& dummy = CsidlConstants::get(); #endif -Opt<Zstring> getEnvironmentVar(const Zstring& envName) //return nullptr if not found +Opt<Zstring> getEnvironmentVar(const Zstring& envName) //return null if not found { wxString value; if (!wxGetEnv(utfCvrtTo<wxString>(envName), &value)) @@ -353,7 +353,7 @@ Opt<Zstring> getPathByVolumenName(const Zstring& volumeName) //return no value o nullptr, //__out_opt LPDWORD lpFileSystemFlags, nullptr, //__out LPTSTR lpFileSystemNameBuffer, 0)) //__in DWORD nFileSystemNameSize - if (EqualFilename()(volumeName, Zstring(&volName[0]))) + if (EqualFilePath()(volumeName, Zstring(&volName[0]))) return zen::make_unique<Zstring>(path); return nullptr; }); @@ -396,9 +396,7 @@ Zstring expandVolumeName(const Zstring& text) // [volname]:\folder [volna //this would be a nice job for a C++11 regex... //we only expect the [.*] pattern at the beginning => do not touch dir names like "C:\somedir\[stuff]" - Zstring textTmp = text; - trim(textTmp, true, false); - + const Zstring textTmp = trimCpy(text, true, false); if (startsWith(textTmp, Zstr("["))) { size_t posEnd = textTmp.find(Zstr("]")); @@ -440,7 +438,7 @@ Zstring expandVolumeName(const Zstring& text) // [volname]:\folder [volna } -void getDirectoryAliasesRecursive(const Zstring& dirpath, std::set<Zstring, LessFilename>& output) +void getDirectoryAliasesRecursive(const Zstring& dirpath, std::set<Zstring, LessFilePath>& output) { #ifdef ZEN_WIN //1. replace volume path by volume name: c:\dirpath -> [SYSTEM]\dirpath @@ -495,14 +493,6 @@ void getDirectoryAliasesRecursive(const Zstring& dirpath, std::set<Zstring, Less addEnvVar("HOME"); //Linux: /home/<user> Mac: /Users/<user> #endif //substitute paths by symbolic names - auto pathStartsWith = [](const Zstring& path, const Zstring& prefix) -> bool - { -#if defined ZEN_WIN || defined ZEN_MAC - return startsWith(makeUpperCopy(path), makeUpperCopy(prefix)); -#elif defined ZEN_LINUX - return startsWith(path, prefix); -#endif - }; for (const auto& entry : envToDir) if (pathStartsWith(dirpath, entry.second)) output.insert(MACRO_SEP + entry.first + MACRO_SEP + (dirpath.c_str() + entry.second.size())); @@ -520,12 +510,11 @@ void getDirectoryAliasesRecursive(const Zstring& dirpath, std::set<Zstring, Less std::vector<Zstring> zen::getDirectoryAliases(const Zstring& dirpathPhrase) { - Zstring dirpath = dirpathPhrase; - trim(dirpath, true, false); + const Zstring dirpath = trimCpy(dirpathPhrase, true, false); if (dirpath.empty()) return std::vector<Zstring>(); - std::set<Zstring, LessFilename> tmp; + std::set<Zstring, LessFilePath> tmp; getDirectoryAliasesRecursive(dirpath, tmp); tmp.erase(dirpath); @@ -535,23 +524,26 @@ std::vector<Zstring> zen::getDirectoryAliases(const Zstring& dirpathPhrase) } -Zstring zen::getFormattedDirectoryPath(const Zstring& dirpassPhrase) // throw() +//coordinate changes with acceptsFolderPathPhraseNative()! +Zstring zen::getResolvedDirectoryPath(const Zstring& dirpassPhrase) //noexcept { - //formatting is needed since functions expect the directory to end with '\' to be able to split the relative names. - Zstring dirpath = dirpassPhrase; + dirpath = expandMacros(dirpath); //expand before trimming! + //remove leading/trailing whitespace before allowing misinterpretation in applyLongPathPrefix() trim(dirpath, true, false); while (endsWith(dirpath, Zstr(' '))) //don't remove all whitespace from right, e.g. 0xa0 may be used as part of dir name dirpath.resize(dirpath.size() - 1); - if (dirpath.empty()) //an empty string would later be resolved as "\"; this is not desired - return Zstring(); +#ifdef ZEN_WIN + dirpath = removeLongPathPrefix(dirpath); +#endif - dirpath = expandMacros(dirpath); - dirpath = expandVolumeName(dirpath); //may block for slow USB sticks! + dirpath = expandVolumeName(dirpath); //may block for slow USB sticks and idle HDDs! + if (dirpath.empty()) //an empty string would later be resolved as "\"; this is not desired + return Zstring(); /* need to resolve relative paths: WINDOWS: @@ -564,7 +556,21 @@ Zstring zen::getFormattedDirectoryPath(const Zstring& dirpassPhrase) // throw() */ dirpath = resolveRelativePath(dirpath); - return appendSeparator(dirpath); + auto isVolumeRoot = [](const Zstring& path) + { +#ifdef ZEN_WIN + return path.size() == 3 && std::iswalpha(path[0]) && path[1] == L':' && path[2] == L'\\'; +#elif defined ZEN_LINUX || defined ZEN_MAC + return path == "/"; +#endif + }; + + //remove trailing slash, unless volume root: + if (endsWith(dirpath, FILE_NAME_SEPARATOR)) + if (!isVolumeRoot(dirpath)) + dirpath = beforeLast(dirpath, FILE_NAME_SEPARATOR); + + return dirpath; } diff --git a/FreeFileSync/Source/lib/resolve_path.h b/FreeFileSync/Source/lib/resolve_path.h index bc141c90..7bfcfd1e 100644 --- a/FreeFileSync/Source/lib/resolve_path.h +++ b/FreeFileSync/Source/lib/resolve_path.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef RESOLVE_PATH_H_INCLUDED -#define RESOLVE_PATH_H_INCLUDED +#ifndef RESOLVE_PATH_H_INCLUDED_817402834713454 +#define RESOLVE_PATH_H_INCLUDED_817402834713454 #include <vector> #include <zen/zstring.h> @@ -13,13 +13,15 @@ namespace zen { /* -FULL directory format: + - trim whitespace - expand macros - expand volume path by name - convert relative paths into absolute - - trim whitespace and append file name separator + + => may block for slow USB sticks and idle HDDs + => not thread-safe, see ::GetFullPathName()! */ -Zstring getFormattedDirectoryPath(const Zstring& dirpathPhrase); //noexcept; may still block for slow USB sticks! not thread-safe!!! see ::GetFullPathName() +Zstring getResolvedDirectoryPath(const Zstring& dirpassPhrase); //noexcept //macro substitution only Zstring expandMacros(const Zstring& text); @@ -33,4 +35,4 @@ std::vector<Zstring> getDirectoryAliases(const Zstring& dirpassPhrase); //may bl } -#endif // RESOLVE_PATH_H_INCLUDED +#endif //RESOLVE_PATH_H_INCLUDED_817402834713454 diff --git a/FreeFileSync/Source/lib/versioning.cpp b/FreeFileSync/Source/lib/versioning.cpp index 621e9ef0..2fa7d924 100644 --- a/FreeFileSync/Source/lib/versioning.cpp +++ b/FreeFileSync/Source/lib/versioning.cpp @@ -1,32 +1,27 @@ #include "versioning.h" -#include <map> #include <cstddef> //required by GCC 4.8.1 to find ptrdiff_t -#include <zen/file_access.h> -#include <zen/file_traverser.h> -#include <zen/string_tools.h> using namespace zen; - +using ABF = AbstractBaseFolder; namespace { -Zstring getExtension(const Zstring& relativePath) //including "." if extension is existing, returns empty string otherwise +inline +Zstring getDotExtension(const Zstring& relativePath) //including "." if extension is existing, returns empty string otherwise { - auto iterSep = find_last(relativePath.begin(), relativePath.end(), FILE_NAME_SEPARATOR); - auto iterName = iterSep != relativePath.end() ? iterSep + 1 : relativePath.begin(); //find beginning of short name - auto iterDot = find_last(iterName, relativePath.end(), Zstr('.')); //equal to relativePath.end() if file has no extension!! - return Zstring(&*iterDot, relativePath.end() - iterDot); + const Zstring& extension = getFileExtension(relativePath); + return extension.empty() ? extension : Zstr('.') + extension; }; } -bool impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameVersion) //e.g. ("Sample.txt", "Sample.txt 2012-05-15 131513.txt") +bool impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameVersioned) //e.g. ("Sample.txt", "Sample.txt 2012-05-15 131513.txt") { - auto it = shortnameVersion.begin(); - auto last = shortnameVersion.end(); + auto it = shortnameVersioned.begin(); + auto itLast = shortnameVersioned.end(); auto nextDigit = [&]() -> bool { - if (it == last || !isDigit(*it)) + if (it == itLast || !isDigit(*it)) return false; ++it; return true; @@ -40,14 +35,14 @@ bool impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameV }; auto nextChar = [&](Zchar c) -> bool { - if (it == last || *it != c) + if (it == itLast || *it != c) return false; ++it; return true; }; auto nextStringI = [&](const Zstring& str) -> bool //windows: ignore case! { - if (last - it < static_cast<ptrdiff_t>(str.size()) || !EqualFilename()(str, Zstring(&*it, str.size()))) + if (itLast - it < static_cast<ptrdiff_t>(str.size()) || !EqualFilePath()(str, Zstring(&*it, str.size()))) return false; it += str.size(); return true; @@ -62,8 +57,8 @@ bool impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameV nextDigits(2) && //DD nextChar(Zstr(' ')) && // nextDigits(6) && //HHMMSS - nextStringI(getExtension(shortname)) && - it == last; + nextStringI(getDotExtension(shortname)) && + it == itLast; } @@ -74,45 +69,48 @@ namespace - create target super directories if missing */ template <class Function> -void moveItemToVersioning(const Zstring& itempath, //throw FileError +void moveItemToVersioning(const AbstractPathRef& itemPath, //throw FileError const Zstring& relativePath, - const Zstring& versioningDirectory, + AbstractBaseFolder& versioningFolder, const Zstring& timestamp, VersioningStyle versioningStyle, - Function moveObj) //move source -> target; may throw FileError + Function moveItem) //move source -> target; may throw FileError { assert(!startsWith(relativePath, FILE_NAME_SEPARATOR)); assert(!endsWith (relativePath, FILE_NAME_SEPARATOR)); + assert(!relativePath.empty()); - Zstring targetPath; + Zstring versionedRelPath; switch (versioningStyle) { + default: + assert(false); case VER_STYLE_REPLACE: - targetPath = appendSeparator(versioningDirectory) + relativePath; - break; - - case VER_STYLE_ADD_TIMESTAMP: - //assemble time-stamped version name - targetPath = appendSeparator(versioningDirectory) + relativePath + Zstr(' ') + timestamp + getExtension(relativePath); - assert(impl::isMatchingVersion(afterLast(relativePath, FILE_NAME_SEPARATOR), afterLast(targetPath, FILE_NAME_SEPARATOR))); //paranoid? no! - break; + versionedRelPath = relativePath; + + case VER_STYLE_ADD_TIMESTAMP: //assemble time-stamped version name + versionedRelPath = relativePath + Zstr(' ') + timestamp + getDotExtension(relativePath); + assert(impl::isMatchingVersion(afterLast(relativePath, FILE_NAME_SEPARATOR), afterLast(versionedRelPath, FILE_NAME_SEPARATOR))); //paranoid? no! } + const AbstractPathRef versionedItemPath = versioningFolder.getAbstractPath(versionedRelPath); + try { - moveObj(itempath, targetPath); //throw FileError + moveItem(itemPath, versionedItemPath); //throw FileError } catch (FileError&) //expected to fail if target directory is not yet existing! { - if (!somethingExists(itempath)) //no source at all is not an error (however a directory as source when a file is expected, *is* an error!) + if (!ABF::somethingExists(itemPath)) //no source at all is not an error (however a directory as source when a file is expected, *is* an error!) return; //object *not* processed //create intermediate directories if missing - const Zstring targetDir = beforeLast(targetPath, FILE_NAME_SEPARATOR); - if (!dirExists(targetDir)) //->(minor) file system race condition! + const AbstractPathRef versionedParentPath = versioningFolder.getAbstractPath(beforeLast(versionedRelPath, FILE_NAME_SEPARATOR)); //returns empty string if term not found + if (!ABF::somethingExists(versionedParentPath)) //->(minor) file system race condition! { - makeDirectory(targetDir); //throw FileError - moveObj(itempath, targetPath); //throw FileError -> this should work now! + ABF::createNewFolder(versionedParentPath); //throw FileError, (ErrorTargetExisting) + //retry: this should work now! + moveItem(itemPath, versionedItemPath); //throw FileError } else throw; @@ -124,25 +122,28 @@ void moveItemToVersioning(const Zstring& itempath, //throw FileError //no need to check if: - super-directories of target exist - source exists: done by moveItemToVersioning() //if target already exists, it is overwritten, even if it is a different type, e.g. a directory! template <class Function> -void moveObject(const Zstring& sourceFile, //throw FileError - const Zstring& targetFile, - Function copyDelete) //throw FileError; fallback if move failed +void moveItem(const AbstractPathRef& sourcePath, //throw FileError + const AbstractPathRef& targetPath, + Function copyDelete) //throw FileError; fallback if move failed { - assert(fileExists(sourceFile) || symlinkExists(sourceFile) || !somethingExists(sourceFile)); //we process files and symlinks only + assert(ABF::fileExists(sourcePath) || ABF::symlinkExists(sourcePath) || !ABF::somethingExists(sourcePath)); //we process files and symlinks only auto removeTarget = [&] { //remove target object - if (dirExists(targetFile)) //directory or dir-symlink - removeDirectory(targetFile); //throw FileError; we do not expect targetFile to be a directory in general => no callback required + if (ABF::dirExists(targetPath)) //directory or dir-symlink + { + assert(false); //we do not expect targetPath to be a directory in general (but possible!) => no removeFolder() callback required! + ABF::removeFolder(targetPath); //throw FileError + } else //file or (broken) file-symlink - removeFile(targetFile); //throw FileError + ABF::removeFile(targetPath); //throw FileError }; //first try to move directly without copying try { - renameFile(sourceFile, targetFile); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting + ABF::renameItem(sourcePath, targetPath); //throw FileError, ErrorTargetExisting, ErrorDifferentVolume return; //great, we get away cheaply! } //if moving failed treat as error (except when it tried to move to a different volume: in this case we will copy the file) @@ -156,7 +157,7 @@ void moveObject(const Zstring& sourceFile, //throw FileError removeTarget(); //throw FileError try { - renameFile(sourceFile, targetFile); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting + ABF::renameItem(sourcePath, targetPath); //throw FileError, (ErrorTargetExisting), ErrorDifferentVolume } catch (const ErrorDifferentVolume&) { @@ -166,182 +167,170 @@ void moveObject(const Zstring& sourceFile, //throw FileError } -void moveFile(const Zstring& sourceFile, //throw FileError - const Zstring& targetFile, - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus) //may be nullptr +void moveFile(const AbstractPathRef& sourcePath, //throw FileError + const AbstractPathRef& targetPath, + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //may be nullptr { auto copyDelete = [&] { - assert(!somethingExists(targetFile)); + assert(!ABF::somethingExists(targetPath)); //create target - if (symlinkExists(sourceFile)) - copySymlink(sourceFile, targetFile, false); //throw FileError; don't copy filesystem permissions + if (ABF::symlinkExists(sourcePath)) + ABF::copySymlink(sourcePath, targetPath, false /*copy filesystem permissions*/); //throw FileError else - copyFile(sourceFile, targetFile, false, true, nullptr, onUpdateCopyStatus); //throw FileError - permissions "false", transactional copy "true" + ABF::copyFileTransactional(sourcePath, targetPath, //throw FileError, (ErrorFileLocked) + false /*copyFilePermissions*/, true /*transactionalCopy*/, nullptr, onNotifyCopyStatus); //delete source - removeFile(sourceFile); //throw FileError; newly copied file is NOT deleted if exception is thrown here! + ABF::removeFile(sourcePath); //throw FileError; newly copied file is NOT deleted if exception is thrown here! }; - moveObject(sourceFile, targetFile, copyDelete); //throw FileError + moveItem(sourcePath, targetPath, copyDelete); //throw FileError } -void moveDirSymlink(const Zstring& sourceLink, const Zstring& targetLink) //throw FileError +void moveDirSymlink(const AbstractPathRef& sourcePath, const AbstractPathRef& targetPath) //throw FileError { - moveObject(sourceLink, targetLink, //throw FileError - [&] + auto copyDelete = [&] //throw FileError { - //create target - copySymlink(sourceLink, targetLink, false); //throw FileError; don't copy filesystem permissions + ABF::copySymlink(sourcePath, targetPath, false /*copy filesystem permissions*/); //throw FileError + ABF::removeFolder(sourcePath); //throw FileError; newly copied link is NOT deleted if exception is thrown here! + }; - //delete source - removeDirectory(sourceLink); //throw FileError; newly copied link is NOT deleted if exception is thrown here! - }); + moveItem(sourcePath, targetPath, copyDelete); } + + +struct FlatTraverserCallback: public ABF::TraverserCallback +{ + FlatTraverserCallback(const AbstractPathRef& folderPath) : folderPath_(folderPath) {} + + void onFile (const FileInfo& fi) override { fileNames_ .push_back(fi.shortName); } + TraverserCallback* onDir (const DirInfo& di) override { folderNames_.push_back(di.shortName); return nullptr; } + HandleLink onSymlink(const SymlinkInfo& si) override + { + if (ABF::dirExists(ABF::appendRelPath(folderPath_, si.shortName))) //dir symlink + folderNames_.push_back(si.shortName); + else //file symlink, broken symlink + fileNames_.push_back(si.shortName); + return TraverserCallback::LINK_SKIP; + } + HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { throw FileError(msg); } + HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) override { throw FileError(msg); } + + const std::vector<Zstring>& refFileNames () const { return fileNames_; } + const std::vector<Zstring>& refFolderNames() const { return folderNames_; } + +private: + const AbstractPathRef& folderPath_; + std::vector<Zstring> fileNames_; + std::vector<Zstring> folderNames_; +}; } -bool FileVersioner::revisionFile(const Zstring& filepath, const Zstring& relativePath, const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus) //throw FileError +bool FileVersioner::revisionFile(const AbstractPathRef& filePath, const Zstring& relativePath, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //throw FileError { - return revisionFileImpl(filepath, relativePath, nullptr, onUpdateCopyStatus); //throw FileError + return revisionFileImpl(filePath, relativePath, nullptr, onNotifyCopyStatus); //throw FileError } -bool FileVersioner::revisionFileImpl(const Zstring& filepath, //throw FileError +bool FileVersioner::revisionFileImpl(const AbstractPathRef& filePath, //throw FileError const Zstring& relativePath, - const std::function<void(const Zstring& fileFrom, const Zstring& fileTo)>& onBeforeFileMove, - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus) + const std::function<void(const Zstring& displayPathFrom, const Zstring& displayPathTo)>& onBeforeFileMove, + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) { bool moveSuccessful = false; - moveItemToVersioning(filepath, //throw FileError + moveItemToVersioning(filePath, //throw FileError relativePath, - versioningDirectory_, + *versioningFolder_, timeStamp_, versioningStyle_, - [&](const Zstring& source, const Zstring& target) + [&](const AbstractPathRef& sourcePath, const AbstractPathRef& targetPath) { if (onBeforeFileMove) - onBeforeFileMove(source, target); //if we're called by revisionDirImpl() we know that "source" exists! - //when called by revisionFile(), "source" might not exist, however onBeforeFileMove() is not propagated in this case! + onBeforeFileMove(ABF::getDisplayPath(sourcePath), ABF::getDisplayPath(targetPath)); //if we're called by revisionFolderImpl() we know that "source" exists! + //when called by revisionFile(), "source" might not exist, but onBeforeFileMove() == nullptr in this case anyway - moveFile(source, target, onUpdateCopyStatus); //throw FileError + moveFile(sourcePath, targetPath, onNotifyCopyStatus); //throw FileError moveSuccessful = true; }); return moveSuccessful; } -void FileVersioner::revisionDir(const Zstring& dirpath, const Zstring& relativePath, //throw FileError - const std::function<void(const Zstring& fileFrom, const Zstring& fileTo)>& onBeforeFileMove, - const std::function<void(const Zstring& dirFrom, const Zstring& dirTo )>& onBeforeDirMove, - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus) +void FileVersioner::revisionFolder(const AbstractPathRef& folderPath, const Zstring& relativePath, //throw FileError + const std::function<void(const Zstring& displayPathFrom, const Zstring& displayPathTo)>& onBeforeFileMove, + const std::function<void(const Zstring& displayPathFrom, const Zstring& displayPathTo)>& onBeforeDirMove, + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) { //no error situation if directory is not existing! manual deletion relies on it! - if (!somethingExists(dirpath)) + if (!ABF::somethingExists(folderPath)) return; //neither directory nor any other object (e.g. broken symlink) with that name existing - revisionDirImpl(dirpath, relativePath, onBeforeFileMove, onBeforeDirMove, onUpdateCopyStatus); //throw FileError + revisionFolderImpl(folderPath, relativePath, onBeforeFileMove, onBeforeDirMove, onNotifyCopyStatus); //throw FileError } -void FileVersioner::revisionDirImpl(const Zstring& dirpath, const Zstring& relativePath, //throw FileError - const std::function<void(const Zstring& fileFrom, const Zstring& fileTo)>& onBeforeFileMove, - const std::function<void(const Zstring& dirFrom, const Zstring& dirTo )>& onBeforeDirMove, - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus) +void FileVersioner::revisionFolderImpl(const AbstractPathRef& folderPath, const Zstring& relativePath, //throw FileError + const std::function<void(const Zstring& displayPathFrom, const Zstring& displayPathTo)>& onBeforeFileMove, + const std::function<void(const Zstring& displayPathFrom, const Zstring& displayPathTo)>& onBeforeDirMove, + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) { - assert(somethingExists(dirpath)); //[!] + assert(ABF::somethingExists(folderPath)); //[!], see call from revisionFolder() //create target - if (symlinkExists(dirpath)) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well! + if (ABF::symlinkExists(folderPath)) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well! { - moveItemToVersioning(dirpath, //throw FileError + moveItemToVersioning(folderPath, //throw FileError relativePath, - versioningDirectory_, + *versioningFolder_, timeStamp_, versioningStyle_, - [&](const Zstring& source, const Zstring& target) + [&](const AbstractPathRef& sourcePath, const AbstractPathRef& targetPath) { if (onBeforeDirMove) - onBeforeDirMove(source, target); - moveDirSymlink(source, target); //throw FileError + onBeforeDirMove(ABF::getDisplayPath(sourcePath), ABF::getDisplayPath(targetPath)); + moveDirSymlink(sourcePath, targetPath); //throw FileError }); } else { - assert(!startsWith(relativePath, FILE_NAME_SEPARATOR)); - assert(endsWith(dirpath, relativePath)); //usually, yes, but we might relax this in the future - const Zstring targetDir = appendSeparator(versioningDirectory_) + relativePath; - - //makeDirectory(targetDir); //FileError -> create only when needed in moveFileToVersioning(); avoids empty directories + //create target directories only when needed in moveFileToVersioning(): avoid empty directories! - //traverse source directory one level - std::vector<Zstring> fileList; //list of *short* names - std::vector<Zstring> dirList; // + FlatTraverserCallback ft(folderPath); //traverse source directory one level deep + ABF::traverseFolder(folderPath, ft); - traverseFolder(dirpath, - [&](const FileInfo& fi) { fileList.push_back(fi.shortName); }, - [&](const DirInfo& di) { dirList .push_back(di.shortName); }, - [&](const SymlinkInfo& si) - { - if (dirExists(si.fullPath)) //dir symlink - dirList.push_back(si.shortName); - else //file symlink, broken symlink - fileList.push_back(si.shortName); - }, - [&](const std::wstring& errorMsg) { throw FileError(errorMsg); }); - - const Zstring dirpathPf = appendSeparator(dirpath); - const Zstring relpathPf = appendSeparator(relativePath); + const Zstring relPathPf = appendSeparator(relativePath); //move files - for (const Zstring& shortname : fileList) - revisionFileImpl(dirpathPf + shortname, //throw FileError - relpathPf + shortname, - onBeforeFileMove, onUpdateCopyStatus); - - //move items in subdirectories - for (const Zstring& shortname : dirList) - revisionDirImpl(dirpathPf + shortname, //throw FileError - relpathPf + shortname, - onBeforeFileMove, onBeforeDirMove, onUpdateCopyStatus); - + for (const Zstring& fileName : ft.refFileNames()) + revisionFileImpl(ABF::appendRelPath(folderPath, fileName), //throw FileError + relPathPf + fileName, + onBeforeFileMove, onNotifyCopyStatus); + + //move folders + for (const Zstring& folderName : ft.refFolderNames()) + revisionFolderImpl(ABF::appendRelPath(folderPath, folderName), //throw FileError + relPathPf + folderName, + onBeforeFileMove, onBeforeDirMove, onNotifyCopyStatus); //delete source if (onBeforeDirMove) - onBeforeDirMove(dirpath, targetDir); - removeDirectory(dirpath); //throw FileError + onBeforeDirMove(ABF::getDisplayPath(folderPath), ABF::getDisplayPath(versioningFolder_->getAbstractPath(relativePath))); + ABF::removeFolder(folderPath); //throw FileError } } /* -namespace -{ -class TraverseVersionsOneLevel : public TraverseCallback -{ -public: - TraverseVersionsOneLevel(std::vector<Zstring>& files, std::function<void()> updateUI) : files_(files), updateUI_(updateUI) {} - -private: - void onFile(const Zchar* shortName, const Zstring& filepath, const FileInfo& details) override { files_.push_back(shortName); updateUI_(); } - HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) override { files_.push_back(shortName); updateUI_(); return LINK_SKIP; } - std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& dirpath) override { updateUI_(); return nullptr; } //DON'T traverse into subdirs - HandleError reportDirError (const std::wstring& msg) override { throw FileError(msg); } - HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) override { throw FileError(msg); } - - std::vector<Zstring>& files_; - std::function<void()> updateUI_; -}; -} - void FileVersioner::limitVersions(std::function<void()> updateUI) //throw FileError { if (versionCountLimit_ < 0) //no limit! return; //buffer map "directory |-> list of immediate child file and symlink short names" - std::map<Zstring, std::vector<Zstring>, LessFilename> dirBuffer; + std::map<Zstring, std::vector<Zstring>, LessFilePath> dirBuffer; auto getVersionsBuffered = [&](const Zstring& dirpath) -> const std::vector<Zstring>& { @@ -377,7 +366,7 @@ void FileVersioner::limitVersions(std::function<void()> updateUI) //throw FileEr //take advantage of version naming convention to find oldest versions if (matches.size() <= static_cast<size_t>(versionCountLimit_)) return; - std::nth_element(matches.begin(), matches.end() - versionCountLimit_, matches.end(), LessFilename()); //windows: ignore case! + std::nth_element(matches.begin(), matches.end() - versionCountLimit_, matches.end(), LessFilePath()); //windows: ignore case! //delete obsolete versions std::for_each(matches.begin(), matches.end() - versionCountLimit_, diff --git a/FreeFileSync/Source/lib/versioning.h b/FreeFileSync/Source/lib/versioning.h index b6456e20..02a6c6e2 100644 --- a/FreeFileSync/Source/lib/versioning.h +++ b/FreeFileSync/Source/lib/versioning.h @@ -7,12 +7,11 @@ #ifndef VERSIONING_HEADER_8760247652438056 #define VERSIONING_HEADER_8760247652438056 -#include <vector> #include <functional> #include <zen/time.h> -#include <zen/zstring.h> #include <zen/file_error.h> #include "../structures.h" +#include "../fs/abstract.h" namespace zen { @@ -31,46 +30,51 @@ namespace zen class FileVersioner { public: - FileVersioner(const Zstring& versioningDirectory, //throw FileError + FileVersioner(std::unique_ptr<AbstractBaseFolder>&& versioningFolder, //must be bound! throw FileError! VersioningStyle versioningStyle, - const TimeComp& timeStamp) : //max versions per file; < 0 := no limit + const TimeComp& timeStamp) : versioningStyle_(versioningStyle), - versioningDirectory_(versioningDirectory), timeStamp_(formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp)) //e.g. "2012-05-15 131513" { + if (!versioningFolder) + throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); + if (timeStamp_.size() != 17) //formatTime() returns empty string on error; unexpected length: e.g. problem in year 10000! throw FileError(_("Unable to create time stamp for versioning:") + L" \"" + timeStamp_ + L"\""); + + //honor strong exception safety guarantee: + versioningFolder_ = std::move(versioningFolder); //noexcept } - bool revisionFile(const Zstring& filepath, //throw FileError; return "false" if file is not existing + bool revisionFile(const AbstractPathRef& filePath, //throw FileError; return "false" if file is not existing const Zstring& relativePath, //called frequently if move has to revert to copy + delete => see zen::copyFile for limitations when throwing exceptions! - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus); //may be nullptr + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); //may be nullptr - void revisionDir (const Zstring& dirpath, const Zstring& relativePath, //throw FileError + void revisionFolder(const AbstractPathRef& folderPath, const Zstring& relativePath, //throw FileError - //optional callbacks: may be nullptr - const std::function<void(const Zstring& fileFrom, const Zstring& fileTo)>& onBeforeFileMove, //one call for each *existing* object! - const std::function<void(const Zstring& dirFrom, const Zstring& dirTo )>& onBeforeDirMove, // - //called frequently if move has to revert to copy + delete => see zen::copyFile for limitations when throwing exceptions! - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus); + //optional callbacks: may be nullptr + const std::function<void(const Zstring& displayPathFrom, const Zstring& displayPathTo)>& onBeforeFileMove, //one call for each *existing* object! + const std::function<void(const Zstring& displayPathFrom, const Zstring& displayPathTo)>& onBeforeDirMove, // + //called frequently if move has to revert to copy + delete => see zen::copyFile for limitations when throwing exceptions! + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); //void limitVersions(std::function<void()> updateUI); //throw FileError; call when done revisioning! private: - bool revisionFileImpl(const Zstring& filepath, const Zstring& relativePath, - const std::function<void(const Zstring& fileFrom, const Zstring& fileTo)>& onBeforeFileMove, - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus); //throw FileError + bool revisionFileImpl(const AbstractPathRef& filePath, const Zstring& relativePath, + const std::function<void(const Zstring& displayPathFrom, const Zstring& displayPathTo)>& onBeforeFileMove, + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); //throw FileError - void revisionDirImpl (const Zstring& filepath, const Zstring& relativePath, - const std::function<void(const Zstring& fileFrom, const Zstring& fileTo)>& onBeforeFileMove, - const std::function<void(const Zstring& dirFrom, const Zstring& dirTo )>& onBeforeDirMove, - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus); //throw FileError + void revisionFolderImpl(const AbstractPathRef& folderPath, const Zstring& relativePath, + const std::function<void(const Zstring& displayPathFrom, const Zstring& displayPathTo)>& onBeforeFileMove, + const std::function<void(const Zstring& displayPathFrom, const Zstring& displayPathTo)>& onBeforeDirMove, + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); //throw FileError const VersioningStyle versioningStyle_; - const Zstring versioningDirectory_; const Zstring timeStamp_; + std::unique_ptr<AbstractBaseFolder> versioningFolder_; //always bound! //std::vector<Zstring> fileRelNames; //store list of revisioned file and symlink relative names for limitVersions() }; diff --git a/FreeFileSync/Source/structures.cpp b/FreeFileSync/Source/structures.cpp index 0d229dc4..8db6bb00 100644 --- a/FreeFileSync/Source/structures.cpp +++ b/FreeFileSync/Source/structures.cpp @@ -391,12 +391,8 @@ FilterConfig mergeFilterConfig(const FilterConfig& global, const FilterConfig& l inline bool effectivelyEmpty(const FolderPairEnh& fp) { - auto isEmpty = [](Zstring dirpath) - { - trim(dirpath); - return dirpath.empty(); - }; - return isEmpty(fp.dirpathPhraseLeft) && isEmpty(fp.dirpathPhraseRight); + return trimCpy(fp.dirpathPhraseLeft ).empty() && + trimCpy(fp.dirpathPhraseRight).empty(); } } diff --git a/FreeFileSync/Source/structures.h b/FreeFileSync/Source/structures.h index 8b183375..ac07c11a 100644 --- a/FreeFileSync/Source/structures.h +++ b/FreeFileSync/Source/structures.h @@ -222,7 +222,7 @@ struct SyncConfig DeletionPolicy handleDeletion; //use Recycle, delete permanently or move to user-defined location //versioning options VersioningStyle versioningStyle; - Zstring versioningDirectory; + Zstring versioningFolderPhrase; //int versionCountLimit; //max versions per file (DELETE_TO_VERSIONING); < 0 := no limit }; @@ -230,10 +230,10 @@ struct SyncConfig inline bool operator==(const SyncConfig& lhs, const SyncConfig& rhs) { - return lhs.directionCfg == rhs.directionCfg && - lhs.handleDeletion == rhs.handleDeletion && - lhs.versioningStyle == rhs.versioningStyle && - lhs.versioningDirectory == rhs.versioningDirectory; + return lhs.directionCfg == rhs.directionCfg && + lhs.handleDeletion == rhs.handleDeletion && + lhs.versioningStyle == rhs.versioningStyle && + lhs.versioningFolderPhrase == rhs.versioningFolderPhrase; //adapt effectivelyEqual() on changes, too! } @@ -245,7 +245,7 @@ bool effectivelyEqual(const SyncConfig& lhs, const SyncConfig& rhs) lhs.handleDeletion == rhs.handleDeletion && (lhs.handleDeletion != DELETE_TO_VERSIONING || //only compare deletion directory if required! (lhs.versioningStyle == rhs.versioningStyle && - lhs.versioningDirectory == rhs.versioningDirectory)); + lhs.versioningFolderPhrase == rhs.versioningFolderPhrase)); } diff --git a/FreeFileSync/Source/synchronization.cpp b/FreeFileSync/Source/synchronization.cpp index eb5b4b3d..7181b3e2 100644 --- a/FreeFileSync/Source/synchronization.cpp +++ b/FreeFileSync/Source/synchronization.cpp @@ -5,29 +5,17 @@ // ************************************************************************** #include "synchronization.h" -#include <memory> -#include <deque> -#include <stdexcept> -#include <zen/format_unit.h> -#include <zen/scope_guard.h> #include <zen/process_priority.h> -#include <zen/file_access.h> -#include <zen/recycler.h> -#include <zen/optional.h> -#include <zen/symlink_target.h> -#include <zen/file_io.h> -#include <zen/time.h> -#include "lib/resolve_path.h" +#include <zen/perf.h> #include "lib/db_file.h" #include "lib/dir_exist_async.h" -#include "lib/cmp_filetime.h" #include "lib/status_handler_impl.h" #include "lib/versioning.h" #include "lib/binary.h" +#include "fs/concrete.h" #ifdef ZEN_WIN #include <zen/long_path_prefix.h> - #include <zen/perf.h> #include "lib/shadow.h" #elif defined ZEN_LINUX || defined ZEN_MAC @@ -280,7 +268,7 @@ std::vector<zen::FolderPairSyncCfg> zen::extractSyncCfg(const MainConfiguration& FolderPairSyncCfg(syncCfg.directionCfg.var == DirectionConfig::TWOWAY || detectMovedFilesEnabled(syncCfg.directionCfg), syncCfg.handleDeletion, syncCfg.versioningStyle, - getFormattedDirectoryPath(syncCfg.versioningDirectory), + syncCfg.versioningFolderPhrase, syncCfg.directionCfg.var)); } return output; @@ -311,158 +299,14 @@ bool significantDifferenceDetected(const SyncStatistics& folderPairStat) //################################################################################################################# -class RecycleSession -{ -public: - RecycleSession(const Zstring baseDirPf) : baseDirPf_(baseDirPf) {} - - bool recycleItem(const Zstring& fullPath, const Zstring& relPath); //throw FileError - void tryCleanup(const std::function<void (const Zstring& currentItem)>& notifyDeletionStatus /*optional; currentItem may be empty*/); //throw FileError - -private: - const Zstring baseDirPf_; //ends with path separator - -#ifdef ZEN_WIN - Zstring getOrCreateRecyclerTempDirPf(); //throw FileError - - std::vector<Zstring> toBeRecycled; //full path of files located in temporary folder, waiting for batch-recycling - Zstring recyclerTmpDir; //temporary folder holding files/folders for *deferred* recycling -#endif -}; - - -#ifdef ZEN_WIN -//create + returns temporary directory postfixed with file name separator -//to support later cleanup if automatic deletion fails for whatever reason -Zstring RecycleSession::getOrCreateRecyclerTempDirPf() //throw FileError -{ - assert(!baseDirPf_.empty()); - if (baseDirPf_.empty()) - return Zstring(); - - if (recyclerTmpDir.empty()) - recyclerTmpDir = [&] - { - assert(endsWith(baseDirPf_, FILE_NAME_SEPARATOR)); - /* - -> this naming convention is too cute and confusing for end users: - - //1. generate random directory name - static std::mt19937 rng(std::time(nullptr)); //don't use std::default_random_engine which leaves the choice to the STL implementer! - //- the alternative std::random_device may not always be available and can even throw an exception! - //- seed with second precision is sufficient: collisions are handled below - - const Zstring chars(Zstr("abcdefghijklmnopqrstuvwxyz") - Zstr("1234567890")); - std::uniform_int_distribution<size_t> distrib(0, chars.size() - 1); //takes closed range - - auto generatePath = [&]() -> Zstring //e.g. C:\Source\3vkf74fq.ffs_tmp - { - Zstring path = baseDirPf; - for (int i = 0; i < 8; ++i) - path += chars[distrib(rng)]; - return path + TEMP_FILE_ENDING; - }; - */ - - //ensure unique ownership: - Zstring dirpath = baseDirPf_ + Zstr("RecycleBin") + TEMP_FILE_ENDING; - for (int i = 0;; ++i) - try - { - makeDirectory(dirpath, /*bool failIfExists*/ true); //throw FileError, ErrorTargetExisting - return dirpath; - } - catch (const ErrorTargetExisting&) - { - if (i == 10) throw; //avoid endless recursion in pathological cases - dirpath = baseDirPf_ + Zstr("RecycleBin") + Zchar('_') + numberTo<Zstring>(i) + TEMP_FILE_ENDING; - } - }(); - - //assemble temporary recycle bin directory with random name and .ffs_tmp ending - return appendSeparator(recyclerTmpDir); -} -#endif - - -bool RecycleSession::recycleItem(const Zstring& itemPath, const Zstring& relPath) //throw FileError -{ -#ifdef ZEN_WIN - const Zstring tmpPath = getOrCreateRecyclerTempDirPf() + relPath; //throw FileError - bool deleted = false; - - auto moveToTempDir = [&] - { - try - { - //performance optimization: Instead of moving each object into recycle bin separately, - //we rename them one by one into a temporary directory and batch-recycle this directory after sync - renameFile(itemPath, tmpPath); //throw FileError, ErrorDifferentVolume - this->toBeRecycled.push_back(tmpPath); - deleted = true; - } - catch (ErrorDifferentVolume&) //MoveFileEx() returns ERROR_PATH_NOT_FOUND *before* considering ERROR_NOT_SAME_DEVICE! => we have to create tmpParentDir anyway to find out! - { - deleted = recycleOrDelete(itemPath); //throw FileError - } - }; - - try - { - moveToTempDir(); //throw FileError, ErrorDifferentVolume - } - catch (FileError&) - { - if (somethingExists(itemPath)) - { - const Zstring tmpParentDir = beforeLast(tmpPath, FILE_NAME_SEPARATOR); //what if C:\ ? - if (!dirExists(tmpParentDir)) - { - makeDirectory(tmpParentDir); //throw FileError -> may legitimately fail on Linux if permissions are missing - moveToTempDir(); //throw FileError -> this should work now! - } - else - throw; - } - } - return deleted; - -#elif defined ZEN_LINUX || defined ZEN_MAC - return recycleOrDelete(itemPath); //throw FileError -#endif -} - - -void RecycleSession::tryCleanup(const std::function<void (const Zstring& currentItem)>& notifyDeletionStatus) //throw FileError -{ -#ifdef ZEN_WIN - if (!toBeRecycled.empty()) - { - //move content of temporary directory to recycle bin in a single call - recycleOrDelete(toBeRecycled, notifyDeletionStatus); //throw FileError - toBeRecycled.clear(); - } - - //clean up temp directory itself (should contain remnant empty directories only) - if (!recyclerTmpDir.empty()) - { - removeDirectory(recyclerTmpDir); //throw FileError - recyclerTmpDir.clear(); - } -#endif -} - -//################################################################################################################# - class DeletionHandling //abstract deletion variants: permanently, recycle bin, user-defined directory { public: - DeletionHandling(DeletionPolicy handleDel, //nothrow! - const Zstring& versioningDir, + DeletionHandling(ABF& baseFolder, + DeletionPolicy handleDel, //nothrow! + const Zstring& versioningFolderPhrase, VersioningStyle versioningStyle, const TimeComp& timeStamp, - const Zstring& baseDirPf, //with separator postfix ProcessCallback& procCallback); ~DeletionHandling() { @@ -482,9 +326,9 @@ public: //clean-up temporary directory (recycle bin optimization) void tryCleanup(bool allowUserCallback); //throw FileError; throw X -> call this in non-exceptional coding, i.e. somewhere after sync! - template <class Function> void removeFileWithCallback (const Zstring& filepath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyFileCopy); // - template <class Function> void removeDirWithCallback (const Zstring& dirpath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyFileCopy); //throw FileError - template <class Function> void removeLinkWithCallback (const Zstring& linkpath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyFileCopy); // + template <class Function> void removeFileWithCallback (const AbstractPathRef& filePath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); // + template <class Function> void removeDirWithCallback (const AbstractPathRef& dirPath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); //throw FileError + template <class Function> void removeLinkWithCallback (const AbstractPathRef& linkPath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); // const std::wstring& getTxtRemovingFile () const { return txtRemovingFile; } // const std::wstring& getTxtRemovingSymLink() const { return txtRemovingSymlink; } //buffered status texts @@ -494,23 +338,34 @@ private: DeletionHandling (const DeletionHandling&) = delete; DeletionHandling& operator=(const DeletionHandling&) = delete; - FileVersioner& getOrCreateVersioner() //throw FileError! => dont create in DeletionHandling()!!! + FileVersioner& getOrCreateVersioner() //throw FileError => dont create in constructor!!! { + assert(deletionPolicy_ == DELETE_TO_VERSIONING); if (!versioner.get()) - versioner = zen::make_unique<FileVersioner>(versioningDir_, versioningStyle_, timeStamp_); //throw FileError + versioner = zen::make_unique<FileVersioner>(std::move(versioningFolder), versioningStyle_, timeStamp_); //throw FileError return *versioner; }; + ABF::RecycleSession& getOrCreateRecyclerSession() //throw FileError => dont create in constructor!!! + { + assert(deletionPolicy_ == DELETE_TO_RECYCLER); + if (!recyclerSession.get()) + recyclerSession = baseFolder_.createRecyclerSession(); //throw FileError + return *recyclerSession; + }; + ProcessCallback& procCallback_; - const Zstring versioningDir_; - const VersioningStyle versioningStyle_; - const TimeComp timeStamp_; - //manage three states: allow dynamic fallback from recycler to permanent deletion - const DeletionPolicy deletionPolicy_; - std::unique_ptr<FileVersioner> versioner; //used for DELETE_TO_VERSIONING; throw FileError in constructor => create on demand! + const DeletionPolicy deletionPolicy_; //keep it invariant! e.g. consider getOrCreateVersioner() one-time construction! + + ABF& baseFolder_; + std::unique_ptr<ABF::RecycleSession> recyclerSession; - RecycleSession recycler; + //used only for DELETE_TO_VERSIONING: + const VersioningStyle versioningStyle_; + const TimeComp timeStamp_; + std::unique_ptr<AbstractBaseFolder> versioningFolder; //bound until first call to getOrCreateVersioner()!!! + std::unique_ptr<FileVersioner> versioner; //throw FileError in constructor => create on demand! //buffer status texts: std::wstring txtRemovingFile; @@ -522,18 +377,17 @@ private: }; -DeletionHandling::DeletionHandling(DeletionPolicy handleDel, //nothrow! - const Zstring& versioningDir, +DeletionHandling::DeletionHandling(ABF& baseFolder, + DeletionPolicy handleDel, //nothrow! + const Zstring& versioningFolderPhrase, VersioningStyle versioningStyle, const TimeComp& timeStamp, - const Zstring& baseDirPf, //with separator postfix ProcessCallback& procCallback) : procCallback_(procCallback), - versioningDir_(versioningDir), + deletionPolicy_(handleDel), + baseFolder_(baseFolder), versioningStyle_(versioningStyle), timeStamp_(timeStamp), - deletionPolicy_(handleDel), - recycler(baseDirPf), txtMovingFile (_("Moving file %x to %y")), txtMovingFolder(_("Moving folder %x to %y")) { @@ -552,10 +406,15 @@ DeletionHandling::DeletionHandling(DeletionPolicy handleDel, //nothrow! break; case DELETE_TO_VERSIONING: - txtRemovingFile = replaceCpy(_("Moving file %x to %y" ), L"%y", fmtFileName(versioningDir_)); - txtRemovingDirectory = replaceCpy(_("Moving folder %x to %y" ), L"%y", fmtFileName(versioningDir_)); - txtRemovingSymlink = replaceCpy(_("Moving symbolic link %x to %y"), L"%y", fmtFileName(versioningDir_)); - break; + { + versioningFolder = createAbstractBaseFolder(versioningFolderPhrase); //noexcept + const std::wstring versioningPathFmt = fmtFileName(ABF::getDisplayPath(versioningFolder->getAbstractPath())); + + txtRemovingFile = replaceCpy(_("Moving file %x to %y" ), L"%y", versioningPathFmt); + txtRemovingDirectory = replaceCpy(_("Moving folder %x to %y" ), L"%y", versioningPathFmt); + txtRemovingSymlink = replaceCpy(_("Moving symbolic link %x to %y"), L"%y", versioningPathFmt); + } + break; } } @@ -568,22 +427,23 @@ void DeletionHandling::tryCleanup(bool allowUserCallback) //throw FileError; thr break; case DELETE_TO_RECYCLER: - { - auto notifyDeletionStatus = [&](const Zstring& currentItem) + if (recyclerSession.get()) { - if (!currentItem.empty()) - procCallback_.reportStatus(replaceCpy(txtRemovingFile, L"%x", fmtFileName(currentItem))); //throw ? - else - procCallback_.requestUiRefresh(); //throw ? - }; + auto notifyDeletionStatus = [&](const Zstring& displayPath) + { + if (!displayPath.empty()) + procCallback_.reportStatus(replaceCpy(txtRemovingFile, L"%x", fmtFileName(displayPath))); //throw ? + else + procCallback_.requestUiRefresh(); //throw ? + }; - //move content of temporary directory to recycle bin in a single call - if (allowUserCallback) - recycler.tryCleanup(notifyDeletionStatus); //throw FileError - else - recycler.tryCleanup(nullptr); //throw FileError - } - break; + //move content of temporary directory to recycle bin in a single call + if (allowUserCallback) + getOrCreateRecyclerSession().tryCleanup(notifyDeletionStatus); //throw FileError + else + getOrCreateRecyclerSession().tryCleanup(nullptr); //throw FileError + } + break; case DELETE_TO_VERSIONING: //if (versioner.get()) @@ -602,44 +462,43 @@ void DeletionHandling::tryCleanup(bool allowUserCallback) //throw FileError; thr template <class Function> -void DeletionHandling::removeDirWithCallback(const Zstring& dirpath, +void DeletionHandling::removeDirWithCallback(const AbstractPathRef& dirPath, const Zstring& relativePath, Function onNotifyItemDeletion, - const std::function<void(std::int64_t bytesDelta)>& onNotifyFileCopy) //throw FileError + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //throw FileError { switch (deletionPolicy_) { case DELETE_PERMANENTLY: { - auto notifyDeletion = [&](const std::wstring& statusText, const Zstring& objName) + auto notifyDeletion = [&](const std::wstring& statusText, const Zstring& displayPath) { onNotifyItemDeletion(); //it would be more correct to report *after* work was done! - procCallback_.reportStatus(replaceCpy(statusText, L"%x", fmtFileName(objName))); + procCallback_.reportStatus(replaceCpy(statusText, L"%x", fmtFileName(displayPath))); }; - auto onBeforeFileDeletion = [&](const Zstring& filepath) { notifyDeletion(txtRemovingFile, filepath); }; - auto onBeforeDirDeletion = [&](const Zstring& dirpath2) { notifyDeletion(txtRemovingDirectory, dirpath2); }; + auto onBeforeFileDeletion = [&](const Zstring& displayPath) { notifyDeletion(txtRemovingFile, displayPath); }; + auto onBeforeDirDeletion = [&](const Zstring& displayPath) { notifyDeletion(txtRemovingDirectory, displayPath); }; - removeDirectory(dirpath, onBeforeFileDeletion, onBeforeDirDeletion); + ABF::removeFolder(dirPath, onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError } break; case DELETE_TO_RECYCLER: - if (recycler.recycleItem(dirpath, relativePath)) //throw FileError + if (getOrCreateRecyclerSession().recycleItem(dirPath, relativePath)) //throw FileError; return true if item existed onNotifyItemDeletion(); //moving to recycler is ONE logical operation, irrespective of the number of child elements! break; case DELETE_TO_VERSIONING: { - auto notifyMove = [&](const std::wstring& statusText, const Zstring& fileFrom, const Zstring& fileTo) + auto notifyMove = [&](const std::wstring& statusText, const Zstring& displayPathFrom, const Zstring& displayPathTo) { onNotifyItemDeletion(); //it would be more correct to report *after* work was done! - procCallback_.reportStatus(replaceCpy(replaceCpy(statusText, L"%x", L"\n" + fmtFileName(fileFrom)), L"%y", L"\n" + fmtFileName(fileTo))); + procCallback_.reportStatus(replaceCpy(replaceCpy(statusText, L"%x", L"\n" + fmtFileName(displayPathFrom)), L"%y", L"\n" + fmtFileName(displayPathTo))); }; + auto onBeforeFileMove = [&](const Zstring& displayPathFrom, const Zstring& displayPathTo) { notifyMove(txtMovingFile, displayPathFrom, displayPathTo); }; + auto onBeforeDirMove = [&](const Zstring& displayPathFrom, const Zstring& displayPathTo) { notifyMove(txtMovingFolder, displayPathFrom, displayPathTo); }; - auto onBeforeFileMove = [&](const Zstring& fileFrom, const Zstring& fileTo) { notifyMove(txtMovingFile, fileFrom, fileTo); }; - auto onBeforeDirMove = [&](const Zstring& dirFrom, const Zstring& dirTo ) { notifyMove(txtMovingFolder, dirFrom, dirTo); }; - - getOrCreateVersioner().revisionDir(dirpath, relativePath, onBeforeFileMove, onBeforeDirMove, onNotifyFileCopy); //throw FileError + getOrCreateVersioner().revisionFolder(dirPath, relativePath, onBeforeFileMove, onBeforeDirMove, onNotifyCopyStatus); //throw FileError } break; } @@ -647,28 +506,28 @@ void DeletionHandling::removeDirWithCallback(const Zstring& dirpath, template <class Function> -void DeletionHandling::removeFileWithCallback(const Zstring& filepath, +void DeletionHandling::removeFileWithCallback(const AbstractPathRef& filePath, const Zstring& relativePath, Function onNotifyItemDeletion, - const std::function<void(std::int64_t bytesDelta)>& onNotifyFileCopy) //throw FileError + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //throw FileError { bool deleted = false; - if (endsWith(relativePath, TEMP_FILE_ENDING)) //special rule for .ffs_tmp files: always delete permanently! - deleted = zen::removeFile(filepath); + if (endsWith(relativePath, ABF::TEMP_FILE_ENDING)) //special rule for .ffs_tmp files: always delete permanently! + deleted = ABF::removeFile(filePath); //throw FileError else switch (deletionPolicy_) { case DELETE_PERMANENTLY: - deleted = zen::removeFile(filepath); //[!] scope specifier resolves nameclash! + deleted = ABF::removeFile(filePath); //throw FileError break; case DELETE_TO_RECYCLER: - deleted = recycler.recycleItem(filepath, relativePath); //throw FileError + deleted = getOrCreateRecyclerSession().recycleItem(filePath, relativePath); //throw FileError; return true if item existed break; case DELETE_TO_VERSIONING: - deleted = getOrCreateVersioner().revisionFile(filepath, relativePath, onNotifyFileCopy); //throw FileError + deleted = getOrCreateVersioner().revisionFile(filePath, relativePath, onNotifyCopyStatus); //throw FileError break; } if (deleted) @@ -677,12 +536,12 @@ void DeletionHandling::removeFileWithCallback(const Zstring& filepath, template <class Function> inline -void DeletionHandling::removeLinkWithCallback(const Zstring& linkpath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyFileCopy) //throw FileError +void DeletionHandling::removeLinkWithCallback(const AbstractPathRef& linkPath, const Zstring& relativePath, Function onNotifyItemDeletion, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //throw FileError { - if (dirExists(linkpath)) //dir symlink - return removeDirWithCallback(linkpath, relativePath, onNotifyItemDeletion, onNotifyFileCopy); //throw FileError + if (ABF::dirExists(linkPath)) //dir symlink + return removeDirWithCallback(linkPath, relativePath, onNotifyItemDeletion, onNotifyCopyStatus); //throw FileError else //file symlink, broken symlink - return removeFileWithCallback(linkpath, relativePath, onNotifyItemDeletion, onNotifyFileCopy); //throw FileError + return removeFileWithCallback(linkPath, relativePath, onNotifyItemDeletion, onNotifyCopyStatus); //throw FileError } //------------------------------------------------------------------------------------------------------------ @@ -851,10 +710,10 @@ private: procCallback_.reportInfo(replaceCpy(replaceCpy(rawText, L"%x", L"\n" + fmtFileName(objname1)), L"%y", L"\n" + fmtFileName(objname2))); }; - InSyncAttributes copyFileWithCallback(const Zstring& sourceFile, - const Zstring& targetFile, - const std::function<void()>& onDeleteTargetFile, - const std::function<void(std::int64_t bytesDelta)>& onNotifyFileCopy) const; //throw FileError + ABF::FileAttribAfterCopy copyFileWithCallback(const AbstractPathRef& sourcePath, + const AbstractPathRef& targetPath, + const std::function<void()>& onDeleteTargetFile, + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) const; //throw FileError template <SelectedSide side> DeletionHandling& getDelHandling(); @@ -922,7 +781,7 @@ template <class List> inline bool haveNameClash(const Zstring& shortname, List& m) { return std::any_of(m.begin(), m.end(), - [&](const typename List::value_type& obj) { return EqualFilename()(obj.getPairShortName(), shortname); }); + [&](const typename List::value_type& obj) { return EqualFilePath()(obj.getPairShortName(), shortname); }); } @@ -930,8 +789,7 @@ template <SelectedSide side> void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj, FilePair& targetObj) //throw FileError { - const Zstring& source = sourceObj.getFullPath<side>(); - Zstring tmpTarget = sourceObj.getBaseDirPf<side>() + sourceObj.getItemName<side>() + TEMP_FILE_ENDING; + Zstring sourceRelPathTmp = sourceObj.getItemName<side>() + ABF::TEMP_FILE_ENDING; //this could still lead to a name-clash in obscure cases, if some file exists on the other side with //the very same (.ffs_tmp) name and is copied before the second step of the move is executed //good news: even in this pathologic case, this may only prevent the copy of the other file, but not the move @@ -939,14 +797,19 @@ void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj, for (int i = 0;; ++i) try { - reportInfo(txtMovingFile, source, tmpTarget); - renameFile(source, tmpTarget); //throw FileError, ErrorTargetExisting + AbstractPathRef sourcePathTmp = sourceObj.getABF<side>().getAbstractPath(sourceRelPathTmp); + + reportInfo(txtMovingFile, + ABF::getDisplayPath(sourceObj.getAbstractPath<side>()), + ABF::getDisplayPath(sourcePathTmp)); + + ABF::renameItem(sourceObj.getAbstractPath<side>(), sourcePathTmp); //throw FileError, ErrorTargetExisting, (ErrorDifferentVolume) break; } catch (const ErrorTargetExisting&) //repeat until unique name found: no file system race condition! { if (i == 10) throw; //avoid endless recursion in pathological cases - tmpTarget = sourceObj.getBaseDirPf<side>() + sourceObj.getItemName<side>() + Zchar('_') + numberTo<Zstring>(i) + TEMP_FILE_ENDING; + sourceRelPathTmp = sourceObj.getItemName<side>() + Zchar('_') + numberTo<Zstring>(i) + ABF::TEMP_FILE_ENDING; } warn_static("was wenn diff volume: symlink aliasing!") //throw FileError, ErrorDifferentVolume, ErrorTargetExisting @@ -957,7 +820,7 @@ void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj, sourceObj.getFileId <side>(), sourceObj.isFollowedSymlink<side>()); - FilePair& tempFile = sourceObj.root().addSubFile<side>(afterLast(tmpTarget, FILE_NAME_SEPARATOR), descrSource); + FilePair& tempFile = sourceObj.root().addSubFile<side>(afterLast(sourceRelPathTmp, FILE_NAME_SEPARATOR), descrSource); static_assert(IsSameType<FixedList<FilePair>, HierarchyObject::SubFileVec>::value, "ATTENTION: we're adding to the file list WHILE looping over it! This is only working because FixedList iterators are not invalidated by insertion!"); sourceObj.removeObject<side>(); //remove only *after* evaluating "sourceObj, side"! @@ -1314,18 +1177,19 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation if (parentDir->isEmpty<sideTrg>()) //BaseDirPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() return; //if parent directory creation failed, there's no reason to show more errors! - const Zstring& target = fileObj.getBaseDirPf<sideTrg>() + fileObj.getRelativePath<sideSrc>(); //can't use "getFullPath" as target is not yet existing - reportInfo(txtCreatingFile, target); + //can't use "getAbstractPath<sideTrg>()" as file name is not available! + const AbstractPathRef targetPath = fileObj.getABF<sideTrg>().getAbstractPath(fileObj.getRelativePath<sideSrc>()); + reportInfo(txtCreatingFile, ABF::getDisplayPath(targetPath)); StatisticsReporter statReporter(1, fileObj.getFileSize<sideSrc>(), procCallback_); try { - auto onNotifyFileCopy = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; + auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - const InSyncAttributes newAttr = copyFileWithCallback(fileObj.getFullPath<sideSrc>(), - target, - nullptr, //no target to delete - onNotifyFileCopy); //throw FileError + const ABF::FileAttribAfterCopy newAttr = copyFileWithCallback(fileObj.getAbstractPath<sideSrc>(), + targetPath, + nullptr, //no target to delete + onNotifyCopyStatus); //throw FileError statReporter.reportDelta(1, 0); //update FilePair @@ -1343,7 +1207,7 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation //if (!dirExists(basedir) || - if (!somethingExists(fileObj.getFullPath<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! + if (!ABF::somethingExists(fileObj.getAbstractPath<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! { //source deleted meanwhile...nothing was done (logical point of view!) fileObj.removeObject<sideSrc>(); //remove only *after* evaluating "fileObj, sideSrc"! @@ -1357,14 +1221,14 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation case SO_DELETE_LEFT: case SO_DELETE_RIGHT: - reportInfo(getDelHandling<sideTrg>().getTxtRemovingFile(), fileObj.getFullPath<sideTrg>()); + reportInfo(getDelHandling<sideTrg>().getTxtRemovingFile(), ABF::getDisplayPath(fileObj.getAbstractPath<sideTrg>())); { StatisticsReporter statReporter(1, 0, procCallback_); auto onNotifyItemDeletion = [&] { statReporter.reportDelta(1, 0); }; - auto onNotifyFileCopy = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; + auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - getDelHandling<sideTrg>().removeFileWithCallback(fileObj.getFullPath<sideTrg>(), fileObj.getPairRelativePath(), onNotifyItemDeletion, onNotifyFileCopy); //throw FileError + getDelHandling<sideTrg>().removeFileWithCallback(fileObj.getAbstractPath<sideTrg>(), fileObj.getPairRelativePath(), onNotifyItemDeletion, onNotifyCopyStatus); //throw FileError warn_static("what if item not found? still an error if base dir is missing; externally deleted otherwise!") @@ -1383,12 +1247,12 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation assert((moveSource->getSyncOperation() == SO_MOVE_LEFT_SOURCE && moveTarget->getSyncOperation() == SO_MOVE_LEFT_TARGET && sideTrg == LEFT_SIDE) || (moveSource->getSyncOperation() == SO_MOVE_RIGHT_SOURCE && moveTarget->getSyncOperation() == SO_MOVE_RIGHT_TARGET && sideTrg == RIGHT_SIDE)); - const Zstring& oldName = moveSource->getFullPath<sideTrg>(); - const Zstring& newName = moveSource->getBaseDirPf<sideTrg>() + moveTarget->getRelativePath<sideSrc>(); + const AbstractPathRef oldItem = moveSource->getAbstractPath<sideTrg>(); + const AbstractPathRef newItem = moveTarget->getABF<sideTrg>().getAbstractPath(moveTarget->getRelativePath<sideSrc>()); - reportInfo(txtMovingFile, oldName, newName); - warn_static("was wenn diff volume: symlink aliasing!") //throw FileError, ErrorDifferentVolume, ErrorTargetExisting - renameFile(oldName, newName); //throw FileError + reportInfo(txtMovingFile, ABF::getDisplayPath(oldItem), ABF::getDisplayPath(newItem)); + warn_static("was wenn diff volume: symlink aliasing!") + ABF::renameItem(oldItem, newItem); //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) //update FilePair assert(moveSource->getFileSize<sideTrg>() == moveTarget->getFileSize<sideSrc>()); @@ -1409,26 +1273,26 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation case SO_OVERWRITE_LEFT: case SO_OVERWRITE_RIGHT: { - const Zstring targetFile = fileObj.isFollowedSymlink<sideTrg>() ? //follow link when updating file rather than delete it and replace with regular file!!! - zen::getResolvedFilePath(fileObj.getFullPath<sideTrg>()) : //throw FileError - fileObj.getBaseDirPf<sideTrg>() + fileObj.getRelativePath<sideSrc>(); //respect differences in case of source object + const AbstractPathRef resolvedTargetPath = fileObj.isFollowedSymlink<sideTrg>() ? //follow link when updating file rather than delete it and replace with regular file!!! + ABF::getResolvedSymlinkPath(fileObj.getAbstractPath<sideTrg>()) : //throw FileError + fileObj.getABF<sideTrg>().getAbstractPath(fileObj.getRelativePath<sideSrc>()); //respect differences in case of source object - reportInfo(txtOverwritingFile, targetFile); + reportInfo(txtOverwritingFile, ABF::getDisplayPath(resolvedTargetPath)); - if (fileObj.isFollowedSymlink<sideTrg>()) //since we follow the link, we need to handle case sensitivity of the link manually! - if (fileObj.getItemName<sideTrg>() != fileObj.getItemName<sideSrc>()) //adapt difference in case (windows only) - renameFile(fileObj.getFullPath<sideTrg>(), - beforeLast(fileObj.getFullPath<sideTrg>(), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + fileObj.getItemName<sideSrc>()); //throw FileError + if (fileObj.isFollowedSymlink<sideTrg>()) //since we follow the link, we need to sync case sensitivity of the link manually! + if (fileObj.getItemName<sideTrg>() != fileObj.getItemName<sideSrc>()) //have difference in case? + ABF::renameItem(fileObj.getAbstractPath<sideTrg>(), //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) + fileObj.getABF<sideTrg>().getAbstractPath(fileObj.getRelativePath<sideSrc>())); StatisticsReporter statReporter(1, fileObj.getFileSize<sideSrc>(), procCallback_); - auto onNotifyFileCopy = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; + auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; auto onDeleteTargetFile = [&] //delete target at appropriate time { - reportStatus(this->getDelHandling<sideTrg>().getTxtRemovingFile(), targetFile); + reportStatus(this->getDelHandling<sideTrg>().getTxtRemovingFile(), ABF::getDisplayPath(resolvedTargetPath)); - this->getDelHandling<sideTrg>().removeFileWithCallback(targetFile, fileObj.getPairRelativePath(), [] {}, onNotifyFileCopy); //throw FileError; + this->getDelHandling<sideTrg>().removeFileWithCallback(resolvedTargetPath, fileObj.getPairRelativePath(), [] {}, onNotifyCopyStatus); //throw FileError; //no (logical) item count update desired - but total byte count may change, e.g. move(copy) deleted file to versioning dir //fileObj.removeObject<sideTrg>(); -> doesn't make sense for isFollowedSymlink(); "fileObj, sideTrg" evaluated below! @@ -1436,13 +1300,13 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation //if fail-safe file copy is active, then the next operation will be a simple "rename" //=> don't risk reportStatus() throwing GuiAbortProcess() leaving the target deleted rather than updated! if (!transactionalFileCopy_) - reportStatus(txtOverwritingFile, targetFile); //restore status text copy file + reportStatus(txtOverwritingFile, ABF::getDisplayPath(resolvedTargetPath)); //restore status text copy file }; - const InSyncAttributes newAttr = copyFileWithCallback(fileObj.getFullPath<sideSrc>(), - targetFile, - onDeleteTargetFile, - onNotifyFileCopy); //throw FileError + const ABF::FileAttribAfterCopy newAttr = copyFileWithCallback(fileObj.getAbstractPath<sideSrc>(), + resolvedTargetPath, + onDeleteTargetFile, + onNotifyCopyStatus); //throw FileError statReporter.reportDelta(1, 0); //we model "delete + copy" as ONE logical operation //update FilePair @@ -1462,16 +1326,16 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation case SO_COPY_METADATA_TO_RIGHT: //harmonize with file_hierarchy.cpp::getSyncOpDescription!! - reportInfo(txtWritingAttributes, fileObj.getFullPath<sideTrg>()); + reportInfo(txtWritingAttributes, ABF::getDisplayPath(fileObj.getAbstractPath<sideTrg>())); - if (fileObj.getItemName<sideTrg>() != fileObj.getItemName<sideSrc>()) //adapt difference in case (windows only) - renameFile(fileObj.getFullPath<sideTrg>(), - beforeLast(fileObj.getFullPath<sideTrg>(), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + fileObj.getItemName<sideSrc>()); //throw FileError + if (fileObj.getItemName<sideTrg>() != fileObj.getItemName<sideSrc>()) //have difference in case? + ABF::renameItem(fileObj.getAbstractPath<sideTrg>(), //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) + fileObj.getABF<sideTrg>().getAbstractPath(fileObj.getRelativePath<sideSrc>())); if (fileObj.getLastWriteTime<sideTrg>() != fileObj.getLastWriteTime<sideSrc>()) //- no need to call sameFileTime() or respect 2 second FAT/FAT32 precision in this comparison //- do NOT read *current* source file time, but use buffered value which corresponds to time of comparison! - setFileTime(fileObj.getFullPath<sideTrg>(), fileObj.getLastWriteTime<sideSrc>(), ProcSymlink::FOLLOW); //throw FileError + ABF::setFileTime(fileObj.getAbstractPath<sideTrg>(), fileObj.getLastWriteTime<sideSrc>(), ABF::SymlinkHandling::FOLLOW); //throw FileError //-> both sides *should* be completely equal now... assert(fileObj.getFileSize<sideTrg>() == fileObj.getFileSize<sideSrc>()); @@ -1528,14 +1392,13 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperati if (parentDir->isEmpty<sideTrg>()) //BaseDirPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() return; //if parent directory creation failed, there's no reason to show more errors! - const Zstring& target = linkObj.getBaseDirPf<sideTrg>() + linkObj.getRelativePath<sideSrc>(); - - reportInfo(txtCreatingLink, target); + const AbstractPathRef targetPath = linkObj.getABF<sideTrg>().getAbstractPath(linkObj.getRelativePath<sideSrc>()); + reportInfo(txtCreatingLink, ABF::getDisplayPath(targetPath)); StatisticsReporter statReporter(1, 0, procCallback_); try { - zen::copySymlink(linkObj.getFullPath<sideSrc>(), target, copyFilePermissions_); //throw FileError + ABF::copySymlink(linkObj.getAbstractPath<sideSrc>(), targetPath, copyFilePermissions_); //throw FileError //update SymlinkPair linkObj.setSyncedTo<sideTrg>(linkObj.getItemName<sideSrc>(), linkObj.getLastWriteTime<sideSrc>(), //target time set from source @@ -1547,7 +1410,7 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperati { warn_static("still an error if base dir is missing!") - if (somethingExists(linkObj.getFullPath<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it! + if (ABF::somethingExists(linkObj.getAbstractPath<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it! throw; //source deleted meanwhile...nothing was done (logical point of view!) linkObj.removeObject<sideSrc>(); @@ -1558,14 +1421,14 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperati case SO_DELETE_LEFT: case SO_DELETE_RIGHT: - reportInfo(getDelHandling<sideTrg>().getTxtRemovingSymLink(), linkObj.getFullPath<sideTrg>()); + reportInfo(getDelHandling<sideTrg>().getTxtRemovingSymLink(), ABF::getDisplayPath(linkObj.getAbstractPath<sideTrg>())); { StatisticsReporter statReporter(1, 0, procCallback_); auto onNotifyItemDeletion = [&] { statReporter.reportDelta(1, 0); }; - auto onNotifyFileCopy = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; + auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - getDelHandling<sideTrg>().removeLinkWithCallback(linkObj.getFullPath<sideTrg>(), linkObj.getPairRelativePath(), onNotifyItemDeletion, onNotifyFileCopy); //throw FileError + getDelHandling<sideTrg>().removeLinkWithCallback(linkObj.getAbstractPath<sideTrg>(), linkObj.getPairRelativePath(), onNotifyItemDeletion, onNotifyCopyStatus); //throw FileError linkObj.removeObject<sideTrg>(); //update SymlinkPair @@ -1575,22 +1438,22 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperati case SO_OVERWRITE_LEFT: case SO_OVERWRITE_RIGHT: - reportInfo(txtOverwritingLink, linkObj.getFullPath<sideTrg>()); + reportInfo(txtOverwritingLink, ABF::getDisplayPath(linkObj.getAbstractPath<sideTrg>())); { StatisticsReporter statReporter(1, 0, procCallback_); - auto onNotifyFileCopy = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; + auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - //reportStatus(getDelHandling<sideTrg>().getTxtRemovingSymLink(), linkObj.getFullPath<sideTrg>()); - getDelHandling<sideTrg>().removeLinkWithCallback(linkObj.getFullPath<sideTrg>(), linkObj.getPairRelativePath(), [] {}, onNotifyFileCopy); //throw FileError + //reportStatus(getDelHandling<sideTrg>().getTxtRemovingSymLink(), ABF::getDisplayPath(linkObj.getAbstractPath<sideTrg>())); + getDelHandling<sideTrg>().removeLinkWithCallback(linkObj.getAbstractPath<sideTrg>(), linkObj.getPairRelativePath(), [] {}, onNotifyCopyStatus); //throw FileError //linkObj.removeObject<sideTrg>(); -> "linkObj, sideTrg" evaluated below! //=> don't risk reportStatus() throwing GuiAbortProcess() leaving the target deleted rather than updated: - //reportStatus(txtOverwritingLink, linkObj.getFullPath<sideTrg>()); //restore status text + //reportStatus(txtOverwritingLink, ABF::getDisplayPath(linkObj.getAbstractPath<sideTrg>())); //restore status text - zen::copySymlink(linkObj.getFullPath<sideSrc>(), - linkObj.getBaseDirPf<sideTrg>() + linkObj.getRelativePath<sideSrc>(), //respect differences in case of source object + ABF::copySymlink(linkObj.getAbstractPath<sideSrc>(), + linkObj.getABF<sideTrg>().getAbstractPath(linkObj.getRelativePath<sideSrc>()), //respect differences in case of source object copyFilePermissions_); //throw FileError statReporter.reportDelta(1, 0); //we model "delete + copy" as ONE logical operation @@ -1606,16 +1469,16 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperati case SO_COPY_METADATA_TO_LEFT: case SO_COPY_METADATA_TO_RIGHT: - reportInfo(txtWritingAttributes, linkObj.getFullPath<sideTrg>()); + reportInfo(txtWritingAttributes, ABF::getDisplayPath(linkObj.getAbstractPath<sideTrg>())); - if (linkObj.getItemName<sideTrg>() != linkObj.getItemName<sideSrc>()) //adapt difference in case (windows only) - renameFile(linkObj.getFullPath<sideTrg>(), - beforeLast(linkObj.getFullPath<sideTrg>(), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + linkObj.getItemName<sideSrc>()); //throw FileError + if (linkObj.getItemName<sideTrg>() != linkObj.getItemName<sideSrc>()) //have difference in case? + ABF::renameItem(linkObj.getAbstractPath<sideTrg>(), //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) + linkObj.getABF<sideTrg>().getAbstractPath(linkObj.getRelativePath<sideSrc>())); if (linkObj.getLastWriteTime<sideTrg>() != linkObj.getLastWriteTime<sideSrc>()) //- no need to call sameFileTime() or respect 2 second FAT/FAT32 precision in this comparison //- do NOT read *current* source file time, but use buffered value which corresponds to time of comparison! - setFileTime(linkObj.getFullPath<sideTrg>(), linkObj.getLastWriteTime<sideSrc>(), ProcSymlink::DIRECT); //throw FileError + ABF::setFileTime(linkObj.getAbstractPath<sideTrg>(), linkObj.getLastWriteTime<sideSrc>(), ABF::SymlinkHandling::DIRECT); //throw FileError //-> both sides *should* be completely equal now... linkObj.setSyncedTo<sideTrg>(linkObj.getItemName<sideSrc>(), @@ -1668,16 +1531,16 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirPair& dirObj, SyncOperation if (parentDir->isEmpty<sideTrg>()) //BaseDirPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() return; //if parent directory creation failed, there's no reason to show more errors! - if (somethingExists(dirObj.getFullPath<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! + if (ABF::somethingExists(dirObj.getAbstractPath<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! { - const Zstring& target = dirObj.getBaseDirPf<sideTrg>() + dirObj.getRelativePath<sideSrc>(); + const AbstractPathRef targetPath = dirObj.getABF<sideTrg>().getAbstractPath(dirObj.getRelativePath<sideSrc>()); + reportInfo(txtCreatingFolder, ABF::getDisplayPath(targetPath)); - reportInfo(txtCreatingFolder, target); try { - makeDirectoryPlain(target, dirObj.getFullPath<sideSrc>(), copyFilePermissions_); //throw FileError + ABF::copyNewFolder(dirObj.getAbstractPath<sideSrc>(), targetPath, copyFilePermissions_); //throw FileError } - catch (const FileError&) { if (!dirExists(target)) throw; } + catch (const FileError&) { if (!ABF::dirExists(targetPath)) throw; } //update DirPair dirObj.setSyncedTo(dirObj.getItemName<sideSrc>()); @@ -1701,16 +1564,16 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirPair& dirObj, SyncOperation case SO_DELETE_LEFT: case SO_DELETE_RIGHT: - reportInfo(getDelHandling<sideTrg>().getTxtRemovingDir(), dirObj.getFullPath<sideTrg>()); + reportInfo(getDelHandling<sideTrg>().getTxtRemovingDir(), ABF::getDisplayPath(dirObj.getAbstractPath<sideTrg>())); { const SyncStatistics subStats(dirObj); //counts sub-objects only! StatisticsReporter statReporter(1 + getCUD(subStats), subStats.getDataToProcess(), procCallback_); auto onNotifyItemDeletion = [&] { statReporter.reportDelta(1, 0); }; - auto onNotifyFileCopy = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; + auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - getDelHandling<sideTrg>().removeDirWithCallback(dirObj.getFullPath<sideTrg>(), dirObj.getPairRelativePath(), onNotifyItemDeletion, onNotifyFileCopy); //throw FileError + getDelHandling<sideTrg>().removeDirWithCallback(dirObj.getAbstractPath<sideTrg>(), dirObj.getPairRelativePath(), onNotifyItemDeletion, onNotifyCopyStatus); //throw FileError dirObj.refSubFiles().clear(); // dirObj.refSubLinks().clear(); //update DirPair @@ -1725,11 +1588,11 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirPair& dirObj, SyncOperation case SO_OVERWRITE_RIGHT: // case SO_COPY_METADATA_TO_LEFT: case SO_COPY_METADATA_TO_RIGHT: - reportInfo(txtWritingAttributes, dirObj.getFullPath<sideTrg>()); + reportInfo(txtWritingAttributes, ABF::getDisplayPath(dirObj.getAbstractPath<sideTrg>())); - if (dirObj.getItemName<sideTrg>() != dirObj.getItemName<sideSrc>()) //adapt difference in case (windows only) - renameFile(dirObj.getFullPath<sideTrg>(), - beforeLast(dirObj.getFullPath<sideTrg>(), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + dirObj.getItemName<sideSrc>()); //throw FileError + if (dirObj.getItemName<sideTrg>() != dirObj.getItemName<sideSrc>()) //have difference in case? + ABF::renameItem(dirObj.getAbstractPath<sideTrg>(), //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) + dirObj.getABF<sideTrg>().getAbstractPath(dirObj.getRelativePath<sideSrc>())); //copyFileTimes -> useless: modification time changes with each child-object creation/deletion //-> both sides *should* be completely equal now... @@ -1755,15 +1618,16 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirPair& dirObj, SyncOperation //########################################################################################### //--------------------- data verification ------------------------- -void verifyFiles(const Zstring& sourceFile, const Zstring& targetFile, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError +void verifyFiles(const AbstractPathRef& sourcePath, const AbstractPathRef& targetPath, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //throw FileError { try { //do like "copy /v": 1. flush target file buffers, 2. read again as usual (using OS buffers) // => it seems OS buffered are not invalidated by this: snake oil??? + if (Opt<Zstring> nativeTargetPath = ABF::getNativeItemPath(targetPath)) { #ifdef ZEN_WIN - const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(targetFile).c_str(), //_In_ LPCTSTR lpFileName, + const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(*nativeTargetPath).c_str(), //_In_ LPCTSTR lpFileName, GENERIC_WRITE | //_In_ DWORD dwDesiredAccess, GENERIC_READ, //=> request read-access, too, just like "copy /v" command FILE_SHARE_READ | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, @@ -1772,27 +1636,29 @@ void verifyFiles(const Zstring& sourceFile, const Zstring& targetFile, const std FILE_ATTRIBUTE_NORMAL, //_In_ DWORD dwFlagsAndAttributes, nullptr); //_In_opt_ HANDLE hTemplateFile if (fileHandle == INVALID_HANDLE_VALUE) - throwFileError(replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(targetFile)), L"CreateFile", getLastError()); + throwFileError(replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(*nativeTargetPath)), L"CreateFile", getLastError()); ZEN_ON_SCOPE_EXIT(::CloseHandle(fileHandle)); if (!::FlushFileBuffers(fileHandle)) - throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(targetFile)), L"FlushFileBuffers", getLastError()); + throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(*nativeTargetPath)), L"FlushFileBuffers", getLastError()); #elif defined ZEN_LINUX || defined ZEN_MAC - const int fileHandle = ::open(targetFile.c_str(), O_WRONLY); + const int fileHandle = ::open(nativeTargetPath->c_str(), O_WRONLY); if (fileHandle == -1) - throwFileError(replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(targetFile)), L"open", getLastError()); + throwFileError(replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(*nativeTargetPath)), L"open", getLastError()); ZEN_ON_SCOPE_EXIT(::close(fileHandle)); if (::fsync(fileHandle) != 0) - throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(targetFile)), L"fsync", getLastError()); + throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(*nativeTargetPath)), L"fsync", getLastError()); #endif - } //close file handles + } //close file handles! if (onUpdateStatus) onUpdateStatus(0); - if (!filesHaveSameContent(sourceFile, targetFile, onUpdateStatus)) //throw FileError - throw FileError(replaceCpy(replaceCpy(_("%x and %y have different content."), L"%x", L"\n" + fmtFileName(sourceFile)), L"%y", L"\n" + fmtFileName(targetFile))); + if (!filesHaveSameContent(sourcePath, targetPath, onUpdateStatus)) //throw FileError + throw FileError(replaceCpy(replaceCpy(_("%x and %y have different content."), L"%x", L"\n" + + fmtFileName(ABF::getDisplayPath(sourcePath))), L"%y", L"\n" + + fmtFileName(ABF::getDisplayPath(targetPath)))); } catch (const FileError& e) //add some context to error message { @@ -1801,28 +1667,26 @@ void verifyFiles(const Zstring& sourceFile, const Zstring& targetFile, const std } -InSyncAttributes SynchronizeFolderPair::copyFileWithCallback(const Zstring& sourceFile, //throw FileError - const Zstring& targetFile, - const std::function<void()>& onDeleteTargetFile, - const std::function<void(std::int64_t bytesDelta)>& onNotifyFileCopy) const //returns current attributes of source file +ABF::FileAttribAfterCopy SynchronizeFolderPair::copyFileWithCallback(const AbstractPathRef& sourcePath, //throw FileError + const AbstractPathRef& targetPath, + const std::function<void()>& onDeleteTargetFile, + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) const //returns current attributes of source file { - auto copyOperation = [this, &targetFile, &onDeleteTargetFile, &onNotifyFileCopy](const Zstring& sourceFileTmp) + auto copyOperation = [this, &targetPath, &onDeleteTargetFile, &onNotifyCopyStatus](const AbstractPathRef& sourcePathTmp) { - InSyncAttributes newAttr = {}; - copyFile(sourceFileTmp, //type File implicitly means symlinks need to be dereferenced! - targetFile, - copyFilePermissions_, - transactionalFileCopy_, - onDeleteTargetFile, onNotifyFileCopy, - &newAttr); //throw FileError, ErrorFileLocked + ABF::FileAttribAfterCopy newAttr = ABF::copyFileTransactional(sourcePathTmp, targetPath, //throw FileError, ErrorFileLocked + copyFilePermissions_, + transactionalFileCopy_, + onDeleteTargetFile, + onNotifyCopyStatus); //#################### Verification ############################# if (verifyCopiedFiles_) { - auto guardTarget = makeGuard([&] { removeFile(targetFile); }); //delete target if verification fails + auto guardTarget = makeGuard([&] { ABF::removeFile(targetPath); }); //delete target if verification fails - procCallback_.reportInfo(replaceCpy(txtVerifying, L"%x", fmtFileName(targetFile))); - verifyFiles(sourceFileTmp, targetFile, [&](std::int64_t bytesDelta) { procCallback_.requestUiRefresh(); }); //throw FileError + procCallback_.reportInfo(replaceCpy(txtVerifying, L"%x", fmtFileName(ABF::getDisplayPath(targetPath)))); + verifyFiles(sourcePathTmp, targetPath, [&](std::int64_t bytesDelta) { procCallback_.requestUiRefresh(); }); //throw FileError guardTarget.dismiss(); } @@ -1834,34 +1698,36 @@ InSyncAttributes SynchronizeFolderPair::copyFileWithCallback(const Zstring& sour #ifdef ZEN_WIN try { - return copyOperation(sourceFile); + return copyOperation(sourcePath); } catch (ErrorFileLocked& e1) { - //if file is locked (try to) use Windows Volume Shadow Copy Service - if (!shadowCopyHandler_) - throw; - - Zstring shadowSource; - try - { - //contains prefix: E.g. "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Program Files\FFS\sample.dat" - shadowSource = shadowCopyHandler_->makeShadowCopy(sourceFile, //throw FileError - [&](const Zstring& volumeName) + if (shadowCopyHandler_) //if file is locked (try to) use Windows Volume Shadow Copy Service: + if (Opt<Zstring> nativeSourcePath = ABF::getNativeItemPath(sourcePath)) { - procCallback_.reportStatus(replaceCpy(_("Creating a Volume Shadow Copy for %x..."), L"%x", fmtFileName(volumeName))); - }); - } - catch (const FileError& e2) //enhance error message - { - throw FileError(e1.toString(), e2.toString()); - } + Zstring nativeShadowPath; //contains prefix: E.g. "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Program Files\FFS\sample.dat" + try + { + nativeShadowPath = shadowCopyHandler_->makeShadowCopy(*nativeSourcePath, //throw FileError + [&](const Zstring& volumeName) + { + procCallback_.reportStatus(replaceCpy(_("Creating a Volume Shadow Copy for %x..."), L"%x", fmtFileName(volumeName))); + }); + } + catch (const FileError& e2) //enhance error message + { + throw FileError(e1.toString(), e2.toString()); + } - //now try again - return copyOperation(shadowSource); + //now try again + if (std::unique_ptr<AbstractPathRef> shadowPath = ABF::getAbf(sourcePath).getAbstractPathFromNativePath(nativeShadowPath)) + return copyOperation(*shadowPath); + else assert(false); + } + throw; } #else - return copyOperation(sourceFile); + return copyOperation(sourcePath); #endif } @@ -1870,8 +1736,7 @@ InSyncAttributes SynchronizeFolderPair::copyFileWithCallback(const Zstring& sour 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 { - const Zstring dirpath = beforeLast(baseDirObj.getBaseDirPf<side>(), FILE_NAME_SEPARATOR); //what about C:\ ??? - if (dirpath.empty()) + if (baseDirObj.getABF<side>().emptyBaseFolderPath()) return true; if (!baseDirObj.isExisting<side>()) //create target directory: user presumably ignored error "dir existing" in order to have it created automatically @@ -1879,17 +1744,18 @@ bool createBaseDirectory(BaseDirPair& baseDirObj, ProcessCallback& callback) //n bool temporaryNetworkDrop = false; zen::Opt<std::wstring> errMsg = tryReportingError([&] { + const AbstractPathRef baseFolder = baseDirObj.getABF<side>().getAbstractPath(); try { //a nice race-free check and set operation: - makeDirectory(dirpath, /*bool failIfExists*/ true); //throw FileError, ErrorTargetExisting + ABF::createNewFolder(baseFolder); //throw FileError, ErrorTargetExisting baseDirObj.setExisting<side>(true); //update our model! } catch (const ErrorTargetExisting&) { //TEMPORARY network drop! base directory not found during comparison, but reappears during synchronization //=> sync-directions are based on false assumptions! Abort. - callback.reportFatalError(replaceCpy(_("Target folder %x already existing."), L"%x", fmtFileName(dirpath))); + callback.reportFatalError(replaceCpy(_("Target folder %x already existing."), L"%x", fmtFileName(ABF::getDisplayPath(baseFolder)))); temporaryNetworkDrop = true; //Is it possible we're catching a "false-positive" here, could FFS have created the directory indirectly after comparison? @@ -1972,59 +1838,53 @@ void zen::synchronize(const TimeComp& timeStamp, //-------------------execute basic checks all at once before starting sync-------------------------------------- - auto dirNotFoundAnymore = [&](const Zstring& baseDirPf, bool wasExisting) + auto dirNotFoundAnymore = [&](const ABF& baseFolder, 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... + if (!folderExistsUpdating(baseFolder, false, callback)) + throw FileError(replaceCpy(_("Cannot find folder %x."), L"%x", fmtFileName(ABF::getDisplayPath(baseFolder.getAbstractPath())))); + //should be logged as a "fatal error" if ignored by the user... }, callback)) //throw X? return true; return false; }; - auto havePathDependency = [](const Zstring& lhs, const Zstring& rhs) //note: this is NOT an equivalence relation! - { - return EqualFilename()(Zstring(lhs.c_str(), std::min(lhs.length(), rhs.length())), - Zstring(rhs.c_str(), std::min(lhs.length(), rhs.length()))); - }; - //aggregate information - std::map<Zstring, ReadWriteCount, LessFilename> dirReadWriteCount; //count read/write accesses + std::map<const ABF*, ReadWriteCount, ABF::LessItemPath> dirReadWriteCount; //count read/write accesses for (auto j = begin(folderCmp); j != end(folderCmp); ++j) { //create all entries first! otherwise counting accesses is too complex during later inserts! - if (!j->getBaseDirPf<LEFT_SIDE >().empty()) //<empty> is always a dependent directory => exclude! - dirReadWriteCount[j->getBaseDirPf<LEFT_SIDE >()]; - if (!j->getBaseDirPf<RIGHT_SIDE >().empty()) - dirReadWriteCount[j->getBaseDirPf<RIGHT_SIDE>()]; + + if (!j->getABF<LEFT_SIDE >().emptyBaseFolderPath()) //empty folder is always dependent => exclude! + dirReadWriteCount[&j->getABF<LEFT_SIDE>()]; + if (!j->getABF<RIGHT_SIDE >().emptyBaseFolderPath()) + dirReadWriteCount[&j->getABF<RIGHT_SIDE>()]; } - auto incReadCount = [&](const Zstring& baseDir) + auto incReadCount = [&](const ABF& baseFolder) { - if (!baseDir.empty()) + if (!baseFolder.emptyBaseFolderPath()) for (auto& item : dirReadWriteCount) - if (havePathDependency(baseDir, item.first)) + if (ABF::havePathDependency(baseFolder, *item.first)) ++item.second.reads; }; - auto incWriteCount = [&](const Zstring& baseDir) + auto incWriteCount = [&](const ABF& baseFolder) { - if (!baseDir.empty()) + if (!baseFolder.emptyBaseFolderPath()) for (auto& item : dirReadWriteCount) - if (havePathDependency(baseDir, item.first)) + if (ABF::havePathDependency(baseFolder, *item.first)) ++item.second.writes; }; - std::vector<std::pair<Zstring, Zstring>> significantDiffPairs; + std::vector<std::pair<const ABF*, const ABF*>> significantDiffPairs; - std::vector<std::pair<Zstring, std::pair<std::int64_t, std::int64_t>>> diskSpaceMissing; //dirpath / space required / space available + std::vector<std::pair<const ABF*, std::pair<std::int64_t, std::int64_t>>> diskSpaceMissing; //base folder / space required / space available -#ifdef ZEN_WIN //status of base directories which are set to DELETE_TO_RECYCLER (and contain actual items to be deleted) - std::map<Zstring, bool, LessFilename> baseDirHasRecycler; //might be expensive to determine => buffer + check recycle bin existence only once per base directory! -#endif + std::map<const ABF*, bool, ABF::LessItemPath> recyclerSupported; //expensive to determine on Win XP => buffer + check recycle bin existence only once per base folder! //start checking folder pairs for (auto j = begin(folderCmp); j != end(folderCmp); ++j) @@ -2033,7 +1893,8 @@ void zen::synchronize(const TimeComp& timeStamp, const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex]; //exclude some pathological case (leftdir, rightdir are empty) - if (EqualFilename()(j->getBaseDirPf<LEFT_SIDE>(), j->getBaseDirPf<RIGHT_SIDE>())) + if (ABF::equalItemPath(j->getABF<LEFT_SIDE >().getAbstractPath(), + j->getABF<RIGHT_SIDE>().getAbstractPath())) { jobType[folderIndex] = FolderPairJobType::SKIP; continue; @@ -2059,33 +1920,33 @@ void zen::synchronize(const TimeComp& timeStamp, } //aggregate information of folders used by multiple pairs in read/write access - if (!havePathDependency(j->getBaseDirPf<LEFT_SIDE>(), j->getBaseDirPf<RIGHT_SIDE>())) //true in general + if (!ABF::havePathDependency(j->getABF<LEFT_SIDE>(), j->getABF<RIGHT_SIDE>())) //true in general { if (writeLeft && writeRight) { - incWriteCount(j->getBaseDirPf<LEFT_SIDE>()); - incWriteCount(j->getBaseDirPf<RIGHT_SIDE>()); + incWriteCount(j->getABF<LEFT_SIDE>()); + incWriteCount(j->getABF<RIGHT_SIDE>()); } else if (writeLeft) { - incWriteCount(j->getBaseDirPf<LEFT_SIDE>()); - incReadCount (j->getBaseDirPf<RIGHT_SIDE>()); + incWriteCount(j->getABF<LEFT_SIDE>()); + incReadCount (j->getABF<RIGHT_SIDE>()); } else if (writeRight) { - incReadCount (j->getBaseDirPf<LEFT_SIDE>()); - incWriteCount(j->getBaseDirPf<RIGHT_SIDE>()); + incReadCount (j->getABF<LEFT_SIDE>()); + incWriteCount(j->getABF<RIGHT_SIDE>()); } } else //if folder pair contains two dependent folders, a warning was already issued after comparison; in this context treat as one write access at most { if (writeLeft || writeRight) - incWriteCount(j->getBaseDirPf<LEFT_SIDE>()); + incWriteCount(j->getABF<LEFT_SIDE>()); } //check for empty target folder paths: 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_))) + if ((j->getABF<LEFT_SIDE >().emptyBaseFolderPath() && (writeLeft || folderPairCfg.saveSyncDB_)) || + (j->getABF<RIGHT_SIDE>().emptyBaseFolderPath() && (writeRight || folderPairCfg.saveSyncDB_))) { callback.reportFatalError(_("Target folder input field must not be empty.")); jobType[folderIndex] = FolderPairJobType::SKIP; @@ -2095,30 +1956,30 @@ void zen::synchronize(const TimeComp& timeStamp, //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>())) + if (dirNotFoundAnymore(j->getABF<LEFT_SIDE >(), j->isExisting<LEFT_SIDE >()) || + dirNotFoundAnymore(j->getABF<RIGHT_SIDE>(), j->isExisting<RIGHT_SIDE>())) { jobType[folderIndex] = FolderPairJobType::SKIP; 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 sourceDirNotFound = [&](const Zstring& baseDirPf, bool wasExisting) -> bool //avoid race-condition: we need to evaluate existence status from time of comparison! + auto sourceDirNotFound = [&](const ABF& baseFolder, bool wasExisting) -> bool //avoid race-condition: we need to evaluate existence status from time of comparison! { - if (!baseDirPf.empty()) + if (!baseFolder.emptyBaseFolderPath()) //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.getDelete() > 0) //check 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! 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))); + callback.reportFatalError(replaceCpy(_("Source folder %x not found."), L"%x", fmtFileName(ABF::getDisplayPath(baseFolder.getAbstractPath())))); return true; } return false; }; - if (sourceDirNotFound(j->getBaseDirPf<LEFT_SIDE >(), j->isExisting<LEFT_SIDE >()) || - sourceDirNotFound(j->getBaseDirPf<RIGHT_SIDE>(), j->isExisting<RIGHT_SIDE>())) + if (sourceDirNotFound(j->getABF<LEFT_SIDE >(), j->isExisting<LEFT_SIDE >()) || + sourceDirNotFound(j->getABF<RIGHT_SIDE>(), j->isExisting<RIGHT_SIDE>())) { jobType[folderIndex] = FolderPairJobType::SKIP; continue; @@ -2128,7 +1989,7 @@ void zen::synchronize(const TimeComp& timeStamp, if (folderPairCfg.handleDeletion == zen::DELETE_TO_VERSIONING && folderPairStat.getUpdate() + folderPairStat.getDelete() > 0) { - if (folderPairCfg.versioningFolder.empty()) //already trimmed by getFormattedDirectoryPath() + if (trimCpy(folderPairCfg.versioningFolderPhrase).empty()) { //should not arrive here: already checked in SyncCfgDialog callback.reportFatalError(_("Please enter a target folder for versioning.")); @@ -2138,43 +1999,45 @@ void zen::synchronize(const TimeComp& timeStamp, } //check if more than 50% of total number of files/dirs are to be created/overwritten/deleted - if (significantDifferenceDetected(folderPairStat)) - significantDiffPairs.emplace_back(j->getBaseDirPf<LEFT_SIDE>(), j->getBaseDirPf<RIGHT_SIDE>()); + if (!j->getABF<LEFT_SIDE >().emptyBaseFolderPath() && + !j->getABF<RIGHT_SIDE>().emptyBaseFolderPath()) + if (significantDifferenceDetected(folderPairStat)) + significantDiffPairs.emplace_back(&j->getABF<LEFT_SIDE>(), &j->getABF<RIGHT_SIDE>()); //check for sufficient free diskspace - auto checkSpace = [&](const Zstring& baseDirPf, std::int64_t minSpaceNeeded) + auto checkSpace = [&](const ABF& baseFolder, std::int64_t minSpaceNeeded) { - try - { - const std::int64_t freeSpace = getFreeDiskSpace(baseDirPf); //throw FileError + if (!baseFolder.emptyBaseFolderPath()) + try + { + const std::int64_t freeSpace = baseFolder.getFreeDiskSpace(); //throw FileError, returns 0 if not available - if (0 < freeSpace && //zero disk space probably means "request not supported" (e.g. see WebDav) - freeSpace < minSpaceNeeded) - diskSpaceMissing.emplace_back(baseDirPf, std::make_pair(minSpaceNeeded, freeSpace)); - } - catch (FileError&) {} + if (0 < freeSpace && //zero means "request not supported" (e.g. see WebDav) + freeSpace < minSpaceNeeded) + diskSpaceMissing.emplace_back(&baseFolder, std::make_pair(minSpaceNeeded, freeSpace)); + } + catch (FileError&) { assert(false); } //for warning only => no need for tryReportingError() }; const std::pair<std::int64_t, std::int64_t> spaceNeeded = MinimumDiskSpaceNeeded::calculate(*j); - checkSpace(j->getBaseDirPf<LEFT_SIDE >(), spaceNeeded.first); - checkSpace(j->getBaseDirPf<RIGHT_SIDE>(), spaceNeeded.second); + checkSpace(j->getABF<LEFT_SIDE >(), spaceNeeded.first); + checkSpace(j->getABF<RIGHT_SIDE>(), spaceNeeded.second); -#ifdef ZEN_WIN //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong - auto checkRecycler = [&](const Zstring& baseDirPf) + auto checkRecycler = [&](const ABF& baseFolder) { - assert(!baseDirPf.empty()); - if (!baseDirPf.empty()) - if (baseDirHasRecycler.find(baseDirPf) == baseDirHasRecycler.end()) //perf: avoid duplicate checks! + assert(!baseFolder.emptyBaseFolderPath()); + if (!baseFolder.emptyBaseFolderPath()) + if (recyclerSupported.find(&baseFolder) == recyclerSupported.end()) //perf: avoid duplicate checks! { - callback.reportStatus(replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", fmtFileName(baseDirPf), false)); - - bool recExists = false; + callback.reportStatus(replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", + fmtFileName(ABF::getDisplayPath(baseFolder.getAbstractPath())))); + bool recSupported = false; tryReportingError([&] { - recExists = recycleBinExists(baseDirPf, [&] { callback.requestUiRefresh(); /*may throw*/ }); //throw FileError + recSupported = baseFolder.supportsRecycleBin([&]{ callback.requestUiRefresh(); /*may throw*/ }); //throw FileError }, callback); //throw X? - baseDirHasRecycler[baseDirPf] = recExists; + recyclerSupported.emplace(&baseFolder, recSupported); } }; @@ -2182,13 +2045,12 @@ void zen::synchronize(const TimeComp& timeStamp, { if (folderPairStat.getUpdate<LEFT_SIDE>() + folderPairStat.getDelete<LEFT_SIDE>() > 0) - checkRecycler(j->getBaseDirPf<LEFT_SIDE>()); + checkRecycler(j->getABF<LEFT_SIDE>()); if (folderPairStat.getUpdate<RIGHT_SIDE>() + folderPairStat.getDelete<RIGHT_SIDE>() > 0) - checkRecycler(j->getBaseDirPf<RIGHT_SIDE>()); + checkRecycler(j->getABF<RIGHT_SIDE>()); } -#endif } //check if unresolved conflicts exist @@ -2209,8 +2071,8 @@ void zen::synchronize(const TimeComp& timeStamp, for (const auto& item : significantDiffPairs) msg += std::wstring(L"\n\n") + - item.first + L" <-> " + L"\n" + - item.second; + ABF::getDisplayPath(item.first ->getAbstractPath()) + L" <-> " + L"\n" + + ABF::getDisplayPath(item.second->getAbstractPath()); callback.reportWarning(msg, warnings.warningSignificantDifference); } @@ -2222,38 +2084,37 @@ void zen::synchronize(const TimeComp& timeStamp, for (const auto& item : diskSpaceMissing) msg += std::wstring(L"\n\n") + - item.first + L"\n" + + ABF::getDisplayPath(item.first->getAbstractPath()) + L"\n" + _("Required:") + L" " + filesizeToShortString(item.second.first) + L"\n" + _("Available:") + L" " + filesizeToShortString(item.second.second); callback.reportWarning(msg, warnings.warningNotEnoughDiskSpace); } -#ifdef ZEN_WIN //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong { std::wstring dirListMissingRecycler; - for (const auto& item : baseDirHasRecycler) + for (const auto& item : recyclerSupported) if (!item.second) - dirListMissingRecycler += std::wstring(L"\n") + item.first; + dirListMissingRecycler += std::wstring(L"\n") + ABF::getDisplayPath(item.first->getAbstractPath()); if (!dirListMissingRecycler.empty()) - callback.reportWarning(_("The recycle bin is not available for the following folders. Files will be deleted permanently instead:") + L"\n" + dirListMissingRecycler, warnings.warningRecyclerMissing); + callback.reportWarning(_("The recycle bin is not available for the following folders. Files will be deleted permanently instead:") + L"\n" + + dirListMissingRecycler, warnings.warningRecyclerMissing); } -#endif //check if folders are used by multiple pairs in read/write access { - std::vector<Zstring> conflictDirs; + std::vector<const ABF*> conflictFolders; for (const auto& item : dirReadWriteCount) 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); + conflictFolders.push_back(item.first); - if (!conflictDirs.empty()) + if (!conflictFolders.empty()) { std::wstring msg = _("Multiple folder pairs write to a common subfolder. Please review your configuration.") + L"\n"; - for (const Zstring& dirpath : conflictDirs) - msg += std::wstring(L"\n") + dirpath; + for (const ABF* baseFolder : conflictFolders) + msg += std::wstring(L"\n") + ABF::getDisplayPath(baseFolder->getAbstractPath()); callback.reportWarning(msg, warnings.warningFolderPairRaceCondition); } @@ -2281,13 +2142,13 @@ void zen::synchronize(const TimeComp& timeStamp, //------------------------------------------------------------------------------------------ callback.reportInfo(_("Synchronizing folder pair:") + L" [" + getVariantName(folderPairCfg.syncVariant_) + L"]\n" + - L" " + j->getBaseDirPf<LEFT_SIDE >() + L"\n" + - L" " + j->getBaseDirPf<RIGHT_SIDE>()); + L" " + ABF::getDisplayPath(j->getABF<LEFT_SIDE >().getAbstractPath()) + L"\n" + + L" " + ABF::getDisplayPath(j->getABF<RIGHT_SIDE>().getAbstractPath())); //------------------------------------------------------------------------------------------ //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>())) + if (dirNotFoundAnymore(j->getABF<LEFT_SIDE >(), j->isExisting<LEFT_SIDE >()) || + dirNotFoundAnymore(j->getABF<RIGHT_SIDE>(), j->isExisting<RIGHT_SIDE>())) continue; //create base directories first (if not yet existing) -> no symlink or attribute copying! @@ -2302,7 +2163,7 @@ void zen::synchronize(const TimeComp& timeStamp, ScopeGuard guardUpdateDb = makeGuard([&] { if (folderPairCfg.saveSyncDB_) - try { zen::saveLastSynchronousState(*j); } //throw FileError + try { zen::saveLastSynchronousState(*j, nullptr); } //throw FileError catch (FileError&) {} }); @@ -2315,40 +2176,37 @@ void zen::synchronize(const TimeComp& timeStamp, tryReportingError([&] { copyPermissionsFp = copyFilePermissions && //copy permissions only if asked for and supported by *both* sides! - !j->getBaseDirPf<LEFT_SIDE >().empty() && //scenario: directory selected on one side only - !j->getBaseDirPf<RIGHT_SIDE>().empty() && // - supportsPermissions(beforeLast(j->getBaseDirPf<LEFT_SIDE >(), FILE_NAME_SEPARATOR)) && //throw FileError - supportsPermissions(beforeLast(j->getBaseDirPf<RIGHT_SIDE>(), FILE_NAME_SEPARATOR)); + !j->getABF<LEFT_SIDE >().emptyBaseFolderPath() && //scenario: directory selected on one side only + !j->getABF<RIGHT_SIDE>().emptyBaseFolderPath() && // + ABF::supportPermissionCopy(j->getABF<LEFT_SIDE>(), j->getABF<RIGHT_SIDE>()); //throw FileError }, callback); //throw X? - auto getEffectiveDeletionPolicy = [&](const Zstring& baseDirPf) -> DeletionPolicy + auto getEffectiveDeletionPolicy = [&](const ABF& baseFolder) -> DeletionPolicy { -#ifdef ZEN_WIN if (folderPairCfg.handleDeletion == DELETE_TO_RECYCLER) { - auto it = baseDirHasRecycler.find(baseDirPf); - if (it != baseDirHasRecycler.end()) //buffer filled during intro checks (but only if deletions are expected) + auto it = recyclerSupported.find(&baseFolder); + if (it != recyclerSupported.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) } -#endif return folderPairCfg.handleDeletion; }; - DeletionHandling delHandlerL(getEffectiveDeletionPolicy(j->getBaseDirPf<LEFT_SIDE>()), - folderPairCfg.versioningFolder, + DeletionHandling delHandlerL(j->getABF<LEFT_SIDE>(), + getEffectiveDeletionPolicy(j->getABF<LEFT_SIDE>()), + folderPairCfg.versioningFolderPhrase, folderPairCfg.versioningStyle_, timeStamp, - j->getBaseDirPf<LEFT_SIDE>(), callback); - DeletionHandling delHandlerR(getEffectiveDeletionPolicy(j->getBaseDirPf<RIGHT_SIDE>()), - folderPairCfg.versioningFolder, + DeletionHandling delHandlerR(j->getABF<RIGHT_SIDE>(), + getEffectiveDeletionPolicy(j->getABF<RIGHT_SIDE>()), + folderPairCfg.versioningFolderPhrase, folderPairCfg.versioningStyle_, timeStamp, - j->getBaseDirPf<RIGHT_SIDE>(), callback); @@ -2367,10 +2225,21 @@ void zen::synchronize(const TimeComp& timeStamp, //(try to gracefully) write database file if (folderPairCfg.saveSyncDB_) { - callback.reportStatus(_("Generating database...")); + const std::wstring dbUpdateMsg = _("Generating database..."); + + callback.reportStatus(dbUpdateMsg); callback.forceUiRefresh(); - tryReportingError([&] { zen::saveLastSynchronousState(*j); /*throw FileError*/ }, callback); //throw X? + tryReportingError([&] + { + std::int64_t bytesWritten = 0; + zen::saveLastSynchronousState(*j, [&](std::int64_t bytesDelta) //throw FileError + { + bytesWritten += bytesDelta; + callback.reportStatus(dbUpdateMsg + L" (" + filesizeToShortString(bytesWritten) + L")"); //throw X + }); + }, callback); //throw X? + guardUpdateDb.dismiss(); } } diff --git a/FreeFileSync/Source/synchronization.h b/FreeFileSync/Source/synchronization.h index bc85f077..a932bf8d 100644 --- a/FreeFileSync/Source/synchronization.h +++ b/FreeFileSync/Source/synchronization.h @@ -63,18 +63,18 @@ struct FolderPairSyncCfg FolderPairSyncCfg(bool saveSyncDB, const DeletionPolicy handleDel, VersioningStyle versioningStyle, - const Zstring& versioningDirFmt, + const Zstring& versioningPhrase, DirectionConfig::Variant syncVariant) : saveSyncDB_(saveSyncDB), handleDeletion(handleDel), versioningStyle_(versioningStyle), - versioningFolder(versioningDirFmt), + versioningFolderPhrase(versioningPhrase), syncVariant_(syncVariant) {} bool saveSyncDB_; //save database if in automatic mode or dection of moved files is active DeletionPolicy handleDeletion; VersioningStyle versioningStyle_; - Zstring versioningFolder; //formatted directory name + Zstring versioningFolderPhrase; //unresolved directory names as entered by user! DirectionConfig::Variant syncVariant_; }; std::vector<FolderPairSyncCfg> extractSyncCfg(const MainConfiguration& mainCfg); diff --git a/FreeFileSync/Source/ui/batch_config.cpp b/FreeFileSync/Source/ui/batch_config.cpp index d658732a..5f6bb204 100644 --- a/FreeFileSync/Source/ui/batch_config.cpp +++ b/FreeFileSync/Source/ui/batch_config.cpp @@ -10,7 +10,7 @@ #include <wx+/font_size.h> #include <wx+/image_resources.h> #include "gui_generated.h" -#include "dir_name.h" +#include "folder_selector.h" #include "../ui/on_completion_box.h" #include "../lib/help_provider.h" @@ -59,7 +59,7 @@ private: XmlBatchConfig& batchCfgOutRef; //output only! XmlBatchConfig localBatchCfg; //a mixture of settings some of which have OWNERSHIP WITHIN GUI CONTROLS! use getConfig() to resolve - std::unique_ptr<DirectoryName<FolderHistoryBox>> logfileDir; //always bound, solve circular compile-time dependency + std::unique_ptr<FolderSelector> logfileDir; //always bound, solve circular compile-time dependency }; //################################################################################################################################### @@ -83,7 +83,7 @@ BatchDialog::BatchDialog(wxWindow* parent, m_bitmapBatchJob->SetBitmap(getResourceImage(L"batch")); - logfileDir = make_unique<DirectoryName<FolderHistoryBox>>(*m_panelLogfile, *m_buttonSelectLogfileDir, *m_logfileDir); + logfileDir = make_unique<FolderSelector>(*m_panelLogfile, *m_buttonSelectLogfileDir, *m_logfileDir); setConfig(batchCfg); @@ -131,7 +131,7 @@ void BatchDialog::setConfig(const XmlBatchConfig& batchCfg) //transfer parameter ownership to GUI m_checkBoxRunMinimized->SetValue(batchCfg.runMinimized); - logfileDir->setPath(utfCvrtTo<wxString>(batchCfg.logFileDirectory)); + logfileDir->setPath(batchCfg.logFolderPathPhrase); m_comboBoxOnCompletion->setValue(batchCfg.mainCfg.onCompletion); //map single parameter "logfiles limit" to all three checkboxs and spin ctrl: @@ -152,7 +152,7 @@ XmlBatchConfig BatchDialog::getConfig() const //load structure with batch settings "batchCfg" batchCfg.runMinimized = m_checkBoxRunMinimized->GetValue(); - batchCfg.logFileDirectory = utfCvrtTo<Zstring>(logfileDir->getPath()); + batchCfg.logFolderPathPhrase = utfCvrtTo<Zstring>(logfileDir->getPath()); batchCfg.mainCfg.onCompletion = m_comboBoxOnCompletion->getValue(); //get single parameter "logfiles limit" from all three checkboxes and spin ctrl: batchCfg.logfilesCountLimit = m_checkBoxGenerateLogfile->GetValue() ? (m_checkBoxLogfilesLimit->GetValue() ? m_spinCtrlLogfileLimit->GetValue() : -1) : 0; diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp index 88b136e7..425e466f 100644 --- a/FreeFileSync/Source/ui/batch_status_handler.cpp +++ b/FreeFileSync/Source/ui/batch_status_handler.cpp @@ -5,8 +5,6 @@ // ************************************************************************** #include "batch_status_handler.h" -#include <zen/file_access.h> -#include <zen/file_traverser.h> #include <zen/shell_execute.h> #include <wx+/popup_dlg.h> #include <wx/app.h> @@ -15,6 +13,7 @@ #include "../lib/resolve_path.h" #include "../lib/status_handler_impl.h" #include "../lib/generate_logfile.h" +#include "../fs/concrete.h" using namespace zen; @@ -22,81 +21,105 @@ using namespace zen; namespace { //"Backup FreeFileSync 2013-09-15 015052.log" -> -//"Backup FreeFileSync 2013-09-15 015052 (Error).log" -Zstring addStatusToLogfilename(const Zstring& logfilepath, const std::wstring& status) +//"Backup FreeFileSync 2013-09-15 015052 [Error].log" + +//return value always bound! +std::pair<std::unique_ptr<ABF::OutputStream>, AbstractPathRef> prepareNewLogfile(ABF& abf, //throw FileError + const std::wstring& jobName, + const TimeComp& timeStamp, + const std::wstring& status) { - //attention: do not interfere with naming convention required by limitLogfileCount()! - size_t pos = logfilepath.rfind(Zstr('.')); - if (pos != Zstring::npos) - return Zstring(logfilepath.begin(), logfilepath.begin() + pos) + - utfCvrtTo<Zstring>(L" [" + status + L"]") + - Zstring(logfilepath.begin() + pos, logfilepath.end()); - assert(false); - return logfilepath; + assert(!jobName.empty()); + + //create logfile directory if required + try + { + ABF::createNewFolder(abf.getAbstractPath()); //throw FileError, ErrorTargetExisting + } + catch (const FileError&) { if (!ABF::dirExists(abf.getAbstractPath())) throw; } + + //const std::string colon = "\xcb\xb8"; //="modifier letter raised colon" => regular colon is forbidden in file names on Windows and OS X + //=> too many issues, most notably cmd.exe is not Unicode-awere: http://sourceforge.net/p/freefilesync/discussion/open-discussion/thread/c559a5fb/ + + //assemble logfile name + Zstring body = utfCvrtTo<Zstring>(jobName) + Zstr(" ") + formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp); + if (!status.empty()) + body += utfCvrtTo<Zstring>(L" [" + status + L"]"); + + //ensure uniqueness; avoid file system race-condition! + Zstring logFileName = body + Zstr(".log"); + for (int i = 0;; ++i) + try + { + const AbstractPathRef logFilePath = abf.getAbstractPath(logFileName); + auto outStream = ABF::getOutputStream(logFilePath, //throw FileError, ErrorTargetExisting + nullptr, /*streamSize*/ + nullptr /*modificationTime*/); + return std::make_pair(std::move(outStream), logFilePath); + } + catch (const ErrorTargetExisting&) + { + if (i == 10) throw; //avoid endless recursion in pathological cases + logFileName = body + Zstr('_') + numberTo<Zstring>(i) + Zstr(".log"); + } } -void limitLogfileCount(const Zstring& logdir, const std::wstring& jobname, size_t maxCount, const std::function<void()>& onUpdateStatus) //noexcept +struct LogTraverserCallback: public ABF::TraverserCallback { - std::vector<Zstring> logFiles; - const Zstring prefix = utfCvrtTo<Zstring>(jobname); + LogTraverserCallback(std::vector<Zstring>& logFileNames, const Zstring& prefix, const std::function<void()>& onUpdateStatus) : + logFileNames_(logFileNames), + prefix_(prefix), + onUpdateStatus_(onUpdateStatus) {} - traverseFolder(logdir, [&](const FileInfo& fi) + void onFile(const FileInfo& fi) override { const Zstring fileName(fi.shortName); - if (startsWith(fileName, prefix) && endsWith(fileName, Zstr(".log"))) - logFiles.push_back(fi.fullPath); - - if (onUpdateStatus) - onUpdateStatus(); - }, - nullptr, nullptr, [&](const std::wstring& errorMsg) { assert(false); }); //errors are not really critical in this context - - if (logFiles.size() <= maxCount) - return; + if (pathStartsWith(fileName, prefix_) && pathEndsWith(fileName, Zstr(".log"))) + logFileNames_.push_back(fi.shortName); - //delete oldest logfiles: take advantage of logfile naming convention to find them - std::nth_element(logFiles.begin(), logFiles.end() - maxCount, logFiles.end(), LessFilename()); + if (onUpdateStatus_) + onUpdateStatus_(); + } + TraverserCallback* onDir (const DirInfo& di) override { return nullptr; } + HandleLink onSymlink(const SymlinkInfo& si) override { return TraverserCallback::LINK_SKIP; } + HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { assert(false); return ON_ERROR_IGNORE; } //errors are not critical in this context + HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) override { assert(false); return ON_ERROR_IGNORE; } // - std::for_each(logFiles.begin(), logFiles.end() - maxCount, [&](const Zstring& filepath) - { - try { removeFile(filepath); } - catch (FileError&) {}; - onUpdateStatus(); - }); -} +private: + std::vector<Zstring>& logFileNames_; //out + const Zstring prefix_; + const std::function<void()> onUpdateStatus_; +}; -std::unique_ptr<FileOutput> prepareNewLogfile(const Zstring& logfileDirectory, //throw FileError - const std::wstring& jobName, - const TimeComp& timeStamp) //return value always bound! +void limitLogfileCount(ABF& abf, const std::wstring& jobname, size_t maxCount, const std::function<void()>& onUpdateStatus) //noexcept { - Zstring logfileDir = logfileDirectory.empty() ? - getConfigDir() + Zstr("Logs") : - getFormattedDirectoryPath(logfileDirectory); + std::vector<Zstring> logFileNames; + { + const Zstring prefix = utfCvrtTo<Zstring>(jobname); + LogTraverserCallback lt(logFileNames, prefix, onUpdateStatus); //traverse source directory one level deep - //create logfile directory if required - makeDirectory(logfileDir); //throw FileError + ABF::traverseFolder(abf.getAbstractPath(), lt); + } - //const std::string colon = "\xcb\xb8"; //="modifier letter raised colon" => regular colon is forbidden in file names on Windows and OS X - //=> too many issues, most notably cmd.exe is not Unicode-awere: http://sourceforge.net/p/freefilesync/discussion/open-discussion/thread/c559a5fb/ + if (logFileNames.size() <= maxCount) + return; - //assemble logfile name - const Zstring body = appendSeparator(logfileDir) + utfCvrtTo<Zstring>(jobName) + Zstr(" ") + formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp); + //delete oldest logfiles: take advantage of logfile naming convention to find them + std::nth_element(logFileNames.begin(), logFileNames.end() - maxCount, logFileNames.end(), LessFilePath()); - //ensure uniqueness - Zstring filepath = body + Zstr(".log"); - for (int i = 0;; ++i) + std::for_each(logFileNames.begin(), logFileNames.end() - maxCount, [&](const Zstring& logFileName) + { try { - return zen::make_unique<FileOutput>(filepath, FileOutput::ACC_CREATE_NEW); //throw FileError, ErrorTargetExisting - //*no* file system race-condition! - } - catch (const ErrorTargetExisting&) - { - if (i == 10) throw; //avoid endless recursion in pathological cases - filepath = body + Zstr('_') + numberTo<Zstring>(i) + Zstr(".log"); + ABF::removeFile(abf.getAbstractPath(logFileName)); //throw FileError } + catch (FileError&) { assert(false); }; + + if (onUpdateStatus) + onUpdateStatus(); + }); } } @@ -105,7 +128,7 @@ std::unique_ptr<FileOutput> prepareNewLogfile(const Zstring& logfileDirectory, / BatchStatusHandler::BatchStatusHandler(bool showProgress, const std::wstring& jobName, const TimeComp& timeStamp, - const Zstring& logfileDirectory, //may be empty + const Zstring& logFolderPathPhrase, //may be empty int logfilesCountLimit, size_t lastSyncsLogFileSizeMax, const xmlAccess::OnError handleError, @@ -126,18 +149,14 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress, automaticRetryDelay_(automaticRetryDelay), progressDlg(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, nullptr, showProgress, jobName, onCompletion, onCompletionHistory)), jobName_(jobName), - startTime_(wxGetUTCTimeMillis().GetValue()) + timeStamp_(timeStamp), + startTime_(wxGetUTCTimeMillis().GetValue()), + logFolderPathPhrase_(logFolderPathPhrase) { - //ATTENTION: "progressDlg" is an unmanaged resource!!! Anyway, at this point we already consider construction complete! => + //ATTENTION: "progressDlg" is an unmanaged resource!!! However, at this point we already consider construction complete! => ScopeGuard guardConstructor = zen::makeGuard([&] { this->~BatchStatusHandler(); }); - if (logfilesCountLimit != 0) - { - zen::Opt<std::wstring> errMsg = tryReportingError([&] { logFile = prepareNewLogfile(logfileDirectory, jobName, timeStamp); }, //throw FileError; return value always bound! - *this); //throw X? - if (errMsg) - abortProcessNow(); //throw BatchAbortProcess - } + //... //if (logFile) // ::wxSetEnv(L"logfile", utfCvrtTo<wxString>(logFile->getFilename())); @@ -190,70 +209,73 @@ BatchStatusHandler::~BatchStatusHandler() const int totalWarnings = errorLog.getItemCount(TYPE_WARNING); //finalize error log - std::wstring finalStatus; + std::wstring status; //additionally indicate errors in log file name + std::wstring finalStatusMsg; if (abortIsRequested()) { raiseReturnCode(returnCode_, FFS_RC_ABORTED); - finalStatus = _("Synchronization stopped"); - errorLog.logMsg(finalStatus, TYPE_ERROR); + finalStatusMsg = _("Synchronization stopped"); + errorLog.logMsg(finalStatusMsg, TYPE_ERROR); + status = _("Stopped"); } else if (totalErrors > 0) { raiseReturnCode(returnCode_, FFS_RC_FINISHED_WITH_ERRORS); - finalStatus = _("Synchronization completed with errors"); - errorLog.logMsg(finalStatus, TYPE_ERROR); + finalStatusMsg = _("Synchronization completed with errors"); + errorLog.logMsg(finalStatusMsg, TYPE_ERROR); + status = _("Error"); } else if (totalWarnings > 0) { raiseReturnCode(returnCode_, FFS_RC_FINISHED_WITH_WARNINGS); - finalStatus = _("Synchronization completed with warnings"); - errorLog.logMsg(finalStatus, TYPE_WARNING); + finalStatusMsg = _("Synchronization completed with warnings"); + errorLog.logMsg(finalStatusMsg, TYPE_WARNING); + status = _("Warning"); } else { if (getObjectsTotal(PHASE_SYNCHRONIZING) == 0 && //we're past "initNewPhase(PHASE_SYNCHRONIZING)" at this point! getDataTotal (PHASE_SYNCHRONIZING) == 0) - finalStatus = _("Nothing to synchronize"); //even if "ignored conflicts" occurred! + finalStatusMsg = _("Nothing to synchronize"); //even if "ignored conflicts" occurred! else - finalStatus = _("Synchronization completed successfully"); - errorLog.logMsg(finalStatus, TYPE_INFO); + finalStatusMsg = _("Synchronization completed successfully"); + errorLog.logMsg(finalStatusMsg, TYPE_INFO); } const SummaryInfo summary = { jobName_, - finalStatus, + finalStatusMsg, getObjectsCurrent(PHASE_SYNCHRONIZING), getDataCurrent(PHASE_SYNCHRONIZING), getObjectsTotal (PHASE_SYNCHRONIZING), getDataTotal (PHASE_SYNCHRONIZING), (wxGetUTCTimeMillis().GetValue() - startTime_) / 1000 }; //----------------- write results into user-specified logfile ------------------------ - if (logFile.get()) //can be null if BatchStatusHandler constructor throws! + //create not before destruction: 1. avoid issues with FFS trying to sync open log file 2. simplify transactional retry on failure 3. no need to rename log file to include status + if (logfilesCountLimit_ != 0) { - if (logfilesCountLimit_ > 0) - { - try { reportStatus(_("Cleaning up old log files...")); } - catch (...) {} - limitLogfileCount(beforeLast(logFile->getFilename(), FILE_NAME_SEPARATOR), jobName_, logfilesCountLimit_, [&] { try { requestUiRefresh(); } catch (...) {} }); //noexcept - } - + std::unique_ptr<AbstractBaseFolder> logFileAbf = createAbstractBaseFolder(trimCpy(logFolderPathPhrase_).empty() ? getConfigDir() + Zstr("Logs") : logFolderPathPhrase_); //noexcept try { - saveLogToFile(summary, errorLog, *logFile, OnUpdateLogfileStatusNoThrow(*this, logFile->getFilename())); //throw FileError + tryReportingError([&] //errors logged here do not impact final status calculation above! => not a problem! + { + auto rv = prepareNewLogfile(*logFileAbf, jobName_, timeStamp_, status); //throw FileError; return value always bound! + ABF::OutputStream& logFileStream = *rv.first; + const AbstractPathRef logFilePath = rv.second; - //additionally notify errors by showing in log file name - const Zstring oldLogfilepath = logFile->getFilename(); - logFile.reset(); + saveLogToFile(summary, errorLog, logFileStream, OnUpdateLogfileStatusNoThrow(*this, ABF::getDisplayPath(logFilePath))); //throw FileError + logFileStream.finalize([&] { try { requestUiRefresh(); } catch (...) {} }); //throw FileError + }, *this); //throw X? + } + catch (...) {} - if (abortIsRequested()) - renameFile(oldLogfilepath, addStatusToLogfilename(oldLogfilepath, _("Stopped"))); //throw FileError - else if (totalErrors > 0) - renameFile(oldLogfilepath, addStatusToLogfilename(oldLogfilepath, _("Error"))); //throw FileError - else if (totalWarnings > 0) - renameFile(oldLogfilepath, addStatusToLogfilename(oldLogfilepath, _("Warning"))); //throw FileError + if (logfilesCountLimit_ > 0) + { + try { reportStatus(_("Cleaning up old log files...")); } + catch (...) {} + limitLogfileCount(*logFileAbf, jobName_, logfilesCountLimit_, [&] { try { requestUiRefresh(); } catch (...) {} }); //noexcept } - catch (FileError&) { assert(false); } } //----------------- write results into LastSyncs.log------------------------ try diff --git a/FreeFileSync/Source/ui/batch_status_handler.h b/FreeFileSync/Source/ui/batch_status_handler.h index 71792dcc..251875e4 100644 --- a/FreeFileSync/Source/ui/batch_status_handler.h +++ b/FreeFileSync/Source/ui/batch_status_handler.h @@ -4,16 +4,17 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef BATCHSTATUSHANDLER_H_INCLUDED -#define BATCHSTATUSHANDLER_H_INCLUDED +#ifndef BATCH_STATUS_HANDLER_857390451451234566 +#define BATCH_STATUS_HANDLER_857390451451234566 #include <zen/error_log.h> -#include <zen/file_io.h> +//#include <zen/file_io.h> #include <zen/time.h> -#include "../lib/status_handler.h" -#include "../lib/process_xml.h" #include "progress_indicator.h" #include "switch_to_gui.h" +//#include "../fs/concrete.h" +#include "../lib/status_handler.h" +#include "../lib/process_xml.h" #include "../lib/return_codes.h" @@ -28,7 +29,7 @@ public: BatchStatusHandler(bool showProgress, //defines: -start minimized and -quit immediately when finished const std::wstring& jobName, //should not be empty for a batch job! const zen::TimeComp& timeStamp, - const Zstring& logfileDirectory, + const Zstring& logFolderPathPhrase, int logfilesCountLimit, //0: logging inactive; < 0: no limit size_t lastSyncsLogFileSizeMax, const xmlAccess::OnError handleError, @@ -67,11 +68,12 @@ private: const size_t automaticRetryDelay_; SyncProgressDialog* progressDlg; //managed to have shorter lifetime than this handler! - std::unique_ptr<zen::FileOutput> logFile; //optional! const std::wstring jobName_; + const zen::TimeComp timeStamp_; const int64_t startTime_; //don't use wxStopWatch: may overflow after a few days due to ::QueryPerformanceCounter() -}; + const Zstring logFolderPathPhrase_; +}; -#endif // BATCHSTATUSHANDLER_H_INCLUDED +#endif //BATCH_STATUS_HANDLER_857390451451234566 diff --git a/FreeFileSync/Source/ui/check_version.cpp b/FreeFileSync/Source/ui/check_version.cpp index fad4b614..5f44248c 100644 --- a/FreeFileSync/Source/ui/check_version.cpp +++ b/FreeFileSync/Source/ui/check_version.cpp @@ -84,7 +84,7 @@ std::wstring getIso3166Country() std::wstring getUserAgentName() //coordinate with on_check_latest_version.php { - std::wstring agentName = std::wstring(L"FreeFileSync (") + zen::currentVersion; + std::wstring agentName = std::wstring(L"FreeFileSync (") + zen::ffsVersion; #ifdef ZEN_WIN agentName += L" Windows"; @@ -302,16 +302,14 @@ GetVerResult getOnlineVersion(wxString& version) return canConnectToSf ? GET_VER_PAGE_NOT_FOUND : GET_VER_NO_CONNECTION; } #endif - trim(version); //Windows: remove trailing blank and newline + trim(version); //Windows: remove trailing blank and newline return version.empty() ? GET_VER_PAGE_NOT_FOUND : GET_VER_SUCCESS; //empty version possible?? } -const wchar_t VERSION_SEP = L'.'; - std::vector<size_t> parseVersion(const wxString& version) { - std::vector<wxString> digits = split(version, VERSION_SEP); + std::vector<wxString> digits = split(version, FFS_VERSION_SEPERATOR); std::vector<size_t> output; std::transform(digits.begin(), digits.end(), std::back_inserter(output), [&](const wxString& d) { return stringTo<size_t>(d); }); @@ -324,8 +322,8 @@ std::vector<size_t> parseVersion(const wxString& version) //use current version to calculate a changing number for the inactive state near UTC begin, in order to always check for updates after installing a new version //=> convert version into 11-based *unique* number (this breaks lexicographical version ordering, but that's irrelevant!) long id = 0; - const wchar_t* first = zen::currentVersion; - const wchar_t* last = first + zen::strLength(currentVersion); + const wchar_t* first = zen::ffsVersion; + const wchar_t* last = first + zen::strLength(ffsVersion); std::for_each(first, last, [&](wchar_t c) { id *= 11; @@ -333,7 +331,7 @@ std::vector<size_t> parseVersion(const wxString& version) id += c - L'0'; else { - assert(c == VERSION_SEP); + assert(c == FFS_VERSION_SEPERATOR); id += 10; } }); @@ -345,7 +343,7 @@ std::vector<size_t> parseVersion(const wxString& version) bool zen::isNewerFreeFileSyncVersion(const wxString& onlineVersion) { - std::vector<size_t> current = parseVersion(zen::currentVersion); + std::vector<size_t> current = parseVersion(zen::ffsVersion); std::vector<size_t> online = parseVersion(onlineVersion); if (online.empty() || online[0] == 0) //online version string may be "This website has been moved..." In this case better check for an update @@ -374,7 +372,7 @@ void zen::checkForUpdateNow(wxWindow* parent, wxString& lastOnlineVersion) switch (getOnlineVersion(onlineVersion)) { case GET_VER_SUCCESS: - lastOnlineVersion = onlineVersion; + lastOnlineVersion = onlineVersion; if (isNewerFreeFileSyncVersion(onlineVersion)) { switch (showConfirmationDialog(parent, DialogInfoType::INFO, PopupDialogCfg(). @@ -402,7 +400,7 @@ void zen::checkForUpdateNow(wxWindow* parent, wxString& lastOnlineVersion) break; case GET_VER_PAGE_NOT_FOUND: - lastOnlineVersion = L"unknown"; + lastOnlineVersion = L"unknown"; switch (showConfirmationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg(). setTitle(_("Check for Program Updates")). setMainInstructions(_("Cannot find current FreeFileSync version number online. Do you want to check manually?")), @@ -431,7 +429,7 @@ void zen::checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck, wx { case GET_VER_SUCCESS: lastUpdateCheck = wxGetLocalTime(); - lastOnlineVersion = onlineVersion; + lastOnlineVersion = onlineVersion; if (isNewerFreeFileSyncVersion(onlineVersion)) { @@ -453,7 +451,7 @@ void zen::checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck, wx break; //ignore this error case GET_VER_PAGE_NOT_FOUND: - lastOnlineVersion = L"unknown"; + lastOnlineVersion = L"unknown"; switch (showConfirmationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg(). setTitle(_("Check for Program Updates")). setMainInstructions(_("Cannot find current FreeFileSync version number online. Do you want to check manually?")), diff --git a/FreeFileSync/Source/ui/custom_grid.cpp b/FreeFileSync/Source/ui/custom_grid.cpp index cf292bf5..1ff91f2b 100644 --- a/FreeFileSync/Source/ui/custom_grid.cpp +++ b/FreeFileSync/Source/ui/custom_grid.cpp @@ -133,12 +133,6 @@ void fillBackgroundDefaultColorAlternating(wxDC& dc, const wxRect& rect, bool ev } -Zstring getExtension(const Zstring& shortName) -{ - return contains(shortName, Zchar('.')) ? afterLast(shortName, Zchar('.')) : Zstring(); -}; - - class IconUpdater; class GridEventManager; class GridDataLeft; @@ -148,7 +142,7 @@ struct IconManager { IconManager(GridDataLeft& provLeft, GridDataRight& provRight, IconBuffer::IconSize sz) : iconBuffer(sz), - fileIcon (IconBuffer::genericFileIcon(sz)), + //fileIcon (IconBuffer::genericFileIcon(sz)), dirIcon (IconBuffer::genericDirIcon (sz)), linkOverlayIcon(IconBuffer::linkOverlayIcon(sz)), iconUpdater(make_unique<IconUpdater>(provLeft, provRight, iconBuffer)) {} @@ -156,13 +150,13 @@ struct IconManager void startIconUpdater(); IconBuffer& refIconBuffer() { return iconBuffer; } - wxBitmap getGenericFileIcon() const { return fileIcon; } + //wxBitmap getGenericFileIcon() const { return fileIcon; } wxBitmap getGenericDirIcon () const { return dirIcon; } wxBitmap getLinkOverlayIcon() const { return linkOverlayIcon; } private: IconBuffer iconBuffer; - const wxBitmap fileIcon; + //const wxBitmap fileIcon; const wxBitmap dirIcon; const wxBitmap linkOverlayIcon; @@ -222,7 +216,7 @@ public: void setIconManager(const std::shared_ptr<IconManager>& iconMgr) { iconMgr_ = iconMgr; } - void updateNewAndGetUnbufferedIcons(std::vector<Zstring>& newLoad) //loads all not yet drawn icons + void updateNewAndGetUnbufferedIcons(std::vector<AbstractPathRef>& newLoad) //loads all not yet drawn icons { if (iconMgr_) { @@ -238,24 +232,24 @@ public: if (isFailedLoad(currentRow)) //find failed attempts to load icon { const IconInfo ii = getIconInfo(currentRow); - if (!ii.iconPath.empty()) + if (ii.type == IconInfo::ICON_PATH) { //test if they are already loaded in buffer: - if (iconMgr_->refIconBuffer().readyForRetrieval(ii.iconPath)) + if (iconMgr_->refIconBuffer().readyForRetrieval(ii.fsObj->template getAbstractPath<side>())) { //do a *full* refresh for *every* failed load to update partial DC updates while scrolling refreshCell(refGrid(), currentRow, static_cast<ColumnType>(COL_TYPE_FILENAME)); setFailedLoad(currentRow, false); } else //not yet in buffer: mark for async. loading - newLoad.push_back(ii.iconPath); + newLoad.push_back(ii.fsObj->template getAbstractPath<side>()); } } } } } - void getUnbufferedIconsForPreload(std::vector<std::pair<ptrdiff_t, Zstring>>& newLoad) //return (priority, filepath) list + void getUnbufferedIconsForPreload(std::vector<std::pair<ptrdiff_t, AbstractPathRef>>& newLoad) //return (priority, filepath) list { if (iconMgr_) { @@ -271,9 +265,9 @@ public: const ptrdiff_t currentRow = rowsOnScreen.first - (preloadSize + 1) / 2 + getAlternatingPos(i, visibleRowCount + preloadSize); //for odd preloadSize start one row earlier const IconInfo ii = getIconInfo(currentRow); - if (!ii.iconPath.empty()) - if (!iconMgr_->refIconBuffer().readyForRetrieval(ii.iconPath)) - newLoad.emplace_back(i, ii.iconPath); //insert least-important items on outer rim first + if (ii.type == IconInfo::ICON_PATH) + if (!iconMgr_->refIconBuffer().readyForRetrieval(ii.fsObj->template getAbstractPath<side>())) + newLoad.emplace_back(i, ii.fsObj->template getAbstractPath<side>()); //insert least-important items on outer rim first } } } @@ -397,106 +391,89 @@ private: { struct GetTextValue : public FSObjectVisitor { - GetTextValue(ColumnTypeRim colType, const FileSystemObject& fso) : colType_(colType), fsObj_(fso) {} + GetTextValue(ColumnTypeRim colType) : colType_(colType) {} void visit(const FilePair& fileObj) override { - switch (colType_) + value = [&] { - case COL_TYPE_FULL_PATH: - value = toWx(fileObj.getFullPath<side>()); - break; - case COL_TYPE_FILENAME: - value = toWx(fileObj.getItemName<side>()); - break; - case COL_TYPE_REL_FOLDER: - value = toWx(beforeLast(fileObj.getPairRelativePath(), FILE_NAME_SEPARATOR)); //returns empty string if ch not found - break; - case COL_TYPE_BASE_DIRECTORY: - value = toWx(fileObj.getBaseDirPf<side>()); - break; - case COL_TYPE_SIZE: - if (!fsObj_.isEmpty<side>()) - value = zen::toGuiString(fileObj.getFileSize<side>()); - - // -> test file id - //if (!fsObj_.isEmpty<side>()) - // value = toGuiString(fileObj.getFileId<side>().second) + L" " + toGuiString(fileObj.getFileId<side>().first); - break; - case COL_TYPE_DATE: - if (!fsObj_.isEmpty<side>()) - value = zen::utcToLocalTimeString(fileObj.getLastWriteTime<side>()); - break; - case COL_TYPE_EXTENSION: - value = toWx(getExtension(fileObj.getItemName<side>())); - break; - } + switch (colType_) + { + case COL_TYPE_FULL_PATH: + return fileObj.isEmpty<side>() ? wxString() : toWx(ABF::getDisplayPath(fileObj.getAbstractPath<side>())); + case COL_TYPE_FILENAME: + return toWx(fileObj.getItemName<side>()); + case COL_TYPE_REL_FOLDER: + return toWx(beforeLast(fileObj.getPairRelativePath(), FILE_NAME_SEPARATOR)); //returns empty string if ch not found + case COL_TYPE_BASE_DIRECTORY: + return toWx(ABF::getDisplayPath(fileObj.getABF<side>().getAbstractPath())); + case COL_TYPE_SIZE: + //return fileObj.isEmpty<side>() ? wxString() : utfCvrtTo<wxString>(fileObj.getFileId<side>()); // -> test file id + return fileObj.isEmpty<side>() ? wxString() : wxString(toGuiString(fileObj.getFileSize<side>())); + case COL_TYPE_DATE: + return fileObj.isEmpty<side>() ? wxString() : wxString(utcToLocalTimeString(fileObj.getLastWriteTime<side>())); + case COL_TYPE_EXTENSION: + return toWx(getFileExtension(fileObj.getItemName<side>())); + } + assert(false); + return wxString(); + }(); } void visit(const SymlinkPair& linkObj) override { - switch (colType_) + value = [&] { - case COL_TYPE_FULL_PATH: - value = toWx(linkObj.getFullPath<side>()); - break; - case COL_TYPE_FILENAME: - value = toWx(linkObj.getItemName<side>()); - break; - case COL_TYPE_REL_FOLDER: - value = toWx(beforeLast(linkObj.getPairRelativePath(), FILE_NAME_SEPARATOR)); //returns empty string if ch not found - break; - case COL_TYPE_BASE_DIRECTORY: - value = toWx(linkObj.getBaseDirPf<side>()); - break; - case COL_TYPE_SIZE: - if (!fsObj_.isEmpty<side>()) - value = L"<" + _("Symlink") + L">"; - break; - case COL_TYPE_DATE: - if (!fsObj_.isEmpty<side>()) - value = zen::utcToLocalTimeString(linkObj.getLastWriteTime<side>()); - break; - case COL_TYPE_EXTENSION: - value = toWx(getExtension(linkObj.getItemName<side>())); - break; - } + switch (colType_) + { + case COL_TYPE_FULL_PATH: + return linkObj.isEmpty<side>() ? wxString() : toWx(ABF::getDisplayPath(linkObj.getAbstractPath<side>())); + case COL_TYPE_FILENAME: + return toWx(linkObj.getItemName<side>()); + case COL_TYPE_REL_FOLDER: + return toWx(beforeLast(linkObj.getPairRelativePath(), FILE_NAME_SEPARATOR)); //returns empty string if ch not found + case COL_TYPE_BASE_DIRECTORY: + return toWx(ABF::getDisplayPath(linkObj.getABF<side>().getAbstractPath())); + case COL_TYPE_SIZE: + return linkObj.isEmpty<side>() ? wxString() : wxString(L"<" + _("Symlink") + L">"); + case COL_TYPE_DATE: + return linkObj.isEmpty<side>() ? wxString() : wxString(utcToLocalTimeString(linkObj.getLastWriteTime<side>())); + case COL_TYPE_EXTENSION: + return toWx(getFileExtension(linkObj.getItemName<side>())); + } + assert(false); + return wxString(); + }(); } void visit(const DirPair& dirObj) override { - switch (colType_) + value = [&] { - case COL_TYPE_FULL_PATH: - value = toWx(dirObj.getFullPath<side>()); - break; - case COL_TYPE_FILENAME: - value = toWx(dirObj.getItemName<side>()); - break; - case COL_TYPE_REL_FOLDER: - value = toWx(beforeLast(dirObj.getPairRelativePath(), FILE_NAME_SEPARATOR)); //returns empty string if ch not found - break; - case COL_TYPE_BASE_DIRECTORY: - value = toWx(dirObj.getBaseDirPf<side>()); - break; - case COL_TYPE_SIZE: - if (!fsObj_.isEmpty<side>()) - value = L"<" + _("Folder") + L">"; - break; - case COL_TYPE_DATE: - if (!fsObj_.isEmpty<side>()) - value = wxEmptyString; - break; - case COL_TYPE_EXTENSION: - value = wxEmptyString; - break; - } + switch (colType_) + { + case COL_TYPE_FULL_PATH: + return dirObj.isEmpty<side>() ? wxString() : toWx(ABF::getDisplayPath(dirObj.getAbstractPath<side>())); + case COL_TYPE_FILENAME: + return toWx(dirObj.getItemName<side>()); + case COL_TYPE_REL_FOLDER: + return toWx(beforeLast(dirObj.getPairRelativePath(), FILE_NAME_SEPARATOR)); //returns empty string if ch not found + case COL_TYPE_BASE_DIRECTORY: + return toWx(ABF::getDisplayPath(dirObj.getABF<side>().getAbstractPath())); + case COL_TYPE_SIZE: + return dirObj.isEmpty<side>() ? wxString() : wxString(L"<" + _("Folder") + L">"); + case COL_TYPE_DATE: + return wxString(); + case COL_TYPE_EXTENSION: + return wxString(); + } + assert(false); + return wxString(); + }(); } ColumnTypeRim colType_; wxString value; - - const FileSystemObject& fsObj_; - } getVal(static_cast<ColumnTypeRim>(colType), *fsObj); + } getVal(static_cast<ColumnTypeRim>(colType)); fsObj->accept(getVal); return getVal.value; } @@ -548,13 +525,14 @@ private: return iconMgr_->getGenericDirIcon(); case IconInfo::ICON_PATH: - if (Opt<wxBitmap> tmpIco = iconMgr_->refIconBuffer().retrieveFileIcon(ii.iconPath)) + if (Opt<wxBitmap> tmpIco = iconMgr_->refIconBuffer().retrieveFileIcon(ii.fsObj->template getAbstractPath<side>())) return *tmpIco; setFailedLoad(row); //save status of failed icon load -> used for async. icon loading //falsify only! we want to avoid writing incorrect success values when only partially updating the DC, e.g. when scrolling, //see repaint behavior of ::ScrollWindow() function! - return iconMgr_->getGenericFileIcon(); //better than nothing + return iconMgr_->refIconBuffer().getIconByExtension(ii.fsObj->template getItemName<side>()); //better than nothing + //return iconMgr_->getGenericFileIcon(); case IconInfo::EMPTY: break; @@ -677,7 +655,7 @@ private: ICON_PATH, }; IconType type; - Zstring iconPath; //only set for type==ICON_PATH + const FileSystemObject* fsObj; //only set if type != EMPTY bool drawAsLink; }; @@ -688,6 +666,8 @@ private: const FileSystemObject* fsObj = getRawData(row); if (fsObj && !fsObj->isEmpty<side>()) { + out.fsObj = fsObj; + struct GetIcon : public FSObjectVisitor { GetIcon(IconInfo& ii) : ii_(ii) {} @@ -695,13 +675,11 @@ private: void visit(const FilePair& fileObj) override { ii_.type = IconInfo::ICON_PATH; - ii_.iconPath = fileObj.getFullPath<side>(); - ii_.drawAsLink = fileObj.isFollowedSymlink<side>() || hasLinkExtension(ii_.iconPath); + ii_.drawAsLink = fileObj.isFollowedSymlink<side>() || hasLinkExtension(fileObj.getItemName<side>()); } void visit(const SymlinkPair& linkObj) override { ii_.type = IconInfo::ICON_PATH; - ii_.iconPath = linkObj.getFullPath<side>(); ii_.drawAsLink = true; } void visit(const DirPair& dirObj) override @@ -725,7 +703,7 @@ private: if (!fsObj->isEmpty<side>()) { toolTip = toWx(getGridDataView() && getGridDataView()->getFolderPairCount() > 1 ? - fsObj->getFullPath<side>() : + ABF::getDisplayPath(fsObj->getAbstractPath<side>()) : fsObj->getRelativePath<side>()); struct AssembleTooltip : public FSObjectVisitor @@ -1713,16 +1691,16 @@ private: void loadIconsAsynchronously(wxEvent& event) //loads all (not yet) drawn icons { - std::vector<std::pair<ptrdiff_t, Zstring>> prefetchLoad; + std::vector<std::pair<ptrdiff_t, AbstractPathRef>> prefetchLoad; provLeft_ .getUnbufferedIconsForPreload(prefetchLoad); provRight_.getUnbufferedIconsForPreload(prefetchLoad); //make sure least-important prefetch rows are inserted first into workload (=> processed last) - typedef std::pair<ptrdiff_t, Zstring> Pft; //priority index nicely considers both grids at the same time! + using Pft = decltype(prefetchLoad)::value_type; //priority index nicely considers both grids at the same time! std::sort(prefetchLoad.begin(), prefetchLoad.end(), [](const Pft& lhs, const Pft& rhs) { return lhs.first < rhs.first; }); //last inserted items are processed first in icon buffer: - std::vector<Zstring> newLoad; + std::vector<AbstractPathRef> newLoad; for (const auto& item : prefetchLoad) newLoad.push_back(item.second); diff --git a/FreeFileSync/Source/ui/folder_history_box.cpp b/FreeFileSync/Source/ui/folder_history_box.cpp index 133dea86..35f5c54b 100644 --- a/FreeFileSync/Source/ui/folder_history_box.cpp +++ b/FreeFileSync/Source/ui/folder_history_box.cpp @@ -81,7 +81,7 @@ void FolderHistoryBox::setValueAndUpdateList(const wxString& dirpath) if (sharedHistory_.get()) { std::vector<Zstring> tmp = sharedHistory_->getList(); - std::sort(tmp.begin(), tmp.end(), LessFilename()); + std::sort(tmp.begin(), tmp.end(), LessFilePath()); if (!dirList.empty() && !tmp.empty()) dirList.push_back(FolderHistory::separationLine()); diff --git a/FreeFileSync/Source/ui/folder_history_box.h b/FreeFileSync/Source/ui/folder_history_box.h index 806c83fa..e078e4cd 100644 --- a/FreeFileSync/Source/ui/folder_history_box.h +++ b/FreeFileSync/Source/ui/folder_history_box.h @@ -38,11 +38,10 @@ public: if (dirpath.empty() || dirpath == zen::utfCvrtTo<Zstring>(separationLine())) return; - Zstring nameTmp = dirpath; - zen::trim(nameTmp); + const Zstring nameTmp = zen::trimCpy(dirpath); //insert new folder or put it to the front if already existing - zen::vector_remove_if(dirpaths_, [&](const Zstring& item) { return ::EqualFilename()(item, nameTmp); }); + zen::vector_remove_if(dirpaths_, [&](const Zstring& item) { return ::EqualFilePath()(item, nameTmp); }); dirpaths_.insert(dirpaths_.begin(), nameTmp); @@ -50,7 +49,7 @@ public: dirpaths_.resize(maxSize_); } - void delItem(const Zstring& dirpath) { zen::vector_remove_if(dirpaths_, [&](const Zstring& item) { return ::EqualFilename()(item, dirpath); }); } + void delItem(const Zstring& dirpath) { zen::vector_remove_if(dirpaths_, [&](const Zstring& item) { return ::EqualFilePath()(item, dirpath); }); } private: size_t maxSize_; diff --git a/FreeFileSync/Source/ui/folder_pair.h b/FreeFileSync/Source/ui/folder_pair.h index 5c544edf..89b84fb8 100644 --- a/FreeFileSync/Source/ui/folder_pair.h +++ b/FreeFileSync/Source/ui/folder_pair.h @@ -13,7 +13,7 @@ #include <wx+/bitmap_button.h> #include <wx+/image_tools.h> #include <wx+/image_resources.h> -#include "dir_name.h" +#include "folder_selector.h" #include "small_dlgs.h" #include "sync_cfg.h" #include "../lib/norm_filter.h" diff --git a/FreeFileSync/Source/ui/dir_name.cpp b/FreeFileSync/Source/ui/folder_selector.cpp index 438cf7a7..6220ace2 100644 --- a/FreeFileSync/Source/ui/dir_name.cpp +++ b/FreeFileSync/Source/ui/folder_selector.cpp @@ -4,20 +4,18 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#include "dir_name.h" +#include "folder_selector.h" #include <zen/thread.h> #include <zen/file_access.h> -#include <wx/dnd.h> -#include <wx/window.h> -#include <wx/textctrl.h> -#include <wx/statbox.h> #include <wx/dirdlg.h> #include <wx/scrolwin.h> #include <wx+/string_conv.h> #include <wx+/popup_dlg.h> -#include "../lib/resolve_path.h" -#include "folder_history_box.h" - +#include "../fs/concrete.h" +#include "../fs/native.h" +#ifdef _MSC_VER + #include "../fs/mtp.h" +#endif #ifdef ZEN_WIN #include <zen/dll.h> #include <zen/win_ver.h> @@ -27,61 +25,40 @@ using namespace zen; -const wxEventType zen::EVENT_ON_DIR_SELECTED = wxNewEventType(); -const wxEventType zen::EVENT_ON_DIR_MANUAL_CORRECTION = wxNewEventType(); - namespace { -void setDirectoryNameImpl(const wxString& dirpath, wxWindow& tooltipWnd, wxStaticText* staticText) +void setFolderPath(const Zstring& dirpath, FolderHistoryBox* comboBox, wxWindow& tooltipWnd, wxStaticText* staticText) //pointers are optional { - const wxString dirFormatted = utfCvrtTo<wxString>(getFormattedDirectoryPath(toZ(dirpath))); //may block when resolving [<volume name>] + if (comboBox) + comboBox->setValue(toWx(dirpath)); + + const Zstring displayPath = getResolvedDisplayPath(dirpath); //may block when resolving [<volume name>] tooltipWnd.SetToolTip(nullptr); //workaround wxComboBox bug http://trac.wxwidgets.org/ticket/10512 / http://trac.wxwidgets.org/ticket/12659 - tooltipWnd.SetToolTip(dirFormatted); //who knows when the real bugfix reaches mere mortals via an official release... + tooltipWnd.SetToolTip(toWx(displayPath)); //who knows when the real bugfix reaches mere mortals via an official release... if (staticText) { //change static box label only if there is a real difference to what is shown in wxTextCtrl anyway - wxString dirNormalized = dirpath; - trim(dirNormalized); - if (!dirNormalized.empty()) - if (!endsWith(dirNormalized, FILE_NAME_SEPARATOR)) - dirNormalized += FILE_NAME_SEPARATOR; + const Zstring dirpathFmt = trimCpy(dirpath); - staticText->SetLabel(dirNormalized == dirFormatted ? wxString(_("Drag && drop")) : dirFormatted); + staticText->SetLabel(EqualFilePath()(appendSeparator(dirpathFmt), appendSeparator(displayPath)) ? wxString(_("Drag && drop")) : toWx(displayPath)); } } +} +//############################################################################################################## -void setDirectoryName(const wxString& dirpath, - wxTextCtrl* txtCtrl, - wxWindow& tooltipWnd, - wxStaticText* staticText) //pointers are optional -{ - if (txtCtrl) - txtCtrl->ChangeValue(dirpath); - setDirectoryNameImpl(dirpath, tooltipWnd, staticText); -} +const wxEventType zen::EVENT_ON_DIR_SELECTED = wxNewEventType(); +const wxEventType zen::EVENT_ON_DIR_MANUAL_CORRECTION = wxNewEventType(); -void setDirectoryName(const wxString& dirpath, - FolderHistoryBox* comboBox, - wxWindow& tooltipWnd, - wxStaticText* staticText) //pointers are optional -{ - if (comboBox) - comboBox->setValue(dirpath); - setDirectoryNameImpl(dirpath, tooltipWnd, staticText); -} -} -//############################################################################################################## -template <class NameControl> -DirectoryName<NameControl>::DirectoryName(wxWindow& dropWindow, - wxButton& selectButton, - NameControl& dirpath, - wxStaticText* staticText, - wxWindow* dropWindow2) : +FolderSelector::FolderSelector(wxWindow& dropWindow, + wxButton& selectButton, + FolderHistoryBox& dirpath, + wxStaticText* staticText, + wxWindow* dropWindow2) : dropWindow_(dropWindow), dropWindow2_(dropWindow2), selectButton_(selectButton), @@ -90,36 +67,34 @@ DirectoryName<NameControl>::DirectoryName(wxWindow& dropWindow, { //prepare drag & drop setupFileDrop(dropWindow_); - dropWindow_.Connect(EVENT_DROP_FILE, FileDropEventHandler(DirectoryName::onFilesDropped), nullptr, this); + dropWindow_.Connect(EVENT_DROP_FILE, FileDropEventHandler(FolderSelector::onFilesDropped), nullptr, this); if (dropWindow2_) { setupFileDrop(*dropWindow2_); - dropWindow2_->Connect(EVENT_DROP_FILE, FileDropEventHandler(DirectoryName::onFilesDropped), nullptr, this); + dropWindow2_->Connect(EVENT_DROP_FILE, FileDropEventHandler(FolderSelector::onFilesDropped), nullptr, this); } //keep dirPicker and dirpath synchronous - dirpath_ .Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler (DirectoryName::onMouseWheel ), nullptr, this); - dirpath_ .Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(DirectoryName::onWriteDirManually), nullptr, this); - selectButton_.Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DirectoryName::onSelectDir ), nullptr, this); + dirpath_ .Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler (FolderSelector::onMouseWheel ), nullptr, this); + dirpath_ .Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(FolderSelector::onWriteDirManually), nullptr, this); + selectButton_.Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderSelector::onSelectDir ), nullptr, this); } -template <class NameControl> -DirectoryName<NameControl>::~DirectoryName() +FolderSelector::~FolderSelector() { - dropWindow_.Disconnect(EVENT_DROP_FILE, FileDropEventHandler(DirectoryName::onFilesDropped), nullptr, this); + dropWindow_.Disconnect(EVENT_DROP_FILE, FileDropEventHandler(FolderSelector::onFilesDropped), nullptr, this); if (dropWindow2_) - dropWindow2_->Disconnect(EVENT_DROP_FILE, FileDropEventHandler(DirectoryName::onFilesDropped), nullptr, this); + dropWindow2_->Disconnect(EVENT_DROP_FILE, FileDropEventHandler(FolderSelector::onFilesDropped), nullptr, this); - dirpath_ .Disconnect(wxEVT_MOUSEWHEEL, wxMouseEventHandler (DirectoryName::onMouseWheel ), nullptr, this); - dirpath_ .Disconnect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(DirectoryName::onWriteDirManually), nullptr, this); - selectButton_.Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DirectoryName::onSelectDir ), nullptr, this); + dirpath_ .Disconnect(wxEVT_MOUSEWHEEL, wxMouseEventHandler (FolderSelector::onMouseWheel ), nullptr, this); + dirpath_ .Disconnect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(FolderSelector::onWriteDirManually), nullptr, this); + selectButton_.Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderSelector::onSelectDir ), nullptr, this); } -template <class NameControl> -void DirectoryName<NameControl>::onMouseWheel(wxMouseEvent& event) +void FolderSelector::onMouseWheel(wxMouseEvent& event) { //for combobox: although switching through available items is wxWidgets default, this is NOT windows default, e.g. explorer //additionally this will delete manual entries, although all the users wanted is scroll the parent window! @@ -133,13 +108,11 @@ void DirectoryName<NameControl>::onMouseWheel(wxMouseEvent& event) evtHandler->AddPendingEvent(event); break; } - // event.Skip(); } -template <class NameControl> -void DirectoryName<NameControl>::onFilesDropped(FileDropEvent& event) +void FolderSelector::onFilesDropped(FileDropEvent& event) { const auto& files = event.getFiles(); if (files.empty()) @@ -147,20 +120,20 @@ void DirectoryName<NameControl>::onFilesDropped(FileDropEvent& event) if (acceptDrop(files, event.getDropPosition(), event.getDropWindow())) { - const wxString fileName = event.getFiles()[0]; - if (dirExists(toZ(fileName))) - setDirectoryName(fileName, &dirpath_, dirpath_, staticText_); + const Zstring fileName = toZ(event.getFiles()[0]); + if (dirExists(fileName)) + setFolderPath(fileName, &dirpath_, dirpath_, staticText_); else { - wxString parentName = beforeLast(fileName, utfCvrtTo<wxString>(FILE_NAME_SEPARATOR)); //returns empty string if ch not found + Zstring parentName = beforeLast(fileName, FILE_NAME_SEPARATOR); //returns empty string if ch not found #ifdef ZEN_WIN - if (endsWith(parentName, L":")) //volume name + if (endsWith(parentName, L":")) //volume root parentName += FILE_NAME_SEPARATOR; #endif - if (dirExists(toZ(parentName))) - setDirectoryName(parentName, &dirpath_, dirpath_, staticText_); + if (dirExists(parentName)) + setFolderPath(parentName, &dirpath_, dirpath_, staticText_); else //set original name unconditionally: usecase: inactive mapped network shares - setDirectoryName(fileName, &dirpath_, dirpath_, staticText_); + setFolderPath(fileName, &dirpath_, dirpath_, staticText_); } //notify action invoked by user @@ -172,10 +145,9 @@ void DirectoryName<NameControl>::onFilesDropped(FileDropEvent& event) } -template <class NameControl> -void DirectoryName<NameControl>::onWriteDirManually(wxCommandEvent& event) +void FolderSelector::onWriteDirManually(wxCommandEvent& event) { - setDirectoryName(event.GetString(), static_cast<NameControl*>(nullptr), dirpath_, staticText_); + setFolderPath(toZ(event.GetString()), nullptr, dirpath_, staticText_); wxCommandEvent dummy(EVENT_ON_DIR_MANUAL_CORRECTION); ProcessEvent(dummy); @@ -183,24 +155,39 @@ void DirectoryName<NameControl>::onWriteDirManually(wxCommandEvent& event) } -template <class NameControl> -void DirectoryName<NameControl>::onSelectDir(wxCommandEvent& event) +#ifdef ZEN_WIN +bool onIFileDialogAcceptFolder(void* wnd /*HWND*/, const wchar_t* folderPath, void* sink) { - wxString defaultdirpath; //default selection for dir picker - { - const Zstring dirFmt = getFormattedDirectoryPath(toZ(getPath())); - if (!dirFmt.empty()) + if (acceptsFolderPathPhraseNative(folderPath)) //noexcept) + return true; +#ifdef _MSC_VER + if (acceptsFolderPathPhraseMtp (folderPath)) //noexcept) + return true; +#endif + + const std::wstring msg = replaceCpy(_("The selected folder %x cannot be used with FreeFileSync. Please select a folder on a local file system, network or an MTP device."), L"%x", fmtFileName(folderPath)); + ::MessageBox(static_cast<HWND>(wnd), msg.c_str(), (_("Select a folder")).c_str(), MB_ICONWARNING); + //showNotificationDialog would not support HWND parent + return false; +} +#endif + + +void FolderSelector::onSelectDir(wxCommandEvent& event) +{ + //IFileDialog requirements for default path: 1. accepts native paths only!!! 2. path must exist! + Zstring defaultFolderPath; + if (Opt<Zstring> nativeFolderPath = AbstractBaseFolder::getNativeItemPath(createAbstractBaseFolder(getPath())->getAbstractPath())) + if (!nativeFolderPath->empty()) { - //convert to Zstring first: we don't want to pass wxString by value and risk MT issues! - auto ft = async([=] { return zen::dirExists(dirFmt); }); + auto ft = async([nativeFolderPath] { return dirExists(*nativeFolderPath); }); if (ft.timed_wait(boost::posix_time::milliseconds(200)) && ft.get()) //potentially slow network access: wait 200ms at most - defaultdirpath = utfCvrtTo<wxString>(dirFmt); + defaultFolderPath = *nativeFolderPath; } - } //wxDirDialog internally uses lame-looking SHBrowseForFolder(); we better use IFileDialog() instead! (remembers size and position!) - std::unique_ptr<wxString> newFolder; + Opt<Zstring> newFolder; #ifdef ZEN_WIN if (vistaOrLater()) { @@ -222,8 +209,10 @@ void DirectoryName<NameControl>::onSelectDir(wxCommandEvent& event) }; //some random GUID => have Windows save IFileDialog state separately from other file/dir pickers! showFolderPicker(static_cast<HWND>(selectButton_.GetHWND()), //in; ==HWND - defaultdirpath.empty() ? static_cast<const wchar_t*>(nullptr) : defaultdirpath.c_str(), //in, optional! + defaultFolderPath.empty() ? nullptr : defaultFolderPath.c_str(), //in, optional! &guid, + onIFileDialogAcceptFolder, + nullptr /*callbackSink*/, selectedFolder, //out: call freeString() after use! cancelled, //out errorMsg); //out, optional: call freeString() after use! @@ -234,19 +223,26 @@ void DirectoryName<NameControl>::onSelectDir(wxCommandEvent& event) } if (cancelled || !selectedFolder) return; - newFolder = make_unique<wxString>(selectedFolder); + +#ifdef _MSC_VER + //make sure FFS-specific explicit MTP-syntax is applied! + if (acceptsFolderPathPhraseMtp(selectedFolder)) //noexcept + newFolder = getResolvedDisplayPathMtp(selectedFolder); //noexcept + else +#endif + newFolder = Zstring(selectedFolder); } } #endif - if (!newFolder.get()) + if (!newFolder) { - wxDirDialog dirPicker(&selectButton_, _("Select a folder"), defaultdirpath); //put modal wxWidgets dialogs on stack: creating on freestore leads to memleak! + wxDirDialog dirPicker(&selectButton_, _("Select a folder"), toWx(defaultFolderPath)); //put modal wxWidgets dialogs on stack: creating on freestore leads to memleak! if (dirPicker.ShowModal() != wxID_OK) return; - newFolder = make_unique<wxString>(dirPicker.GetPath()); + newFolder = toZ(dirPicker.GetPath()); } - setDirectoryName(*newFolder, &dirpath_, dirpath_, staticText_); + setFolderPath(*newFolder, &dirpath_, dirpath_, staticText_); //notify action invoked by user wxCommandEvent dummy(EVENT_ON_DIR_SELECTED); @@ -254,23 +250,13 @@ void DirectoryName<NameControl>::onSelectDir(wxCommandEvent& event) } -template <class NameControl> -wxString DirectoryName<NameControl>::getPath() const -{ - return dirpath_.GetValue(); -} - - -template <class NameControl> -void DirectoryName<NameControl>::setPath(const wxString& dirpath) +Zstring FolderSelector::getPath() const { - setDirectoryName(dirpath, &dirpath_, dirpath_, staticText_); + return toZ(dirpath_.GetValue()); } -//explicit template instantiations -namespace zen +void FolderSelector::setPath(const Zstring& dirpath) { -template class DirectoryName<wxTextCtrl>; -template class DirectoryName<FolderHistoryBox>; + setFolderPath(dirpath, &dirpath_, dirpath_, staticText_); } diff --git a/FreeFileSync/Source/ui/dir_name.h b/FreeFileSync/Source/ui/folder_selector.h index 4bf9001d..122f9c89 100644 --- a/FreeFileSync/Source/ui/dir_name.h +++ b/FreeFileSync/Source/ui/folder_selector.h @@ -4,15 +4,14 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef DRAGANDDROP_H_INCLUDED -#define DRAGANDDROP_H_INCLUDED +#ifndef DIR_NAME_H_24857842375234523463425 +#define DIR_NAME_H_24857842375234523463425 -#include <vector> -#include <wx/event.h> -#include <wx/sizer.h> -#include <wx+/file_drop.h> +#include <zen/zstring.h> #include <wx/stattext.h> #include <wx/button.h> +#include <wx+/file_drop.h> +#include "folder_history_box.h" namespace zen { @@ -29,20 +28,19 @@ extern const wxEventType EVENT_ON_DIR_SELECTED; //directory is changed by the extern const wxEventType EVENT_ON_DIR_MANUAL_CORRECTION; //manual type-in //example: wnd.Connect(EVENT_ON_DIR_SELECTED, wxCommandEventHandler(MyDlg::OnDirSelected), nullptr, this); -template <class NameControl> //NameControl may be wxTextCtrl, FolderHistoryBox -class DirectoryName: public wxEvtHandler +class FolderSelector: public wxEvtHandler { public: - DirectoryName(wxWindow& dropWindow, - wxButton& selectButton, - NameControl& dirpath, - wxStaticText* staticText = nullptr, //optional - wxWindow* dropWindow2 = nullptr); // + FolderSelector(wxWindow& dropWindow, + wxButton& selectButton, + FolderHistoryBox& dirpath, + wxStaticText* staticText = nullptr, //optional + wxWindow* dropWindow2 = nullptr); // - ~DirectoryName(); + ~FolderSelector(); - wxString getPath() const; - void setPath(const wxString& dirpath); + Zstring getPath() const; + void setPath(const Zstring& dirpath); private: virtual bool acceptDrop(const std::vector<wxString>& droppedFiles, const wxPoint& clientPos, const wxWindow& wnd) { return true; }; //return true if drop should be processed @@ -52,13 +50,12 @@ private: void onWriteDirManually(wxCommandEvent& event); void onSelectDir (wxCommandEvent& event); - wxWindow& dropWindow_; - wxWindow* dropWindow2_; - wxButton& selectButton_; - NameControl& dirpath_; - wxStaticText* staticText_; //optional + wxWindow& dropWindow_; + wxWindow* dropWindow2_; + wxButton& selectButton_; + FolderHistoryBox& dirpath_; + wxStaticText* staticText_; //optional }; } - -#endif // DRAGANDDROP_H_INCLUDED +#endif //DIR_NAME_H_24857842375234523463425 diff --git a/FreeFileSync/Source/ui/grid_view.cpp b/FreeFileSync/Source/ui/grid_view.cpp index 2e0e6e52..a34d42c4 100644 --- a/FreeFileSync/Source/ui/grid_view.cpp +++ b/FreeFileSync/Source/ui/grid_view.cpp @@ -345,8 +345,8 @@ void GridView::setData(FolderComparison& folderCmp) folderPairCount = std::count_if(begin(folderCmp), end(folderCmp), [](const BaseDirPair& baseObj) //count non-empty pairs to distinguish single/multiple folder pair cases { - return !baseObj.getBaseDirPf<LEFT_SIDE >().empty() || - !baseObj.getBaseDirPf<RIGHT_SIDE>().empty(); + return !baseObj.getABF<LEFT_SIDE >().emptyBaseFolderPath() || + !baseObj.getABF<RIGHT_SIDE>().emptyBaseFolderPath(); }); for (auto it = begin(folderCmp); it != end(folderCmp); ++it) diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp index c233de4c..b3350d8e 100644 --- a/FreeFileSync/Source/ui/gui_generated.cpp +++ b/FreeFileSync/Source/ui/gui_generated.cpp @@ -19,971 +19,971 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( 640,400 ), wxDefaultSize ); - - m_menubar1 = new wxMenuBar( 0 ); - m_menuFile = new wxMenu(); - m_menuItemNew = new wxMenuItem( m_menuFile, wxID_NEW, wxString( _("&New") ) + wxT('\t') + wxT("Ctrl+N"), wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItemNew ); - - m_menuItemLoad = new wxMenuItem( m_menuFile, wxID_OPEN, wxString( _("&Open...") ) + wxT('\t') + wxT("Ctrl+O"), wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItemLoad ); - - m_menuFile->AppendSeparator(); - - m_menuItemSave = new wxMenuItem( m_menuFile, wxID_SAVE, wxString( _("&Save") ) + wxT('\t') + wxT("Ctrl+S"), wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItemSave ); - - m_menuItemSaveAs = new wxMenuItem( m_menuFile, wxID_SAVEAS, wxString( _("Save &as...") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItemSaveAs ); - - m_menuItemSaveAsBatch = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("Save as &batch job...") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItemSaveAsBatch ); - - m_menuFile->AppendSeparator(); - - wxMenuItem* m_menuItem4; - m_menuItem4 = new wxMenuItem( m_menuFile, wxID_EXIT, wxString( _("E&xit") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItem4 ); - - m_menubar1->Append( m_menuFile, _("&File") ); - - m_menu4 = new wxMenu(); - m_menuItemCompare = new wxMenuItem( m_menu4, wxID_ANY, wxString( _("Start &comparison") ) + wxT('\t') + wxT("F5"), wxEmptyString, wxITEM_NORMAL ); - m_menu4->Append( m_menuItemCompare ); - - m_menu4->AppendSeparator(); - - m_menuItemCompSettings = new wxMenuItem( m_menu4, wxID_ANY, wxString( _("C&omparison settings") ) + wxT('\t') + wxT("F6"), wxEmptyString, wxITEM_NORMAL ); - m_menu4->Append( m_menuItemCompSettings ); - - m_menuItemFilter = new wxMenuItem( m_menu4, wxID_ANY, wxString( _("&Filter settings") ) + wxT('\t') + wxT("F7"), wxEmptyString, wxITEM_NORMAL ); - m_menu4->Append( m_menuItemFilter ); - - m_menuItemSyncSettings = new wxMenuItem( m_menu4, wxID_ANY, wxString( _("S&ynchronization settings") ) + wxT('\t') + wxT("F8"), wxEmptyString, wxITEM_NORMAL ); - m_menu4->Append( m_menuItemSyncSettings ); - - m_menu4->AppendSeparator(); - - m_menuItemSynchronize = new wxMenuItem( m_menu4, wxID_ANY, wxString( _("Start &synchronization") ) + wxT('\t') + wxT("F9"), wxEmptyString, wxITEM_NORMAL ); - m_menu4->Append( m_menuItemSynchronize ); - - m_menubar1->Append( m_menu4, _("&Actions") ); - - m_menuTools = new wxMenu(); - m_menuItemOptions = new wxMenuItem( m_menuTools, wxID_PREFERENCES, wxString( _("&Options") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuTools->Append( m_menuItemOptions ); - - m_menuLanguages = new wxMenu(); - wxMenuItem* m_menuLanguagesItem = new wxMenuItem( m_menuTools, wxID_ANY, _("&Language"), wxEmptyString, wxITEM_NORMAL, m_menuLanguages ); - m_menuTools->Append( m_menuLanguagesItem ); - - m_menuTools->AppendSeparator(); - - wxMenuItem* m_menuItem15; - m_menuItem15 = new wxMenuItem( m_menuTools, wxID_FIND, wxString( _("&Find...") ) + wxT('\t') + wxT("Ctrl+F"), wxEmptyString, wxITEM_NORMAL ); - m_menuTools->Append( m_menuItem15 ); - - wxMenuItem* m_menuItem51; - m_menuItem51 = new wxMenuItem( m_menuTools, wxID_ANY, wxString( _("&Reset layout") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuTools->Append( m_menuItem51 ); - - wxMenuItem* m_menuItem5; - m_menuItem5 = new wxMenuItem( m_menuTools, wxID_ANY, wxString( _("&Export file list...") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuTools->Append( m_menuItem5 ); - - m_menubar1->Append( m_menuTools, _("&Tools") ); - - m_menuHelp = new wxMenu(); - m_menuItemHelp = new wxMenuItem( m_menuHelp, wxID_HELP, wxString( _("&View help") ) + wxT('\t') + wxT("F1"), wxEmptyString, wxITEM_NORMAL ); - m_menuHelp->Append( m_menuItemHelp ); - - m_menuCheckVersion = new wxMenu(); - wxMenuItem* m_menuCheckVersionItem = new wxMenuItem( m_menuHelp, wxID_ANY, _("&Check for new version"), wxEmptyString, wxITEM_NORMAL, m_menuCheckVersion ); - m_menuItemCheckVersionNow = new wxMenuItem( m_menuCheckVersion, wxID_ANY, wxString( _("&Check now") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuCheckVersion->Append( m_menuItemCheckVersionNow ); - - m_menuItemCheckVersionAuto = new wxMenuItem( m_menuCheckVersion, wxID_ANY, wxString( _("Check &automatically once a week") ) , wxEmptyString, wxITEM_CHECK ); - m_menuCheckVersion->Append( m_menuItemCheckVersionAuto ); - m_menuItemCheckVersionAuto->Check( true ); - - m_menuHelp->Append( m_menuCheckVersionItem ); - - m_menuHelp->AppendSeparator(); - - m_menuItemAbout = new wxMenuItem( m_menuHelp, wxID_ABOUT, wxString( _("&About") ) + wxT('\t') + wxT("Shift+F1"), wxEmptyString, wxITEM_NORMAL ); - m_menuHelp->Append( m_menuItemAbout ); - - m_menubar1->Append( m_menuHelp, _("&Help") ); - - this->SetMenuBar( m_menubar1 ); - - bSizerPanelHolder = new wxBoxSizer( wxVERTICAL ); - - m_panelTopButtons = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxRAISED_BORDER|wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer1791; - bSizer1791 = new wxBoxSizer( wxVERTICAL ); - - bSizerTopButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonCancel = new zen::BitmapTextButton( m_panelTopButtons, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonCancel->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - m_buttonCancel->Enable( false ); - m_buttonCancel->Hide(); - - bSizerTopButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_buttonCompare = new zen::BitmapTextButton( m_panelTopButtons, wxID_ANY, _("Compare"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonCompare->SetDefault(); - m_buttonCompare->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - m_buttonCompare->SetToolTip( _("dummy") ); - - bSizerTopButtons->Add( m_buttonCompare, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerTopButtons->Add( 3, 3, 0, 0, 5 ); - - m_bpButtonCmpConfig = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW ); - m_bpButtonCmpConfig->SetToolTip( _("dummy") ); - - bSizerTopButtons->Add( m_bpButtonCmpConfig, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerTopButtons->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerTopButtons->Add( 5, 5, 0, 0, 5 ); - - m_bpButtonFilter = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 60,-1 ), wxBU_AUTODRAW|wxFULL_REPAINT_ON_RESIZE ); - bSizerTopButtons->Add( m_bpButtonFilter, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerTopButtons->Add( 5, 5, 0, 0, 5 ); - - - bSizerTopButtons->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonSyncConfig = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW ); - m_bpButtonSyncConfig->SetToolTip( _("dummy") ); - - bSizerTopButtons->Add( m_bpButtonSyncConfig, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerTopButtons->Add( 3, 3, 0, 0, 5 ); - - m_buttonSync = new zen::BitmapTextButton( m_panelTopButtons, wxID_ANY, _("Synchronize"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonSync->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - m_buttonSync->SetToolTip( _("dummy") ); - - bSizerTopButtons->Add( m_buttonSync, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer1791->Add( bSizerTopButtons, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - m_panelTopButtons->SetSizer( bSizer1791 ); - m_panelTopButtons->Layout(); - bSizer1791->Fit( m_panelTopButtons ); - bSizerPanelHolder->Add( m_panelTopButtons, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - m_panelDirectoryPairs = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSTATIC_BORDER|wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer1601; - bSizer1601 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer91; - bSizer91 = new wxBoxSizer( wxHORIZONTAL ); - - m_panelTopLeft = new wxPanel( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelTopLeft->SetMinSize( wxSize( 1,-1 ) ); - - wxFlexGridSizer* fgSizer8; - fgSizer8 = new wxFlexGridSizer( 0, 2, 0, 0 ); - fgSizer8->AddGrowableCol( 1 ); - fgSizer8->SetFlexibleDirection( wxBOTH ); - fgSizer8->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_ALL ); - - - fgSizer8->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextResolvedPathL = new wxStaticText( m_panelTopLeft, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextResolvedPathL->Wrap( -1 ); - fgSizer8->Add( m_staticTextResolvedPathL, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); - - wxBoxSizer* bSizer159; - bSizer159 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonAddPair = new wxBitmapButton( m_panelTopLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - m_bpButtonAddPair->SetToolTip( _("Add folder pair") ); - - bSizer159->Add( m_bpButtonAddPair, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonRemovePair = new wxBitmapButton( m_panelTopLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - m_bpButtonRemovePair->SetToolTip( _("Remove folder pair") ); - - bSizer159->Add( m_bpButtonRemovePair, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - fgSizer8->Add( bSizer159, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - wxBoxSizer* bSizer182; - bSizer182 = new wxBoxSizer( wxHORIZONTAL ); - - m_directoryLeft = new FolderHistoryBox( m_panelTopLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer182->Add( m_directoryLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectDirLeft = new wxButton( m_panelTopLeft, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectDirLeft->SetToolTip( _("Select a folder") ); - - bSizer182->Add( m_buttonSelectDirLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - fgSizer8->Add( bSizer182, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - m_panelTopLeft->SetSizer( fgSizer8 ); - m_panelTopLeft->Layout(); - fgSizer8->Fit( m_panelTopLeft ); - bSizer91->Add( m_panelTopLeft, 1, wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); - - m_panelTopMiddle = new wxPanel( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer1771; - bSizer1771 = new wxBoxSizer( wxVERTICAL ); - - - bSizer1771->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_bpButtonSwapSides = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW ); - m_bpButtonSwapSides->SetToolTip( _("Swap sides") ); - - bSizer1771->Add( m_bpButtonSwapSides, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - wxBoxSizer* bSizer160; - bSizer160 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonAltCompCfg = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer160->Add( m_bpButtonAltCompCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonLocalFilter = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer160->Add( m_bpButtonLocalFilter, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 2 ); - - m_bpButtonAltSyncCfg = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer160->Add( m_bpButtonAltSyncCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer1771->Add( bSizer160, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer1771->Add( 0, 0, 1, wxEXPAND, 5 ); - - - m_panelTopMiddle->SetSizer( bSizer1771 ); - m_panelTopMiddle->Layout(); - bSizer1771->Fit( m_panelTopMiddle ); - bSizer91->Add( m_panelTopMiddle, 0, wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_panelTopRight = new wxPanel( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelTopRight->SetMinSize( wxSize( 1,-1 ) ); - - wxBoxSizer* bSizer183; - bSizer183 = new wxBoxSizer( wxVERTICAL ); - - m_staticTextResolvedPathR = new wxStaticText( m_panelTopRight, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextResolvedPathR->Wrap( -1 ); - bSizer183->Add( m_staticTextResolvedPathR, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); - - wxBoxSizer* bSizer179; - bSizer179 = new wxBoxSizer( wxHORIZONTAL ); - - m_directoryRight = new FolderHistoryBox( m_panelTopRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer179->Add( m_directoryRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectDirRight = new wxButton( m_panelTopRight, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectDirRight->SetToolTip( _("Select a folder") ); - - bSizer179->Add( m_buttonSelectDirRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer183->Add( bSizer179, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - m_panelTopRight->SetSizer( bSizer183 ); - m_panelTopRight->Layout(); - bSizer183->Fit( m_panelTopRight ); - bSizer91->Add( m_panelTopRight, 1, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - - bSizer1601->Add( bSizer91, 0, wxEXPAND, 5 ); - - m_scrolledWindowFolderPairs = new wxScrolledWindow( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), wxHSCROLL|wxVSCROLL ); - m_scrolledWindowFolderPairs->SetScrollRate( 10, 10 ); - m_scrolledWindowFolderPairs->SetMinSize( wxSize( -1,0 ) ); - - bSizerAddFolderPairs = new wxBoxSizer( wxVERTICAL ); - - - m_scrolledWindowFolderPairs->SetSizer( bSizerAddFolderPairs ); - m_scrolledWindowFolderPairs->Layout(); - bSizerAddFolderPairs->Fit( m_scrolledWindowFolderPairs ); - bSizer1601->Add( m_scrolledWindowFolderPairs, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - m_panelDirectoryPairs->SetSizer( bSizer1601 ); - m_panelDirectoryPairs->Layout(); - bSizer1601->Fit( m_panelDirectoryPairs ); - bSizerPanelHolder->Add( m_panelDirectoryPairs, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - m_gridNavi = new zen::Grid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); - m_gridNavi->SetScrollRate( 5, 5 ); - bSizerPanelHolder->Add( m_gridNavi, 1, wxEXPAND, 5 ); - - m_panelCenter = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer1711; - bSizer1711 = new wxBoxSizer( wxVERTICAL ); - - m_splitterMain = new zen::TripleSplitter( m_panelCenter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer1781; - bSizer1781 = new wxBoxSizer( wxHORIZONTAL ); - - m_gridMainL = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); - m_gridMainL->SetScrollRate( 5, 5 ); - bSizer1781->Add( m_gridMainL, 1, wxEXPAND, 5 ); - - m_gridMainC = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); - m_gridMainC->SetScrollRate( 5, 5 ); - bSizer1781->Add( m_gridMainC, 0, wxEXPAND, 5 ); - - m_gridMainR = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); - m_gridMainR->SetScrollRate( 5, 5 ); - bSizer1781->Add( m_gridMainR, 1, wxEXPAND, 5 ); - - - m_splitterMain->SetSizer( bSizer1781 ); - m_splitterMain->Layout(); - bSizer1781->Fit( m_splitterMain ); - bSizer1711->Add( m_splitterMain, 1, wxEXPAND, 5 ); - - m_panelStatusBar = new wxPanel( m_panelCenter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSTATIC_BORDER|wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer451; - bSizer451 = new wxBoxSizer( wxHORIZONTAL ); - - bSizer451->SetMinSize( wxSize( -1,22 ) ); - bSizerFileStatus = new wxBoxSizer( wxHORIZONTAL ); - - bSizerStatusLeft = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer53; - bSizer53 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer53->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - bSizerStatusLeftDirectories = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapSmallDirectoryLeft = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizerStatusLeftDirectories->Add( m_bitmapSmallDirectoryLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusLeftDirectories->Add( 2, 0, 0, 0, 5 ); - - m_staticTextStatusLeftDirs = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatusLeftDirs->Wrap( -1 ); - bSizerStatusLeftDirectories->Add( m_staticTextStatusLeftDirs, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer53->Add( bSizerStatusLeftDirectories, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - bSizerStatusLeftFiles = new wxBoxSizer( wxHORIZONTAL ); - - - bSizerStatusLeftFiles->Add( 10, 0, 0, 0, 5 ); - - m_bitmapSmallFileLeft = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizerStatusLeftFiles->Add( m_bitmapSmallFileLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusLeftFiles->Add( 2, 0, 0, 0, 5 ); - - m_staticTextStatusLeftFiles = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatusLeftFiles->Wrap( -1 ); - bSizerStatusLeftFiles->Add( m_staticTextStatusLeftFiles, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusLeftFiles->Add( 4, 0, 0, 0, 5 ); - - m_staticTextStatusLeftBytes = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatusLeftBytes->Wrap( -1 ); - bSizerStatusLeftFiles->Add( m_staticTextStatusLeftBytes, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer53->Add( bSizerStatusLeftFiles, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer53->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusLeft->Add( bSizer53, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticline9 = new wxStaticLine( m_panelStatusBar, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizerStatusLeft->Add( m_staticline9, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxTOP, 2 ); - - - bSizerFileStatus->Add( bSizerStatusLeft, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerFileStatus->Add( 26, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextStatusMiddle = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatusMiddle->Wrap( -1 ); - bSizerFileStatus->Add( m_staticTextStatusMiddle, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerFileStatus->Add( 26, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - bSizerStatusRight = new wxBoxSizer( wxHORIZONTAL ); - - m_staticline10 = new wxStaticLine( m_panelStatusBar, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizerStatusRight->Add( m_staticline10, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP, 2 ); - - wxBoxSizer* bSizer52; - bSizer52 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer52->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - bSizerStatusRightDirectories = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapSmallDirectoryRight = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizerStatusRightDirectories->Add( m_bitmapSmallDirectoryRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusRightDirectories->Add( 2, 0, 0, 0, 5 ); - - m_staticTextStatusRightDirs = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatusRightDirs->Wrap( -1 ); - bSizerStatusRightDirectories->Add( m_staticTextStatusRightDirs, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer52->Add( bSizerStatusRightDirectories, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - bSizerStatusRightFiles = new wxBoxSizer( wxHORIZONTAL ); - - - bSizerStatusRightFiles->Add( 10, 0, 0, 0, 5 ); - - m_bitmapSmallFileRight = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizerStatusRightFiles->Add( m_bitmapSmallFileRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusRightFiles->Add( 2, 0, 0, 0, 5 ); - - m_staticTextStatusRightFiles = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatusRightFiles->Wrap( -1 ); - bSizerStatusRightFiles->Add( m_staticTextStatusRightFiles, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusRightFiles->Add( 4, 0, 0, 0, 5 ); - - m_staticTextStatusRightBytes = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatusRightBytes->Wrap( -1 ); - bSizerStatusRightFiles->Add( m_staticTextStatusRightBytes, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer52->Add( bSizerStatusRightFiles, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer52->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusRight->Add( bSizer52, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerFileStatus->Add( bSizerStatusRight, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer451->Add( bSizerFileStatus, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_staticTextFullStatus = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextFullStatus->Wrap( -1 ); - m_staticTextFullStatus->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer451->Add( m_staticTextFullStatus, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - - m_panelStatusBar->SetSizer( bSizer451 ); - m_panelStatusBar->Layout(); - bSizer451->Fit( m_panelStatusBar ); - bSizer1711->Add( m_panelStatusBar, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - - m_panelCenter->SetSizer( bSizer1711 ); - m_panelCenter->Layout(); - bSizer1711->Fit( m_panelCenter ); - bSizerPanelHolder->Add( m_panelCenter, 1, wxEXPAND, 5 ); - - m_panelSearch = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer1713; - bSizer1713 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonHideSearch = new wxBitmapButton( m_panelSearch, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - m_bpButtonHideSearch->SetToolTip( _("Close search bar") ); - - bSizer1713->Add( m_bpButtonHideSearch, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_staticText101 = new wxStaticText( m_panelSearch, wxID_ANY, _("Find:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText101->Wrap( -1 ); - bSizer1713->Add( m_staticText101, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_textCtrlSearchTxt = new wxTextCtrl( m_panelSearch, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220,-1 ), 0|wxWANTS_CHARS ); - m_textCtrlSearchTxt->SetMaxLength( 0 ); - bSizer1713->Add( m_textCtrlSearchTxt, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - - m_checkBoxMatchCase = new wxCheckBox( m_panelSearch, wxID_ANY, _("Match case"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer1713->Add( m_checkBoxMatchCase, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - - m_panelSearch->SetSizer( bSizer1713 ); - m_panelSearch->Layout(); - bSizer1713->Fit( m_panelSearch ); - bSizerPanelHolder->Add( m_panelSearch, 0, 0, 5 ); - - m_panelConfig = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - bSizerConfig = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer151; - bSizer151 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer17611; - bSizer17611 = new wxBoxSizer( wxVERTICAL ); - - m_bpButtonNew = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - m_bpButtonNew->SetToolTip( _("dummy") ); - - bSizer17611->Add( m_bpButtonNew, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_staticText951 = new wxStaticText( m_panelConfig, wxID_ANY, _("New"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText951->Wrap( -1 ); - bSizer17611->Add( m_staticText951, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 2 ); - - - bSizer151->Add( bSizer17611, 0, 0, 5 ); - - wxBoxSizer* bSizer1761; - bSizer1761 = new wxBoxSizer( wxVERTICAL ); - - m_bpButtonOpen = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - m_bpButtonOpen->SetToolTip( _("dummy") ); - - bSizer1761->Add( m_bpButtonOpen, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_staticText95 = new wxStaticText( m_panelConfig, wxID_ANY, _("Open..."), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText95->Wrap( -1 ); - bSizer1761->Add( m_staticText95, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 2 ); - - - bSizer151->Add( bSizer1761, 0, 0, 5 ); - - wxBoxSizer* bSizer175; - bSizer175 = new wxBoxSizer( wxVERTICAL ); - - m_bpButtonSave = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - m_bpButtonSave->SetToolTip( _("dummy") ); - - bSizer175->Add( m_bpButtonSave, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_staticText961 = new wxStaticText( m_panelConfig, wxID_ANY, _("Save"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText961->Wrap( -1 ); - bSizer175->Add( m_staticText961, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 2 ); - - - bSizer151->Add( bSizer175, 0, 0, 5 ); - - wxBoxSizer* bSizer174; - bSizer174 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer1772; - bSizer1772 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonSaveAs = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - m_bpButtonSaveAs->SetToolTip( _("dummy") ); - - bSizer1772->Add( m_bpButtonSaveAs, 0, 0, 5 ); - - m_bpButtonSaveAsBatch = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - m_bpButtonSaveAsBatch->SetToolTip( _("dummy") ); - - bSizer1772->Add( m_bpButtonSaveAsBatch, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer174->Add( bSizer1772, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_staticText97 = new wxStaticText( m_panelConfig, wxID_ANY, _("Save as..."), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText97->Wrap( -1 ); - bSizer174->Add( m_staticText97, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 2 ); - - - bSizer151->Add( bSizer174, 0, 0, 5 ); - - - bSizerConfig->Add( bSizer151, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_listBoxHistory = new wxListBox( m_panelConfig, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_EXTENDED|wxLB_NEEDED_SB ); - m_listBoxHistory->SetMinSize( wxSize( -1,40 ) ); - - bSizerConfig->Add( m_listBoxHistory, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - m_panelConfig->SetSizer( bSizerConfig ); - m_panelConfig->Layout(); - bSizerConfig->Fit( m_panelConfig ); - bSizerPanelHolder->Add( m_panelConfig, 0, 0, 5 ); - - m_panelViewFilter = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - bSizerViewFilter = new wxBoxSizer( wxHORIZONTAL ); - - m_staticTextViewType = new wxStaticText( m_panelViewFilter, wxID_ANY, _("View type:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextViewType->Wrap( -1 ); - bSizerViewFilter->Add( m_staticTextViewType, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonViewTypeSyncAction = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 82,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonViewTypeSyncAction, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxRIGHT, 5 ); - - m_bpButtonShowExcluded = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowExcluded, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizerViewFilter->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextSelectView = new wxStaticText( m_panelViewFilter, wxID_ANY, _("Select view:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextSelectView->Wrap( -1 ); - bSizerViewFilter->Add( m_staticTextSelectView, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowDeleteLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowDeleteLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowUpdateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowCreateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowLeftOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowLeftOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowLeftNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowLeftNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowEqual = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowEqual, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowDoNothing = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowDoNothing, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowDifferent = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowDifferent, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowRightNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowRightNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowRightOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowRightOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowCreateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowUpdateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowDeleteRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowDeleteRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowConflict = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowConflict, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizerViewFilter->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticText96 = new wxStaticText( m_panelViewFilter, wxID_ANY, _("Statistics:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText96->Wrap( -1 ); - bSizerViewFilter->Add( m_staticText96, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_panelStatistics = new wxPanel( m_panelViewFilter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER|wxTAB_TRAVERSAL ); - m_panelStatistics->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer1801 = new wxBoxSizer( wxVERTICAL ); - - bSizerStatistics = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer173; - bSizer173 = new wxBoxSizer( wxVERTICAL ); - - m_bitmapDeleteLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); - - bSizer173->Add( m_bitmapDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer173->Add( 5, 2, 0, 0, 5 ); - - - bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextDeleteLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDeleteLeft->Wrap( -1 ); - m_staticTextDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); - - bSizer173->Add( m_staticTextDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatistics->Add( bSizer173, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerStatistics->Add( 5, 5, 0, 0, 5 ); - - wxBoxSizer* bSizer172; - bSizer172 = new wxBoxSizer( wxVERTICAL ); - - m_bitmapUpdateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapUpdateLeft->SetToolTip( _("Number of files that will be updated") ); - - bSizer172->Add( m_bitmapUpdateLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer172->Add( 5, 2, 0, 0, 5 ); - - - bSizer172->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextUpdateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextUpdateLeft->Wrap( -1 ); - m_staticTextUpdateLeft->SetToolTip( _("Number of files that will be updated") ); - - bSizer172->Add( m_staticTextUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizerStatistics->Add( bSizer172, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerStatistics->Add( 5, 5, 0, 0, 5 ); - - wxBoxSizer* bSizer1712; - bSizer1712 = new wxBoxSizer( wxVERTICAL ); - - m_bitmapCreateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); - - bSizer1712->Add( m_bitmapCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer1712->Add( 5, 2, 0, 0, 5 ); - - - bSizer1712->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextCreateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextCreateLeft->Wrap( -1 ); - m_staticTextCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); - - bSizer1712->Add( m_staticTextCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizerStatistics->Add( bSizer1712, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerStatistics->Add( 5, 5, 0, 0, 5 ); - - bSizerData = new wxBoxSizer( wxVERTICAL ); - - m_bitmapData = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapData->SetToolTip( _("Total bytes to copy") ); - - bSizerData->Add( m_bitmapData, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizerData->Add( 5, 2, 0, 0, 5 ); - - - bSizerData->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextData = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextData->Wrap( -1 ); - m_staticTextData->SetToolTip( _("Total bytes to copy") ); - - bSizerData->Add( m_staticTextData, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatistics->Add( bSizerData, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerStatistics->Add( 5, 5, 0, 0, 5 ); - - wxBoxSizer* bSizer178; - bSizer178 = new wxBoxSizer( wxVERTICAL ); - - m_bitmapCreateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapCreateRight->SetToolTip( _("Number of files and folders that will be created") ); - - bSizer178->Add( m_bitmapCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer178->Add( 5, 2, 0, 0, 5 ); - - - bSizer178->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextCreateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextCreateRight->Wrap( -1 ); - m_staticTextCreateRight->SetToolTip( _("Number of files and folders that will be created") ); - - bSizer178->Add( m_staticTextCreateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatistics->Add( bSizer178, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerStatistics->Add( 5, 5, 0, 0, 5 ); - - wxBoxSizer* bSizer177; - bSizer177 = new wxBoxSizer( wxVERTICAL ); - - m_bitmapUpdateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapUpdateRight->SetToolTip( _("Number of files that will be updated") ); - - bSizer177->Add( m_bitmapUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer177->Add( 5, 2, 0, 0, 5 ); - - - bSizer177->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextUpdateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextUpdateRight->Wrap( -1 ); - m_staticTextUpdateRight->SetToolTip( _("Number of files that will be updated") ); - - bSizer177->Add( m_staticTextUpdateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatistics->Add( bSizer177, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerStatistics->Add( 5, 5, 0, 0, 5 ); - - wxBoxSizer* bSizer176; - bSizer176 = new wxBoxSizer( wxVERTICAL ); - - m_bitmapDeleteRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); - - bSizer176->Add( m_bitmapDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer176->Add( 5, 2, 0, 0, 5 ); - - - bSizer176->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextDeleteRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDeleteRight->Wrap( -1 ); - m_staticTextDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); - - bSizer176->Add( m_staticTextDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatistics->Add( bSizer176, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer1801->Add( bSizerStatistics, 0, wxALIGN_CENTER_VERTICAL|wxALL, 4 ); - - - m_panelStatistics->SetSizer( bSizer1801 ); - m_panelStatistics->Layout(); - bSizer1801->Fit( m_panelStatistics ); - bSizerViewFilter->Add( m_panelStatistics, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - m_panelViewFilter->SetSizer( bSizerViewFilter ); - m_panelViewFilter->Layout(); - bSizerViewFilter->Fit( m_panelViewFilter ); - bSizerPanelHolder->Add( m_panelViewFilter, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - this->SetSizer( bSizerPanelHolder ); - this->Layout(); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( MainDialogGenerated::OnClose ) ); - this->Connect( m_menuItemNew->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigNew ) ); - this->Connect( m_menuItemLoad->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ) ); - this->Connect( m_menuItemSave->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ) ); - this->Connect( m_menuItemSaveAs->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigSaveAs ) ); - this->Connect( m_menuItemSaveAsBatch->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnSaveAsBatchJob ) ); - this->Connect( m_menuItem4->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuQuit ) ); - this->Connect( m_menuItemCompare->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnCompare ) ); - this->Connect( m_menuItemCompSettings->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnCmpSettings ) ); - this->Connect( m_menuItemFilter->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigureFilter ) ); - this->Connect( m_menuItemSyncSettings->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnSyncSettings ) ); - this->Connect( m_menuItemSynchronize->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ) ); - this->Connect( m_menuItemOptions->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuOptions ) ); - this->Connect( m_menuItem15->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuFindItem ) ); - this->Connect( m_menuItem51->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuResetLayout ) ); - this->Connect( m_menuItem5->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuExportFileList ) ); - this->Connect( m_menuItemHelp->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnShowHelp ) ); - this->Connect( m_menuItemCheckVersionNow->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersion ) ); - this->Connect( m_menuItemCheckVersionAuto->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersionAutomatically ) ); - this->Connect( m_menuItemAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuAbout ) ); - m_buttonCompare->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCompare ), NULL, this ); - m_bpButtonCmpConfig->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCmpSettings ), NULL, this ); - m_bpButtonCmpConfig->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnCompSettingsContext ), NULL, this ); - m_bpButtonFilter->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigureFilter ), NULL, this ); - m_bpButtonFilter->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnGlobalFilterContext ), NULL, this ); - m_bpButtonSyncConfig->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncSettings ), NULL, this ); - m_bpButtonSyncConfig->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnSyncSettingsContext ), NULL, this ); - m_buttonSync->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ), NULL, this ); - m_bpButtonAddPair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnTopFolderPairAdd ), NULL, this ); - m_bpButtonRemovePair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnTopFolderPairRemove ), NULL, this ); - m_bpButtonSwapSides->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSwapSides ), NULL, this ); - m_bpButtonHideSearch->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnHideSearchPanel ), NULL, this ); - m_textCtrlSearchTxt->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( MainDialogGenerated::OnSearchGridEnter ), NULL, this ); - m_bpButtonNew->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigNew ), NULL, this ); - m_bpButtonOpen->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ), NULL, this ); - m_bpButtonSave->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ), NULL, this ); - m_bpButtonSaveAs->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigSaveAs ), NULL, this ); - m_bpButtonSaveAsBatch->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSaveAsBatchJob ), NULL, this ); - m_listBoxHistory->Connect( wxEVT_KEY_DOWN, wxKeyEventHandler( MainDialogGenerated::OnCfgHistoryKeyEvent ), NULL, this ); - m_listBoxHistory->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistory ), NULL, this ); - m_listBoxHistory->Connect( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistoryDoubleClick ), NULL, this ); - m_listBoxHistory->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnCfgHistoryRightClick ), NULL, this ); - m_bpButtonViewTypeSyncAction->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewType ), NULL, this ); - m_bpButtonShowExcluded->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowExcluded->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowDeleteLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowDeleteLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowUpdateLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowUpdateLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowCreateLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowCreateLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowLeftOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowLeftOnly->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowLeftNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowLeftNewer->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowEqual->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowEqual->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowDoNothing->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowDoNothing->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowDifferent->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowDifferent->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowRightNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowRightNewer->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowRightOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowRightOnly->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowCreateRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowCreateRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowUpdateRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowUpdateRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowDeleteRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowDeleteRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowConflict->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowConflict->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + this->SetSizeHints( wxSize( 640,400 ), wxDefaultSize ); + + m_menubar1 = new wxMenuBar( 0 ); + m_menuFile = new wxMenu(); + m_menuItemNew = new wxMenuItem( m_menuFile, wxID_NEW, wxString( _("&New") ) + wxT('\t') + wxT("Ctrl+N"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItemNew ); + + m_menuItemLoad = new wxMenuItem( m_menuFile, wxID_OPEN, wxString( _("&Open...") ) + wxT('\t') + wxT("Ctrl+O"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItemLoad ); + + m_menuFile->AppendSeparator(); + + m_menuItemSave = new wxMenuItem( m_menuFile, wxID_SAVE, wxString( _("&Save") ) + wxT('\t') + wxT("Ctrl+S"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItemSave ); + + m_menuItemSaveAs = new wxMenuItem( m_menuFile, wxID_SAVEAS, wxString( _("Save &as...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItemSaveAs ); + + m_menuItemSaveAsBatch = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("Save as &batch job...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItemSaveAsBatch ); + + m_menuFile->AppendSeparator(); + + wxMenuItem* m_menuItem4; + m_menuItem4 = new wxMenuItem( m_menuFile, wxID_EXIT, wxString( _("E&xit") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem4 ); + + m_menubar1->Append( m_menuFile, _("&File") ); + + m_menu4 = new wxMenu(); + m_menuItemCompare = new wxMenuItem( m_menu4, wxID_ANY, wxString( _("Start &comparison") ) + wxT('\t') + wxT("F5"), wxEmptyString, wxITEM_NORMAL ); + m_menu4->Append( m_menuItemCompare ); + + m_menu4->AppendSeparator(); + + m_menuItemCompSettings = new wxMenuItem( m_menu4, wxID_ANY, wxString( _("C&omparison settings") ) + wxT('\t') + wxT("F6"), wxEmptyString, wxITEM_NORMAL ); + m_menu4->Append( m_menuItemCompSettings ); + + m_menuItemFilter = new wxMenuItem( m_menu4, wxID_ANY, wxString( _("&Filter settings") ) + wxT('\t') + wxT("F7"), wxEmptyString, wxITEM_NORMAL ); + m_menu4->Append( m_menuItemFilter ); + + m_menuItemSyncSettings = new wxMenuItem( m_menu4, wxID_ANY, wxString( _("S&ynchronization settings") ) + wxT('\t') + wxT("F8"), wxEmptyString, wxITEM_NORMAL ); + m_menu4->Append( m_menuItemSyncSettings ); + + m_menu4->AppendSeparator(); + + m_menuItemSynchronize = new wxMenuItem( m_menu4, wxID_ANY, wxString( _("Start &synchronization") ) + wxT('\t') + wxT("F9"), wxEmptyString, wxITEM_NORMAL ); + m_menu4->Append( m_menuItemSynchronize ); + + m_menubar1->Append( m_menu4, _("&Actions") ); + + m_menuTools = new wxMenu(); + m_menuItemOptions = new wxMenuItem( m_menuTools, wxID_PREFERENCES, wxString( _("&Options") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuTools->Append( m_menuItemOptions ); + + m_menuLanguages = new wxMenu(); + wxMenuItem* m_menuLanguagesItem = new wxMenuItem( m_menuTools, wxID_ANY, _("&Language"), wxEmptyString, wxITEM_NORMAL, m_menuLanguages ); + m_menuTools->Append( m_menuLanguagesItem ); + + m_menuTools->AppendSeparator(); + + wxMenuItem* m_menuItem15; + m_menuItem15 = new wxMenuItem( m_menuTools, wxID_FIND, wxString( _("&Find...") ) + wxT('\t') + wxT("Ctrl+F"), wxEmptyString, wxITEM_NORMAL ); + m_menuTools->Append( m_menuItem15 ); + + wxMenuItem* m_menuItem51; + m_menuItem51 = new wxMenuItem( m_menuTools, wxID_ANY, wxString( _("&Reset layout") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuTools->Append( m_menuItem51 ); + + wxMenuItem* m_menuItem5; + m_menuItem5 = new wxMenuItem( m_menuTools, wxID_ANY, wxString( _("&Export file list...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuTools->Append( m_menuItem5 ); + + m_menubar1->Append( m_menuTools, _("&Tools") ); + + m_menuHelp = new wxMenu(); + m_menuItemHelp = new wxMenuItem( m_menuHelp, wxID_HELP, wxString( _("&View help") ) + wxT('\t') + wxT("F1"), wxEmptyString, wxITEM_NORMAL ); + m_menuHelp->Append( m_menuItemHelp ); + + m_menuCheckVersion = new wxMenu(); + wxMenuItem* m_menuCheckVersionItem = new wxMenuItem( m_menuHelp, wxID_ANY, _("&Check for new version"), wxEmptyString, wxITEM_NORMAL, m_menuCheckVersion ); + m_menuItemCheckVersionNow = new wxMenuItem( m_menuCheckVersion, wxID_ANY, wxString( _("&Check now") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuCheckVersion->Append( m_menuItemCheckVersionNow ); + + m_menuItemCheckVersionAuto = new wxMenuItem( m_menuCheckVersion, wxID_ANY, wxString( _("Check &automatically once a week") ) , wxEmptyString, wxITEM_CHECK ); + m_menuCheckVersion->Append( m_menuItemCheckVersionAuto ); + m_menuItemCheckVersionAuto->Check( true ); + + m_menuHelp->Append( m_menuCheckVersionItem ); + + m_menuHelp->AppendSeparator(); + + m_menuItemAbout = new wxMenuItem( m_menuHelp, wxID_ABOUT, wxString( _("&About") ) + wxT('\t') + wxT("Shift+F1"), wxEmptyString, wxITEM_NORMAL ); + m_menuHelp->Append( m_menuItemAbout ); + + m_menubar1->Append( m_menuHelp, _("&Help") ); + + this->SetMenuBar( m_menubar1 ); + + bSizerPanelHolder = new wxBoxSizer( wxVERTICAL ); + + m_panelTopButtons = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxRAISED_BORDER|wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer1791; + bSizer1791 = new wxBoxSizer( wxVERTICAL ); + + bSizerTopButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonCancel = new zen::BitmapTextButton( m_panelTopButtons, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCancel->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + m_buttonCancel->Enable( false ); + m_buttonCancel->Hide(); + + bSizerTopButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_buttonCompare = new zen::BitmapTextButton( m_panelTopButtons, wxID_ANY, _("Compare"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCompare->SetDefault(); + m_buttonCompare->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + m_buttonCompare->SetToolTip( _("dummy") ); + + bSizerTopButtons->Add( m_buttonCompare, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerTopButtons->Add( 3, 3, 0, 0, 5 ); + + m_bpButtonCmpConfig = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW ); + m_bpButtonCmpConfig->SetToolTip( _("dummy") ); + + bSizerTopButtons->Add( m_bpButtonCmpConfig, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerTopButtons->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerTopButtons->Add( 5, 5, 0, 0, 5 ); + + m_bpButtonFilter = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 60,-1 ), wxBU_AUTODRAW|wxFULL_REPAINT_ON_RESIZE ); + bSizerTopButtons->Add( m_bpButtonFilter, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerTopButtons->Add( 5, 5, 0, 0, 5 ); + + + bSizerTopButtons->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonSyncConfig = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW ); + m_bpButtonSyncConfig->SetToolTip( _("dummy") ); + + bSizerTopButtons->Add( m_bpButtonSyncConfig, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerTopButtons->Add( 3, 3, 0, 0, 5 ); + + m_buttonSync = new zen::BitmapTextButton( m_panelTopButtons, wxID_ANY, _("Synchronize"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonSync->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + m_buttonSync->SetToolTip( _("dummy") ); + + bSizerTopButtons->Add( m_buttonSync, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer1791->Add( bSizerTopButtons, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + m_panelTopButtons->SetSizer( bSizer1791 ); + m_panelTopButtons->Layout(); + bSizer1791->Fit( m_panelTopButtons ); + bSizerPanelHolder->Add( m_panelTopButtons, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_panelDirectoryPairs = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSTATIC_BORDER|wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer1601; + bSizer1601 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer91; + bSizer91 = new wxBoxSizer( wxHORIZONTAL ); + + m_panelTopLeft = new wxPanel( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelTopLeft->SetMinSize( wxSize( 1,-1 ) ); + + wxFlexGridSizer* fgSizer8; + fgSizer8 = new wxFlexGridSizer( 0, 2, 0, 0 ); + fgSizer8->AddGrowableCol( 1 ); + fgSizer8->SetFlexibleDirection( wxBOTH ); + fgSizer8->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_ALL ); + + + fgSizer8->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextResolvedPathL = new wxStaticText( m_panelTopLeft, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextResolvedPathL->Wrap( -1 ); + fgSizer8->Add( m_staticTextResolvedPathL, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); + + wxBoxSizer* bSizer159; + bSizer159 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonAddPair = new wxBitmapButton( m_panelTopLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonAddPair->SetToolTip( _("Add folder pair") ); + + bSizer159->Add( m_bpButtonAddPair, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonRemovePair = new wxBitmapButton( m_panelTopLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonRemovePair->SetToolTip( _("Remove folder pair") ); + + bSizer159->Add( m_bpButtonRemovePair, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + fgSizer8->Add( bSizer159, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + wxBoxSizer* bSizer182; + bSizer182 = new wxBoxSizer( wxHORIZONTAL ); + + m_directoryLeft = new FolderHistoryBox( m_panelTopLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer182->Add( m_directoryLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectDirLeft = new wxButton( m_panelTopLeft, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectDirLeft->SetToolTip( _("Select a folder") ); + + bSizer182->Add( m_buttonSelectDirLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + fgSizer8->Add( bSizer182, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + m_panelTopLeft->SetSizer( fgSizer8 ); + m_panelTopLeft->Layout(); + fgSizer8->Fit( m_panelTopLeft ); + bSizer91->Add( m_panelTopLeft, 1, wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); + + m_panelTopMiddle = new wxPanel( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer1771; + bSizer1771 = new wxBoxSizer( wxVERTICAL ); + + + bSizer1771->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bpButtonSwapSides = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW ); + m_bpButtonSwapSides->SetToolTip( _("Swap sides") ); + + bSizer1771->Add( m_bpButtonSwapSides, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + wxBoxSizer* bSizer160; + bSizer160 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonAltCompCfg = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer160->Add( m_bpButtonAltCompCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonLocalFilter = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer160->Add( m_bpButtonLocalFilter, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 2 ); + + m_bpButtonAltSyncCfg = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer160->Add( m_bpButtonAltSyncCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer1771->Add( bSizer160, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer1771->Add( 0, 0, 1, wxEXPAND, 5 ); + + + m_panelTopMiddle->SetSizer( bSizer1771 ); + m_panelTopMiddle->Layout(); + bSizer1771->Fit( m_panelTopMiddle ); + bSizer91->Add( m_panelTopMiddle, 0, wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_panelTopRight = new wxPanel( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelTopRight->SetMinSize( wxSize( 1,-1 ) ); + + wxBoxSizer* bSizer183; + bSizer183 = new wxBoxSizer( wxVERTICAL ); + + m_staticTextResolvedPathR = new wxStaticText( m_panelTopRight, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextResolvedPathR->Wrap( -1 ); + bSizer183->Add( m_staticTextResolvedPathR, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); + + wxBoxSizer* bSizer179; + bSizer179 = new wxBoxSizer( wxHORIZONTAL ); + + m_directoryRight = new FolderHistoryBox( m_panelTopRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer179->Add( m_directoryRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectDirRight = new wxButton( m_panelTopRight, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectDirRight->SetToolTip( _("Select a folder") ); + + bSizer179->Add( m_buttonSelectDirRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer183->Add( bSizer179, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + m_panelTopRight->SetSizer( bSizer183 ); + m_panelTopRight->Layout(); + bSizer183->Fit( m_panelTopRight ); + bSizer91->Add( m_panelTopRight, 1, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + + bSizer1601->Add( bSizer91, 0, wxEXPAND, 5 ); + + m_scrolledWindowFolderPairs = new wxScrolledWindow( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), wxHSCROLL|wxVSCROLL ); + m_scrolledWindowFolderPairs->SetScrollRate( 10, 10 ); + m_scrolledWindowFolderPairs->SetMinSize( wxSize( -1,0 ) ); + + bSizerAddFolderPairs = new wxBoxSizer( wxVERTICAL ); + + + m_scrolledWindowFolderPairs->SetSizer( bSizerAddFolderPairs ); + m_scrolledWindowFolderPairs->Layout(); + bSizerAddFolderPairs->Fit( m_scrolledWindowFolderPairs ); + bSizer1601->Add( m_scrolledWindowFolderPairs, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + m_panelDirectoryPairs->SetSizer( bSizer1601 ); + m_panelDirectoryPairs->Layout(); + bSizer1601->Fit( m_panelDirectoryPairs ); + bSizerPanelHolder->Add( m_panelDirectoryPairs, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_gridNavi = new zen::Grid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); + m_gridNavi->SetScrollRate( 5, 5 ); + bSizerPanelHolder->Add( m_gridNavi, 1, wxEXPAND, 5 ); + + m_panelCenter = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer1711; + bSizer1711 = new wxBoxSizer( wxVERTICAL ); + + m_splitterMain = new zen::TripleSplitter( m_panelCenter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer1781; + bSizer1781 = new wxBoxSizer( wxHORIZONTAL ); + + m_gridMainL = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); + m_gridMainL->SetScrollRate( 5, 5 ); + bSizer1781->Add( m_gridMainL, 1, wxEXPAND, 5 ); + + m_gridMainC = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); + m_gridMainC->SetScrollRate( 5, 5 ); + bSizer1781->Add( m_gridMainC, 0, wxEXPAND, 5 ); + + m_gridMainR = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); + m_gridMainR->SetScrollRate( 5, 5 ); + bSizer1781->Add( m_gridMainR, 1, wxEXPAND, 5 ); + + + m_splitterMain->SetSizer( bSizer1781 ); + m_splitterMain->Layout(); + bSizer1781->Fit( m_splitterMain ); + bSizer1711->Add( m_splitterMain, 1, wxEXPAND, 5 ); + + m_panelStatusBar = new wxPanel( m_panelCenter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSTATIC_BORDER|wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer451; + bSizer451 = new wxBoxSizer( wxHORIZONTAL ); + + bSizer451->SetMinSize( wxSize( -1,22 ) ); + bSizerFileStatus = new wxBoxSizer( wxHORIZONTAL ); + + bSizerStatusLeft = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer53; + bSizer53 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer53->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizerStatusLeftDirectories = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapSmallDirectoryLeft = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizerStatusLeftDirectories->Add( m_bitmapSmallDirectoryLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusLeftDirectories->Add( 2, 0, 0, 0, 5 ); + + m_staticTextStatusLeftDirs = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatusLeftDirs->Wrap( -1 ); + bSizerStatusLeftDirectories->Add( m_staticTextStatusLeftDirs, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer53->Add( bSizerStatusLeftDirectories, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizerStatusLeftFiles = new wxBoxSizer( wxHORIZONTAL ); + + + bSizerStatusLeftFiles->Add( 10, 0, 0, 0, 5 ); + + m_bitmapSmallFileLeft = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizerStatusLeftFiles->Add( m_bitmapSmallFileLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusLeftFiles->Add( 2, 0, 0, 0, 5 ); + + m_staticTextStatusLeftFiles = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatusLeftFiles->Wrap( -1 ); + bSizerStatusLeftFiles->Add( m_staticTextStatusLeftFiles, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusLeftFiles->Add( 4, 0, 0, 0, 5 ); + + m_staticTextStatusLeftBytes = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatusLeftBytes->Wrap( -1 ); + bSizerStatusLeftFiles->Add( m_staticTextStatusLeftBytes, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer53->Add( bSizerStatusLeftFiles, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer53->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusLeft->Add( bSizer53, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticline9 = new wxStaticLine( m_panelStatusBar, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizerStatusLeft->Add( m_staticline9, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxTOP, 2 ); + + + bSizerFileStatus->Add( bSizerStatusLeft, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerFileStatus->Add( 26, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextStatusMiddle = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatusMiddle->Wrap( -1 ); + bSizerFileStatus->Add( m_staticTextStatusMiddle, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerFileStatus->Add( 26, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizerStatusRight = new wxBoxSizer( wxHORIZONTAL ); + + m_staticline10 = new wxStaticLine( m_panelStatusBar, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizerStatusRight->Add( m_staticline10, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP, 2 ); + + wxBoxSizer* bSizer52; + bSizer52 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer52->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizerStatusRightDirectories = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapSmallDirectoryRight = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizerStatusRightDirectories->Add( m_bitmapSmallDirectoryRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusRightDirectories->Add( 2, 0, 0, 0, 5 ); + + m_staticTextStatusRightDirs = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatusRightDirs->Wrap( -1 ); + bSizerStatusRightDirectories->Add( m_staticTextStatusRightDirs, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer52->Add( bSizerStatusRightDirectories, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizerStatusRightFiles = new wxBoxSizer( wxHORIZONTAL ); + + + bSizerStatusRightFiles->Add( 10, 0, 0, 0, 5 ); + + m_bitmapSmallFileRight = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizerStatusRightFiles->Add( m_bitmapSmallFileRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusRightFiles->Add( 2, 0, 0, 0, 5 ); + + m_staticTextStatusRightFiles = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatusRightFiles->Wrap( -1 ); + bSizerStatusRightFiles->Add( m_staticTextStatusRightFiles, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusRightFiles->Add( 4, 0, 0, 0, 5 ); + + m_staticTextStatusRightBytes = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatusRightBytes->Wrap( -1 ); + bSizerStatusRightFiles->Add( m_staticTextStatusRightBytes, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer52->Add( bSizerStatusRightFiles, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer52->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusRight->Add( bSizer52, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerFileStatus->Add( bSizerStatusRight, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer451->Add( bSizerFileStatus, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_staticTextFullStatus = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextFullStatus->Wrap( -1 ); + m_staticTextFullStatus->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer451->Add( m_staticTextFullStatus, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + m_panelStatusBar->SetSizer( bSizer451 ); + m_panelStatusBar->Layout(); + bSizer451->Fit( m_panelStatusBar ); + bSizer1711->Add( m_panelStatusBar, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + + m_panelCenter->SetSizer( bSizer1711 ); + m_panelCenter->Layout(); + bSizer1711->Fit( m_panelCenter ); + bSizerPanelHolder->Add( m_panelCenter, 1, wxEXPAND, 5 ); + + m_panelSearch = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer1713; + bSizer1713 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonHideSearch = new wxBitmapButton( m_panelSearch, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonHideSearch->SetToolTip( _("Close search bar") ); + + bSizer1713->Add( m_bpButtonHideSearch, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_staticText101 = new wxStaticText( m_panelSearch, wxID_ANY, _("Find:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText101->Wrap( -1 ); + bSizer1713->Add( m_staticText101, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_textCtrlSearchTxt = new wxTextCtrl( m_panelSearch, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220,-1 ), 0|wxWANTS_CHARS ); + m_textCtrlSearchTxt->SetMaxLength( 0 ); + bSizer1713->Add( m_textCtrlSearchTxt, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + m_checkBoxMatchCase = new wxCheckBox( m_panelSearch, wxID_ANY, _("Match case"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer1713->Add( m_checkBoxMatchCase, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + m_panelSearch->SetSizer( bSizer1713 ); + m_panelSearch->Layout(); + bSizer1713->Fit( m_panelSearch ); + bSizerPanelHolder->Add( m_panelSearch, 0, 0, 5 ); + + m_panelConfig = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + bSizerConfig = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer151; + bSizer151 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer17611; + bSizer17611 = new wxBoxSizer( wxVERTICAL ); + + m_bpButtonNew = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonNew->SetToolTip( _("dummy") ); + + bSizer17611->Add( m_bpButtonNew, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_staticText951 = new wxStaticText( m_panelConfig, wxID_ANY, _("New"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText951->Wrap( -1 ); + bSizer17611->Add( m_staticText951, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 2 ); + + + bSizer151->Add( bSizer17611, 0, 0, 5 ); + + wxBoxSizer* bSizer1761; + bSizer1761 = new wxBoxSizer( wxVERTICAL ); + + m_bpButtonOpen = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonOpen->SetToolTip( _("dummy") ); + + bSizer1761->Add( m_bpButtonOpen, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_staticText95 = new wxStaticText( m_panelConfig, wxID_ANY, _("Open..."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText95->Wrap( -1 ); + bSizer1761->Add( m_staticText95, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 2 ); + + + bSizer151->Add( bSizer1761, 0, 0, 5 ); + + wxBoxSizer* bSizer175; + bSizer175 = new wxBoxSizer( wxVERTICAL ); + + m_bpButtonSave = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonSave->SetToolTip( _("dummy") ); + + bSizer175->Add( m_bpButtonSave, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_staticText961 = new wxStaticText( m_panelConfig, wxID_ANY, _("Save"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText961->Wrap( -1 ); + bSizer175->Add( m_staticText961, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 2 ); + + + bSizer151->Add( bSizer175, 0, 0, 5 ); + + wxBoxSizer* bSizer174; + bSizer174 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer1772; + bSizer1772 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonSaveAs = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonSaveAs->SetToolTip( _("dummy") ); + + bSizer1772->Add( m_bpButtonSaveAs, 0, 0, 5 ); + + m_bpButtonSaveAsBatch = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonSaveAsBatch->SetToolTip( _("dummy") ); + + bSizer1772->Add( m_bpButtonSaveAsBatch, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer174->Add( bSizer1772, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_staticText97 = new wxStaticText( m_panelConfig, wxID_ANY, _("Save as..."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText97->Wrap( -1 ); + bSizer174->Add( m_staticText97, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 2 ); + + + bSizer151->Add( bSizer174, 0, 0, 5 ); + + + bSizerConfig->Add( bSizer151, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_listBoxHistory = new wxListBox( m_panelConfig, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_EXTENDED|wxLB_NEEDED_SB ); + m_listBoxHistory->SetMinSize( wxSize( -1,40 ) ); + + bSizerConfig->Add( m_listBoxHistory, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + m_panelConfig->SetSizer( bSizerConfig ); + m_panelConfig->Layout(); + bSizerConfig->Fit( m_panelConfig ); + bSizerPanelHolder->Add( m_panelConfig, 0, 0, 5 ); + + m_panelViewFilter = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + bSizerViewFilter = new wxBoxSizer( wxHORIZONTAL ); + + m_staticTextViewType = new wxStaticText( m_panelViewFilter, wxID_ANY, _("View type:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextViewType->Wrap( -1 ); + bSizerViewFilter->Add( m_staticTextViewType, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonViewTypeSyncAction = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 82,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonViewTypeSyncAction, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxRIGHT, 5 ); + + m_bpButtonShowExcluded = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowExcluded, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizerViewFilter->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextSelectView = new wxStaticText( m_panelViewFilter, wxID_ANY, _("Select view:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextSelectView->Wrap( -1 ); + bSizerViewFilter->Add( m_staticTextSelectView, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowDeleteLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowDeleteLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowUpdateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowCreateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowLeftOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowLeftOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowLeftNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowLeftNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowEqual = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowEqual, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowDoNothing = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowDoNothing, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowDifferent = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowDifferent, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowRightNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowRightNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowRightOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowRightOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowCreateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowUpdateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowDeleteRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowDeleteRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowConflict = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowConflict, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizerViewFilter->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticText96 = new wxStaticText( m_panelViewFilter, wxID_ANY, _("Statistics:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText96->Wrap( -1 ); + bSizerViewFilter->Add( m_staticText96, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_panelStatistics = new wxPanel( m_panelViewFilter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER|wxTAB_TRAVERSAL ); + m_panelStatistics->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer1801 = new wxBoxSizer( wxVERTICAL ); + + bSizerStatistics = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer173; + bSizer173 = new wxBoxSizer( wxVERTICAL ); + + m_bitmapDeleteLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); + + bSizer173->Add( m_bitmapDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer173->Add( 5, 2, 0, 0, 5 ); + + + bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextDeleteLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDeleteLeft->Wrap( -1 ); + m_staticTextDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); + + bSizer173->Add( m_staticTextDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatistics->Add( bSizer173, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerStatistics->Add( 5, 5, 0, 0, 5 ); + + wxBoxSizer* bSizer172; + bSizer172 = new wxBoxSizer( wxVERTICAL ); + + m_bitmapUpdateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapUpdateLeft->SetToolTip( _("Number of files that will be updated") ); + + bSizer172->Add( m_bitmapUpdateLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer172->Add( 5, 2, 0, 0, 5 ); + + + bSizer172->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextUpdateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextUpdateLeft->Wrap( -1 ); + m_staticTextUpdateLeft->SetToolTip( _("Number of files that will be updated") ); + + bSizer172->Add( m_staticTextUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizerStatistics->Add( bSizer172, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerStatistics->Add( 5, 5, 0, 0, 5 ); + + wxBoxSizer* bSizer1712; + bSizer1712 = new wxBoxSizer( wxVERTICAL ); + + m_bitmapCreateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); + + bSizer1712->Add( m_bitmapCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer1712->Add( 5, 2, 0, 0, 5 ); + + + bSizer1712->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextCreateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextCreateLeft->Wrap( -1 ); + m_staticTextCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); + + bSizer1712->Add( m_staticTextCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizerStatistics->Add( bSizer1712, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerStatistics->Add( 5, 5, 0, 0, 5 ); + + bSizerData = new wxBoxSizer( wxVERTICAL ); + + m_bitmapData = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapData->SetToolTip( _("Total bytes to copy") ); + + bSizerData->Add( m_bitmapData, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizerData->Add( 5, 2, 0, 0, 5 ); + + + bSizerData->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextData = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextData->Wrap( -1 ); + m_staticTextData->SetToolTip( _("Total bytes to copy") ); + + bSizerData->Add( m_staticTextData, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatistics->Add( bSizerData, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerStatistics->Add( 5, 5, 0, 0, 5 ); + + wxBoxSizer* bSizer178; + bSizer178 = new wxBoxSizer( wxVERTICAL ); + + m_bitmapCreateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapCreateRight->SetToolTip( _("Number of files and folders that will be created") ); + + bSizer178->Add( m_bitmapCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer178->Add( 5, 2, 0, 0, 5 ); + + + bSizer178->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextCreateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextCreateRight->Wrap( -1 ); + m_staticTextCreateRight->SetToolTip( _("Number of files and folders that will be created") ); + + bSizer178->Add( m_staticTextCreateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatistics->Add( bSizer178, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerStatistics->Add( 5, 5, 0, 0, 5 ); + + wxBoxSizer* bSizer177; + bSizer177 = new wxBoxSizer( wxVERTICAL ); + + m_bitmapUpdateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapUpdateRight->SetToolTip( _("Number of files that will be updated") ); + + bSizer177->Add( m_bitmapUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer177->Add( 5, 2, 0, 0, 5 ); + + + bSizer177->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextUpdateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextUpdateRight->Wrap( -1 ); + m_staticTextUpdateRight->SetToolTip( _("Number of files that will be updated") ); + + bSizer177->Add( m_staticTextUpdateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatistics->Add( bSizer177, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerStatistics->Add( 5, 5, 0, 0, 5 ); + + wxBoxSizer* bSizer176; + bSizer176 = new wxBoxSizer( wxVERTICAL ); + + m_bitmapDeleteRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); + + bSizer176->Add( m_bitmapDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer176->Add( 5, 2, 0, 0, 5 ); + + + bSizer176->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextDeleteRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDeleteRight->Wrap( -1 ); + m_staticTextDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); + + bSizer176->Add( m_staticTextDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatistics->Add( bSizer176, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer1801->Add( bSizerStatistics, 0, wxALIGN_CENTER_VERTICAL|wxALL, 4 ); + + + m_panelStatistics->SetSizer( bSizer1801 ); + m_panelStatistics->Layout(); + bSizer1801->Fit( m_panelStatistics ); + bSizerViewFilter->Add( m_panelStatistics, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + m_panelViewFilter->SetSizer( bSizerViewFilter ); + m_panelViewFilter->Layout(); + bSizerViewFilter->Fit( m_panelViewFilter ); + bSizerPanelHolder->Add( m_panelViewFilter, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + this->SetSizer( bSizerPanelHolder ); + this->Layout(); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( MainDialogGenerated::OnClose ) ); + this->Connect( m_menuItemNew->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigNew ) ); + this->Connect( m_menuItemLoad->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ) ); + this->Connect( m_menuItemSave->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ) ); + this->Connect( m_menuItemSaveAs->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigSaveAs ) ); + this->Connect( m_menuItemSaveAsBatch->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnSaveAsBatchJob ) ); + this->Connect( m_menuItem4->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuQuit ) ); + this->Connect( m_menuItemCompare->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnCompare ) ); + this->Connect( m_menuItemCompSettings->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnCmpSettings ) ); + this->Connect( m_menuItemFilter->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigureFilter ) ); + this->Connect( m_menuItemSyncSettings->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnSyncSettings ) ); + this->Connect( m_menuItemSynchronize->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ) ); + this->Connect( m_menuItemOptions->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuOptions ) ); + this->Connect( m_menuItem15->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuFindItem ) ); + this->Connect( m_menuItem51->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuResetLayout ) ); + this->Connect( m_menuItem5->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuExportFileList ) ); + this->Connect( m_menuItemHelp->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnShowHelp ) ); + this->Connect( m_menuItemCheckVersionNow->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersion ) ); + this->Connect( m_menuItemCheckVersionAuto->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersionAutomatically ) ); + this->Connect( m_menuItemAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuAbout ) ); + m_buttonCompare->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCompare ), NULL, this ); + m_bpButtonCmpConfig->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCmpSettings ), NULL, this ); + m_bpButtonCmpConfig->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnCompSettingsContext ), NULL, this ); + m_bpButtonFilter->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigureFilter ), NULL, this ); + m_bpButtonFilter->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnGlobalFilterContext ), NULL, this ); + m_bpButtonSyncConfig->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncSettings ), NULL, this ); + m_bpButtonSyncConfig->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnSyncSettingsContext ), NULL, this ); + m_buttonSync->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ), NULL, this ); + m_bpButtonAddPair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnTopFolderPairAdd ), NULL, this ); + m_bpButtonRemovePair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnTopFolderPairRemove ), NULL, this ); + m_bpButtonSwapSides->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSwapSides ), NULL, this ); + m_bpButtonHideSearch->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnHideSearchPanel ), NULL, this ); + m_textCtrlSearchTxt->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( MainDialogGenerated::OnSearchGridEnter ), NULL, this ); + m_bpButtonNew->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigNew ), NULL, this ); + m_bpButtonOpen->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ), NULL, this ); + m_bpButtonSave->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ), NULL, this ); + m_bpButtonSaveAs->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigSaveAs ), NULL, this ); + m_bpButtonSaveAsBatch->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSaveAsBatchJob ), NULL, this ); + m_listBoxHistory->Connect( wxEVT_KEY_DOWN, wxKeyEventHandler( MainDialogGenerated::OnCfgHistoryKeyEvent ), NULL, this ); + m_listBoxHistory->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistory ), NULL, this ); + m_listBoxHistory->Connect( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistoryDoubleClick ), NULL, this ); + m_listBoxHistory->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnCfgHistoryRightClick ), NULL, this ); + m_bpButtonViewTypeSyncAction->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewType ), NULL, this ); + m_bpButtonShowExcluded->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowExcluded->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDeleteLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDeleteLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowUpdateLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowUpdateLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowCreateLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowCreateLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowLeftOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowLeftOnly->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowLeftNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowLeftNewer->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowEqual->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowEqual->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDoNothing->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDoNothing->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDifferent->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDifferent->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowRightNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowRightNewer->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowRightOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowRightOnly->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowCreateRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowCreateRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowUpdateRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowUpdateRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDeleteRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDeleteRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowConflict->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowConflict->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); } MainDialogGenerated::~MainDialogGenerated() @@ -992,815 +992,815 @@ MainDialogGenerated::~MainDialogGenerated() ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxDefaultSize, wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer7; - bSizer7 = new wxBoxSizer( wxVERTICAL ); - - m_notebook = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - m_panelCompSettingsHolder = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelCompSettingsHolder->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer275; - bSizer275 = new wxBoxSizer( wxVERTICAL ); - - bSizerLocalCompSettings = new wxBoxSizer( wxVERTICAL ); - - m_checkBoxUseLocalCmpOptions = new wxCheckBox( m_panelCompSettingsHolder, wxID_ANY, _("Use local settings:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_checkBoxUseLocalCmpOptions->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizerLocalCompSettings->Add( m_checkBoxUseLocalCmpOptions, 0, wxALL|wxEXPAND, 10 ); - - m_staticline59 = new wxStaticLine( m_panelCompSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizerLocalCompSettings->Add( m_staticline59, 0, wxEXPAND, 5 ); - - - bSizer275->Add( bSizerLocalCompSettings, 0, wxEXPAND, 5 ); - - m_panelComparisonSettings = new wxPanel( m_panelCompSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelComparisonSettings->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer159; - bSizer159 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer178; - bSizer178 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer182; - bSizer182 = new wxBoxSizer( wxVERTICAL ); - - m_staticText91 = new wxStaticText( m_panelComparisonSettings, wxID_ANY, _("Select a variant:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText91->Wrap( -1 ); - bSizer182->Add( m_staticText91, 0, wxALL, 5 ); - - wxFlexGridSizer* fgSizer16; - fgSizer16 = new wxFlexGridSizer( 2, 2, 5, 5 ); - fgSizer16->SetFlexibleDirection( wxBOTH ); - fgSizer16->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - - m_bitmapByTime = new wxStaticBitmap( m_panelComparisonSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapByTime->SetToolTip( _("Identify equal files by comparing modification time and size.") ); - - fgSizer16->Add( m_bitmapByTime, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_toggleBtnTimeSize = new wxToggleButton( m_panelComparisonSettings, wxID_ANY, _("File time and size"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_toggleBtnTimeSize->SetValue( true ); - m_toggleBtnTimeSize->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - fgSizer16->Add( m_toggleBtnTimeSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_bitmapByContent = new wxStaticBitmap( m_panelComparisonSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapByContent->SetToolTip( _("Identify equal files by comparing the file content.") ); - - fgSizer16->Add( m_bitmapByContent, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_toggleBtnContent = new wxToggleButton( m_panelComparisonSettings, wxID_ANY, _("File content"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_toggleBtnContent->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - fgSizer16->Add( m_toggleBtnContent, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer182->Add( fgSizer16, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer178->Add( bSizer182, 0, wxALL, 5 ); - - m_staticline42 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer178->Add( m_staticline42, 0, wxEXPAND, 5 ); - - m_textCtrlCompVarDescription = new wxTextCtrl( m_panelComparisonSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); - bSizer178->Add( m_textCtrlCompVarDescription, 1, wxEXPAND|wxLEFT, 5 ); - - - bSizer159->Add( bSizer178, 0, wxEXPAND, 5 ); - - m_staticline33 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer159->Add( m_staticline33, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer1734; - bSizer1734 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer1733; - bSizer1733 = new wxBoxSizer( wxVERTICAL ); - - m_checkBoxTimeShift = new wxCheckBox( m_panelComparisonSettings, wxID_ANY, _("Ignore time shift (in hours)"), wxDefaultPosition, wxDefaultSize, 0 ); - m_checkBoxTimeShift->SetToolTip( _("Consider file times with specified offset as equal") ); - - bSizer1733->Add( m_checkBoxTimeShift, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_spinCtrlTimeShift = new wxSpinCtrl( m_panelComparisonSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70,-1 ), wxSP_ARROW_KEYS|wxSP_WRAP, 1, 26, 0 ); - m_spinCtrlTimeShift->SetToolTip( _("Consider file times with specified offset as equal") ); - - bSizer1733->Add( m_spinCtrlTimeShift, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - m_hyperlink241 = new wxHyperlinkCtrl( m_panelComparisonSettings, wxID_ANY, _("Handle daylight saving time"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - bSizer1733->Add( m_hyperlink241, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer1734->Add( bSizer1733, 0, wxALL, 5 ); - - m_staticline44 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer1734->Add( m_staticline44, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer1721; - bSizer1721 = new wxBoxSizer( wxVERTICAL ); - - m_checkBoxSymlinksInclude = new wxCheckBox( m_panelComparisonSettings, wxID_ANY, _("Include symbolic links:"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer1721->Add( m_checkBoxSymlinksInclude, 0, wxALL, 5 ); - - wxBoxSizer* bSizer176; - bSizer176 = new wxBoxSizer( wxVERTICAL ); - - m_radioBtnSymlinksFollow = new wxRadioButton( m_panelComparisonSettings, wxID_ANY, _("Follow"), wxDefaultPosition, wxDefaultSize, 0 ); - m_radioBtnSymlinksFollow->SetValue( true ); - bSizer176->Add( m_radioBtnSymlinksFollow, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - m_radioBtnSymlinksDirect = new wxRadioButton( m_panelComparisonSettings, wxID_ANY, _("Direct"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer176->Add( m_radioBtnSymlinksDirect, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer1721->Add( bSizer176, 0, wxEXPAND|wxLEFT, 18 ); - - m_hyperlink24 = new wxHyperlinkCtrl( m_panelComparisonSettings, wxID_ANY, _("More information"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - bSizer1721->Add( m_hyperlink24, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer1734->Add( bSizer1721, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_staticline441 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer1734->Add( m_staticline441, 0, wxEXPAND, 5 ); - - - bSizer159->Add( bSizer1734, 0, 0, 5 ); - - m_staticline331 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer159->Add( m_staticline331, 0, wxEXPAND, 5 ); - - - m_panelComparisonSettings->SetSizer( bSizer159 ); - m_panelComparisonSettings->Layout(); - bSizer159->Fit( m_panelComparisonSettings ); - bSizer275->Add( m_panelComparisonSettings, 0, wxEXPAND, 5 ); - - - m_panelCompSettingsHolder->SetSizer( bSizer275 ); - m_panelCompSettingsHolder->Layout(); - bSizer275->Fit( m_panelCompSettingsHolder ); - m_notebook->AddPage( m_panelCompSettingsHolder, _("dummy"), true ); - m_panelFilterSettingsHolder = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelFilterSettingsHolder->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer278; - bSizer278 = new wxBoxSizer( wxVERTICAL ); - - bSizerLocalFilterSettings = new wxBoxSizer( wxVERTICAL ); - - m_staticText144 = new wxStaticText( m_panelFilterSettingsHolder, wxID_ANY, _("Local settings:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText144->Wrap( -1 ); - bSizerLocalFilterSettings->Add( m_staticText144, 0, wxALL, 10 ); - - m_staticline61 = new wxStaticLine( m_panelFilterSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizerLocalFilterSettings->Add( m_staticline61, 0, wxEXPAND, 5 ); - - - bSizer278->Add( bSizerLocalFilterSettings, 0, wxEXPAND, 5 ); - - m_panelFilterSettings = new wxPanel( m_panelFilterSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelFilterSettings->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer1591; - bSizer1591 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer166; - bSizer166 = new wxBoxSizer( wxVERTICAL ); - - - bSizer166->Add( 0, 10, 0, 0, 5 ); - - wxBoxSizer* bSizer1661; - bSizer1661 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapInclude = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); - bSizer1661->Add( m_bitmapInclude, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); - - wxBoxSizer* bSizer1731; - bSizer1731 = new wxBoxSizer( wxVERTICAL ); - - m_staticText78 = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("Include:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText78->Wrap( -1 ); - bSizer1731->Add( m_staticText78, 0, 0, 5 ); - - m_textCtrlInclude = new wxTextCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE ); - m_textCtrlInclude->SetMinSize( wxSize( 280,-1 ) ); - - bSizer1731->Add( m_textCtrlInclude, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxTOP, 5 ); - - - bSizer1661->Add( bSizer1731, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer166->Add( bSizer1661, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxLEFT, 5 ); - - m_staticline22 = new wxStaticLine( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer166->Add( m_staticline22, 0, wxEXPAND, 5 ); - - - bSizer166->Add( 0, 10, 0, 0, 5 ); - - wxBoxSizer* bSizer1651; - bSizer1651 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapExclude = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); - bSizer1651->Add( m_bitmapExclude, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - wxBoxSizer* bSizer1742; - bSizer1742 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer189; - bSizer189 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText77 = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("Exclude:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText77->Wrap( -1 ); - bSizer189->Add( m_staticText77, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer189->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_hyperlink171 = new wxHyperlinkCtrl( m_panelFilterSettings, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - bSizer189->Add( m_hyperlink171, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - - - bSizer1742->Add( bSizer189, 0, wxEXPAND, 5 ); - - m_textCtrlExclude = new wxTextCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE ); - bSizer1742->Add( m_textCtrlExclude, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxTOP, 5 ); - - - bSizer1651->Add( bSizer1742, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer166->Add( bSizer1651, 2, wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxLEFT, 5 ); - - - bSizer1591->Add( bSizer166, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_staticline24 = new wxStaticLine( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer1591->Add( m_staticline24, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - wxBoxSizer* bSizer160; - bSizer160 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer167; - bSizer167 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapFilterDate = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 34,34 ), 0 ); - bSizer167->Add( m_bitmapFilterDate, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - wxBoxSizer* bSizer165; - bSizer165 = new wxBoxSizer( wxVERTICAL ); - - m_staticText79 = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("Time span:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText79->Wrap( -1 ); - bSizer165->Add( m_staticText79, 0, wxBOTTOM, 5 ); - - m_spinCtrlTimespan = new wxSpinCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 ); - bSizer165->Add( m_spinCtrlTimespan, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - wxArrayString m_choiceUnitTimespanChoices; - m_choiceUnitTimespan = new wxChoice( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceUnitTimespanChoices, 0 ); - m_choiceUnitTimespan->SetSelection( 0 ); - bSizer165->Add( m_choiceUnitTimespan, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer167->Add( bSizer165, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer160->Add( bSizer167, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 5 ); - - m_staticline23 = new wxStaticLine( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer160->Add( m_staticline23, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer168; - bSizer168 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapFilterSize = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), 0 ); - bSizer168->Add( m_bitmapFilterSize, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); - - wxBoxSizer* bSizer158; - bSizer158 = new wxBoxSizer( wxVERTICAL ); - - m_staticText80 = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("File size:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText80->Wrap( -1 ); - bSizer158->Add( m_staticText80, 0, wxBOTTOM, 5 ); - - wxBoxSizer* bSizer162; - bSizer162 = new wxBoxSizer( wxVERTICAL ); - - m_staticText101 = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("Minimum:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText101->Wrap( -1 ); - bSizer162->Add( m_staticText101, 0, wxBOTTOM, 2 ); - - m_spinCtrlMinSize = new wxSpinCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 ); - bSizer162->Add( m_spinCtrlMinSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - wxArrayString m_choiceUnitMinSizeChoices; - m_choiceUnitMinSize = new wxChoice( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceUnitMinSizeChoices, 0 ); - m_choiceUnitMinSize->SetSelection( 0 ); - bSizer162->Add( m_choiceUnitMinSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer158->Add( bSizer162, 0, wxBOTTOM|wxEXPAND, 5 ); - - wxBoxSizer* bSizer163; - bSizer163 = new wxBoxSizer( wxVERTICAL ); - - m_staticText102 = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("Maximum:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText102->Wrap( -1 ); - bSizer163->Add( m_staticText102, 0, wxBOTTOM, 2 ); - - m_spinCtrlMaxSize = new wxSpinCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 ); - bSizer163->Add( m_spinCtrlMaxSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - wxArrayString m_choiceUnitMaxSizeChoices; - m_choiceUnitMaxSize = new wxChoice( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceUnitMaxSizeChoices, 0 ); - m_choiceUnitMaxSize->SetSelection( 0 ); - bSizer163->Add( m_choiceUnitMaxSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer158->Add( bSizer163, 0, wxEXPAND, 5 ); - - - bSizer168->Add( bSizer158, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer160->Add( bSizer168, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 5 ); - - - bSizer1591->Add( bSizer160, 0, wxEXPAND, 5 ); - - - m_panelFilterSettings->SetSizer( bSizer1591 ); - m_panelFilterSettings->Layout(); - bSizer1591->Fit( m_panelFilterSettings ); - bSizer278->Add( m_panelFilterSettings, 1, wxEXPAND, 5 ); - - m_staticline62 = new wxStaticLine( m_panelFilterSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer278->Add( m_staticline62, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer280; - bSizer280 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText44 = new wxStaticText( m_panelFilterSettingsHolder, wxID_ANY, _("Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair."), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_staticText44->Wrap( 590 ); - bSizer280->Add( m_staticText44, 0, wxALIGN_CENTER_VERTICAL|wxALL, 10 ); - - - bSizer280->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticline46 = new wxStaticLine( m_panelFilterSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer280->Add( m_staticline46, 0, wxEXPAND, 5 ); - - m_buttonClear = new wxButton( m_panelFilterSettingsHolder, wxID_DEFAULT, _("C&lear"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer280->Add( m_buttonClear, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); - - - bSizer278->Add( bSizer280, 0, wxEXPAND, 5 ); - - - m_panelFilterSettingsHolder->SetSizer( bSizer278 ); - m_panelFilterSettingsHolder->Layout(); - bSizer278->Fit( m_panelFilterSettingsHolder ); - m_notebook->AddPage( m_panelFilterSettingsHolder, _("dummy"), false ); - m_panelSyncSettingsHolder = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelSyncSettingsHolder->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer276; - bSizer276 = new wxBoxSizer( wxVERTICAL ); - - bSizerLocalSyncSettings = new wxBoxSizer( wxVERTICAL ); - - m_checkBoxUseLocalSyncOptions = new wxCheckBox( m_panelSyncSettingsHolder, wxID_ANY, _("Use local settings:"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizerLocalSyncSettings->Add( m_checkBoxUseLocalSyncOptions, 0, wxALL|wxEXPAND, 10 ); - - m_staticline60 = new wxStaticLine( m_panelSyncSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizerLocalSyncSettings->Add( m_staticline60, 0, wxEXPAND, 5 ); - - - bSizer276->Add( bSizerLocalSyncSettings, 0, wxEXPAND, 5 ); - - m_panelSyncSettings = new wxPanel( m_panelSyncSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelSyncSettings->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer232; - bSizer232 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer237; - bSizer237 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer235; - bSizer235 = new wxBoxSizer( wxVERTICAL ); - - m_staticText86 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Select a variant:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText86->Wrap( -1 ); - bSizer235->Add( m_staticText86, 0, wxALL, 5 ); - - wxBoxSizer* bSizer236; - bSizer236 = new wxBoxSizer( wxVERTICAL ); - - m_toggleBtnTwoWay = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_toggleBtnTwoWay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer236->Add( m_toggleBtnTwoWay, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxBOTTOM, 5 ); - - m_toggleBtnMirror = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_toggleBtnMirror->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer236->Add( m_toggleBtnMirror, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxBOTTOM, 5 ); - - m_toggleBtnUpdate = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_toggleBtnUpdate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer236->Add( m_toggleBtnUpdate, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxBOTTOM, 5 ); - - m_toggleBtnCustom = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_toggleBtnCustom->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer236->Add( m_toggleBtnCustom, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer235->Add( bSizer236, 0, wxRIGHT|wxLEFT, 5 ); - - m_checkBoxDetectMove = new wxCheckBox( m_panelSyncSettings, wxID_ANY, _("Detect moved files"), wxDefaultPosition, wxDefaultSize, 0 ); - m_checkBoxDetectMove->SetValue(true); - m_checkBoxDetectMove->SetToolTip( _("- Detection active after initial sync\n- Requires and creates database files\n- Not supported by all file systems") ); - - bSizer235->Add( m_checkBoxDetectMove, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALL, 5 ); - - - bSizer237->Add( bSizer235, 0, wxALL, 5 ); - - m_staticline53 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer237->Add( m_staticline53, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer238; - bSizer238 = new wxBoxSizer( wxVERTICAL ); - - m_textCtrlSyncVarDescription = new wxTextCtrl( m_panelSyncSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); - bSizer238->Add( m_textCtrlSyncVarDescription, 1, wxEXPAND|wxLEFT, 5 ); - - m_staticline43 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer238->Add( m_staticline43, 0, wxEXPAND, 5 ); - - bSizerSyncConfig = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer173; - bSizer173 = new wxBoxSizer( wxVERTICAL ); - - - bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticText119 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Category"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText119->Wrap( -1 ); - bSizer173->Add( m_staticText119, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); - - - bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticText120 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Action"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText120->Wrap( -1 ); - bSizer173->Add( m_staticText120, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); - - - bSizerSyncConfig->Add( bSizer173, 0, wxEXPAND|wxRIGHT, 5 ); - - fgSizerSyncDirections = new wxFlexGridSizer( 2, 0, 5, 5 ); - fgSizerSyncDirections->SetFlexibleDirection( wxBOTH ); - fgSizerSyncDirections->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - - m_bitmapLeftOnly = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); - m_bitmapLeftOnly->SetToolTip( _("Item exists on left side only") ); - - fgSizerSyncDirections->Add( m_bitmapLeftOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bitmapLeftNewer = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); - m_bitmapLeftNewer->SetToolTip( _("Left side is newer") ); - - fgSizerSyncDirections->Add( m_bitmapLeftNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bitmapDifferent = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); - m_bitmapDifferent->SetToolTip( _("Items have different content") ); - - fgSizerSyncDirections->Add( m_bitmapDifferent, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bitmapConflict = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); - m_bitmapConflict->SetToolTip( _("Conflict/item cannot be categorized") ); - - fgSizerSyncDirections->Add( m_bitmapConflict, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bitmapRightNewer = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); - m_bitmapRightNewer->SetToolTip( _("Right side is newer") ); - - fgSizerSyncDirections->Add( m_bitmapRightNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bitmapRightOnly = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); - m_bitmapRightOnly->SetToolTip( _("Item exists on right side only") ); - - fgSizerSyncDirections->Add( m_bitmapRightOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonLeftOnly = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); - fgSizerSyncDirections->Add( m_bpButtonLeftOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonLeftNewer = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); - fgSizerSyncDirections->Add( m_bpButtonLeftNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonDifferent = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); - fgSizerSyncDirections->Add( m_bpButtonDifferent, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonConflict = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); - fgSizerSyncDirections->Add( m_bpButtonConflict, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonRightNewer = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); - fgSizerSyncDirections->Add( m_bpButtonRightNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonRightOnly = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); - fgSizerSyncDirections->Add( m_bpButtonRightOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerSyncConfig->Add( fgSizerSyncDirections, 0, 0, 5 ); - - m_bitmapDatabase = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_bitmapDatabase->SetToolTip( _("Detect synchronization directions with the help of database files") ); - - bSizerSyncConfig->Add( m_bitmapDatabase, 0, wxALIGN_CENTER_HORIZONTAL|wxLEFT|wxALIGN_CENTER_VERTICAL, 10 ); - - wxBoxSizer* bSizerKeepVerticalHeightWhenSyncDirsNotShown; - bSizerKeepVerticalHeightWhenSyncDirsNotShown = new wxBoxSizer( wxVERTICAL ); - - - bSizerKeepVerticalHeightWhenSyncDirsNotShown->Add( 0, 45, 0, 0, 5 ); - - - bSizerKeepVerticalHeightWhenSyncDirsNotShown->Add( 0, 5, 1, 0, 5 ); - - - bSizerKeepVerticalHeightWhenSyncDirsNotShown->Add( 0, 46, 0, 0, 5 ); - - - bSizerSyncConfig->Add( bSizerKeepVerticalHeightWhenSyncDirsNotShown, 0, 0, 5 ); - - - bSizer238->Add( bSizerSyncConfig, 0, wxALL, 10 ); - - - bSizer237->Add( bSizer238, 1, wxEXPAND, 5 ); - - - bSizer232->Add( bSizer237, 0, wxEXPAND, 5 ); - - m_staticline54 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer232->Add( m_staticline54, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer184; - bSizer184 = new wxBoxSizer( wxVERTICAL ); - - m_staticText87 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Delete files:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText87->Wrap( -1 ); - bSizer184->Add( m_staticText87, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM, 5 ); - - wxBoxSizer* bSizer180; - bSizer180 = new wxBoxSizer( wxHORIZONTAL ); - - m_toggleBtnPermanent = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("&Permanent"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnPermanent->SetToolTip( _("Delete or overwrite files permanently") ); - - bSizer180->Add( m_toggleBtnPermanent, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxRIGHT, 5 ); - - m_toggleBtnRecycler = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("&Recycle bin"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnRecycler->SetToolTip( _("Back up deleted and overwritten files in the recycle bin") ); - - bSizer180->Add( m_toggleBtnRecycler, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxRIGHT, 5 ); - - m_toggleBtnVersioning = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("&Versioning"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnVersioning->SetToolTip( _("Move files to a user-defined folder") ); - - bSizer180->Add( m_toggleBtnVersioning, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer184->Add( bSizer180, 0, 0, 5 ); - - bSizerVersioning = new wxBoxSizer( wxHORIZONTAL ); - - m_panelVersioning = new wxPanel( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelVersioning->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer191; - bSizer191 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer156; - bSizer156 = new wxBoxSizer( wxHORIZONTAL ); - - m_versioningFolder = new FolderHistoryBox( m_panelVersioning, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer156->Add( m_versioningFolder, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectDirVersioning = new wxButton( m_panelVersioning, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectDirVersioning->SetToolTip( _("Select a folder") ); - - bSizer156->Add( m_buttonSelectDirVersioning, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer191->Add( bSizer156, 0, wxEXPAND|wxBOTTOM, 5 ); - - bSizer192 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText93 = new wxStaticText( m_panelVersioning, wxID_ANY, _("Naming convention:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText93->Wrap( -1 ); - bSizer192->Add( m_staticText93, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - wxArrayString m_choiceVersioningStyleChoices; - m_choiceVersioningStyle = new wxChoice( m_panelVersioning, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceVersioningStyleChoices, 0 ); - m_choiceVersioningStyle->SetSelection( 0 ); - bSizer192->Add( m_choiceVersioningStyle, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_staticTextNamingCvtPart1 = new wxStaticText( m_panelVersioning, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextNamingCvtPart1->Wrap( -1 ); - m_staticTextNamingCvtPart1->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - - bSizer192->Add( m_staticTextNamingCvtPart1, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextNamingCvtPart2Bold = new wxStaticText( m_panelVersioning, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextNamingCvtPart2Bold->Wrap( -1 ); - m_staticTextNamingCvtPart2Bold->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - m_staticTextNamingCvtPart2Bold->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - - bSizer192->Add( m_staticTextNamingCvtPart2Bold, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextNamingCvtPart3 = new wxStaticText( m_panelVersioning, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextNamingCvtPart3->Wrap( -1 ); - m_staticTextNamingCvtPart3->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - - bSizer192->Add( m_staticTextNamingCvtPart3, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer192->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_hyperlink17 = new wxHyperlinkCtrl( m_panelVersioning, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - bSizer192->Add( m_hyperlink17, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); - - - bSizer191->Add( bSizer192, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - m_panelVersioning->SetSizer( bSizer191 ); - m_panelVersioning->Layout(); - bSizer191->Fit( m_panelVersioning ); - bSizerVersioning->Add( m_panelVersioning, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer184->Add( bSizerVersioning, 0, wxTOP|wxEXPAND, 5 ); - - - bSizer232->Add( bSizer184, 0, wxALL|wxEXPAND, 10 ); - - bSizerMiscConfig = new wxBoxSizer( wxVERTICAL ); - - m_staticline582 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizerMiscConfig->Add( m_staticline582, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer1732; - bSizer1732 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer174; - bSizer174 = new wxBoxSizer( wxVERTICAL ); - - m_staticText88 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Handle errors:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText88->Wrap( -1 ); - bSizer174->Add( m_staticText88, 0, wxBOTTOM, 5 ); - - wxBoxSizer* bSizer175; - bSizer175 = new wxBoxSizer( wxHORIZONTAL ); - - m_toggleBtnErrorIgnore = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("&Ignore"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnErrorIgnore->SetToolTip( _("Hide all error and warning messages") ); - - bSizer175->Add( m_toggleBtnErrorIgnore, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_toggleBtnErrorPopup = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("&Pop-up"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnErrorPopup->SetToolTip( _("Show pop-up on errors or warnings") ); - - bSizer175->Add( m_toggleBtnErrorPopup, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer174->Add( bSizer175, 0, 0, 5 ); - - - bSizer1732->Add( bSizer174, 0, wxALL, 10 ); - - m_staticline57 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer1732->Add( m_staticline57, 0, wxEXPAND, 5 ); - - bSizerOnCompletion = new wxBoxSizer( wxVERTICAL ); - - m_staticText89 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("On completion:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText89->Wrap( -1 ); - bSizerOnCompletion->Add( m_staticText89, 0, wxBOTTOM, 5 ); - - m_comboBoxOnCompletion = new OnCompletionBox( m_panelSyncSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizerOnCompletion->Add( m_comboBoxOnCompletion, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer1732->Add( bSizerOnCompletion, 1, wxALL, 10 ); - - - bSizerMiscConfig->Add( bSizer1732, 1, wxEXPAND, 5 ); - - - bSizer232->Add( bSizerMiscConfig, 1, wxEXPAND, 5 ); - - - m_panelSyncSettings->SetSizer( bSizer232 ); - m_panelSyncSettings->Layout(); - bSizer232->Fit( m_panelSyncSettings ); - bSizer276->Add( m_panelSyncSettings, 1, wxEXPAND, 5 ); - - - m_panelSyncSettingsHolder->SetSizer( bSizer276 ); - m_panelSyncSettingsHolder->Layout(); - bSizer276->Fit( m_panelSyncSettingsHolder ); - m_notebook->AddPage( m_panelSyncSettingsHolder, _("dummy"), false ); - - bSizer7->Add( m_notebook, 1, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonOkay->SetDefault(); - m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer7->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); - - - this->SetSizer( bSizer7 ); - this->Layout(); - bSizer7->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( ConfigDlgGenerated::OnClose ) ); - m_checkBoxUseLocalCmpOptions->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleLocalCompSettings ), NULL, this ); - m_toggleBtnTimeSize->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( ConfigDlgGenerated::OnTimeSizeDouble ), NULL, this ); - m_toggleBtnTimeSize->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnTimeSize ), NULL, this ); - m_toggleBtnContent->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( ConfigDlgGenerated::OnContentDouble ), NULL, this ); - m_toggleBtnContent->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnContent ), NULL, this ); - m_checkBoxTimeShift->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeCompOption ), NULL, this ); - m_hyperlink241->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpTimeShift ), NULL, this ); - m_checkBoxSymlinksInclude->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeCompOption ), NULL, this ); - m_hyperlink24->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpComparisonSettings ), NULL, this ); - m_textCtrlInclude->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this ); - m_hyperlink171->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpShowExamples ), NULL, this ); - m_textCtrlExclude->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this ); - m_choiceUnitTimespan->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this ); - m_choiceUnitMinSize->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this ); - m_choiceUnitMaxSize->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this ); - m_buttonClear->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnFilterReset ), NULL, this ); - m_checkBoxUseLocalSyncOptions->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleLocalSyncSettings ), NULL, this ); - m_toggleBtnTwoWay->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( ConfigDlgGenerated::OnSyncTwoWayDouble ), NULL, this ); - m_toggleBtnTwoWay->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnSyncTwoWay ), NULL, this ); - m_toggleBtnMirror->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( ConfigDlgGenerated::OnSyncMirrorDouble ), NULL, this ); - m_toggleBtnMirror->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnSyncMirror ), NULL, this ); - m_toggleBtnUpdate->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( ConfigDlgGenerated::OnSyncUpdateDouble ), NULL, this ); - m_toggleBtnUpdate->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnSyncUpdate ), NULL, this ); - m_toggleBtnCustom->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( ConfigDlgGenerated::OnSyncCustomDouble ), NULL, this ); - m_toggleBtnCustom->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnSyncCustom ), NULL, this ); - m_checkBoxDetectMove->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleDetectMovedFiles ), NULL, this ); - m_bpButtonLeftOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnExLeftSideOnly ), NULL, this ); - m_bpButtonLeftNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnLeftNewer ), NULL, this ); - m_bpButtonDifferent->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnDifferent ), NULL, this ); - m_bpButtonConflict->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnConflict ), NULL, this ); - m_bpButtonRightNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnRightNewer ), NULL, this ); - m_bpButtonRightOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnExRightSideOnly ), NULL, this ); - m_toggleBtnPermanent->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnDeletionPermanent ), NULL, this ); - m_toggleBtnRecycler->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnDeletionRecycler ), NULL, this ); - m_toggleBtnVersioning->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnDeletionVersioning ), NULL, this ); - m_choiceVersioningStyle->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeSyncOption ), NULL, this ); - m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpVersioning ), NULL, this ); - m_toggleBtnErrorIgnore->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnErrorIgnore ), NULL, this ); - m_toggleBtnErrorPopup->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnErrorPopup ), NULL, this ); - m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnOkay ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnCancel ), NULL, this ); + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer7; + bSizer7 = new wxBoxSizer( wxVERTICAL ); + + m_notebook = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_panelCompSettingsHolder = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelCompSettingsHolder->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer275; + bSizer275 = new wxBoxSizer( wxVERTICAL ); + + bSizerLocalCompSettings = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxUseLocalCmpOptions = new wxCheckBox( m_panelCompSettingsHolder, wxID_ANY, _("Use local settings:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxUseLocalCmpOptions->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizerLocalCompSettings->Add( m_checkBoxUseLocalCmpOptions, 0, wxALL|wxEXPAND, 10 ); + + m_staticline59 = new wxStaticLine( m_panelCompSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerLocalCompSettings->Add( m_staticline59, 0, wxEXPAND, 5 ); + + + bSizer275->Add( bSizerLocalCompSettings, 0, wxEXPAND, 5 ); + + m_panelComparisonSettings = new wxPanel( m_panelCompSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelComparisonSettings->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer159; + bSizer159 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer178; + bSizer178 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer182; + bSizer182 = new wxBoxSizer( wxVERTICAL ); + + m_staticText91 = new wxStaticText( m_panelComparisonSettings, wxID_ANY, _("Select a variant:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText91->Wrap( -1 ); + bSizer182->Add( m_staticText91, 0, wxALL, 5 ); + + wxFlexGridSizer* fgSizer16; + fgSizer16 = new wxFlexGridSizer( 2, 2, 5, 5 ); + fgSizer16->SetFlexibleDirection( wxBOTH ); + fgSizer16->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_bitmapByTime = new wxStaticBitmap( m_panelComparisonSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapByTime->SetToolTip( _("Identify equal files by comparing modification time and size.") ); + + fgSizer16->Add( m_bitmapByTime, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_toggleBtnTimeSize = new wxToggleButton( m_panelComparisonSettings, wxID_ANY, _("File time and size"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_toggleBtnTimeSize->SetValue( true ); + m_toggleBtnTimeSize->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + fgSizer16->Add( m_toggleBtnTimeSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_bitmapByContent = new wxStaticBitmap( m_panelComparisonSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapByContent->SetToolTip( _("Identify equal files by comparing the file content.") ); + + fgSizer16->Add( m_bitmapByContent, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_toggleBtnContent = new wxToggleButton( m_panelComparisonSettings, wxID_ANY, _("File content"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_toggleBtnContent->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + fgSizer16->Add( m_toggleBtnContent, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer182->Add( fgSizer16, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer178->Add( bSizer182, 0, wxALL, 5 ); + + m_staticline42 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer178->Add( m_staticline42, 0, wxEXPAND, 5 ); + + m_textCtrlCompVarDescription = new wxTextCtrl( m_panelComparisonSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); + bSizer178->Add( m_textCtrlCompVarDescription, 1, wxEXPAND|wxLEFT, 5 ); + + + bSizer159->Add( bSizer178, 0, wxEXPAND, 5 ); + + m_staticline33 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer159->Add( m_staticline33, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer1734; + bSizer1734 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer1733; + bSizer1733 = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxTimeShift = new wxCheckBox( m_panelComparisonSettings, wxID_ANY, _("Ignore time shift (in hours)"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxTimeShift->SetToolTip( _("Consider file times with specified offset as equal") ); + + bSizer1733->Add( m_checkBoxTimeShift, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_spinCtrlTimeShift = new wxSpinCtrl( m_panelComparisonSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70,-1 ), wxSP_ARROW_KEYS|wxSP_WRAP, 1, 26, 0 ); + m_spinCtrlTimeShift->SetToolTip( _("Consider file times with specified offset as equal") ); + + bSizer1733->Add( m_spinCtrlTimeShift, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_hyperlink241 = new wxHyperlinkCtrl( m_panelComparisonSettings, wxID_ANY, _("Handle daylight saving time"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + bSizer1733->Add( m_hyperlink241, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer1734->Add( bSizer1733, 0, wxALL, 5 ); + + m_staticline44 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer1734->Add( m_staticline44, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer1721; + bSizer1721 = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxSymlinksInclude = new wxCheckBox( m_panelComparisonSettings, wxID_ANY, _("Include symbolic links:"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer1721->Add( m_checkBoxSymlinksInclude, 0, wxALL, 5 ); + + wxBoxSizer* bSizer176; + bSizer176 = new wxBoxSizer( wxVERTICAL ); + + m_radioBtnSymlinksFollow = new wxRadioButton( m_panelComparisonSettings, wxID_ANY, _("Follow"), wxDefaultPosition, wxDefaultSize, 0 ); + m_radioBtnSymlinksFollow->SetValue( true ); + bSizer176->Add( m_radioBtnSymlinksFollow, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_radioBtnSymlinksDirect = new wxRadioButton( m_panelComparisonSettings, wxID_ANY, _("Direct"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer176->Add( m_radioBtnSymlinksDirect, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer1721->Add( bSizer176, 0, wxEXPAND|wxLEFT, 18 ); + + m_hyperlink24 = new wxHyperlinkCtrl( m_panelComparisonSettings, wxID_ANY, _("More information"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + bSizer1721->Add( m_hyperlink24, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer1734->Add( bSizer1721, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_staticline441 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer1734->Add( m_staticline441, 0, wxEXPAND, 5 ); + + + bSizer159->Add( bSizer1734, 0, 0, 5 ); + + m_staticline331 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer159->Add( m_staticline331, 0, wxEXPAND, 5 ); + + + m_panelComparisonSettings->SetSizer( bSizer159 ); + m_panelComparisonSettings->Layout(); + bSizer159->Fit( m_panelComparisonSettings ); + bSizer275->Add( m_panelComparisonSettings, 0, wxEXPAND, 5 ); + + + m_panelCompSettingsHolder->SetSizer( bSizer275 ); + m_panelCompSettingsHolder->Layout(); + bSizer275->Fit( m_panelCompSettingsHolder ); + m_notebook->AddPage( m_panelCompSettingsHolder, _("dummy"), true ); + m_panelFilterSettingsHolder = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelFilterSettingsHolder->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer278; + bSizer278 = new wxBoxSizer( wxVERTICAL ); + + bSizerLocalFilterSettings = new wxBoxSizer( wxVERTICAL ); + + m_staticText144 = new wxStaticText( m_panelFilterSettingsHolder, wxID_ANY, _("Local settings:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText144->Wrap( -1 ); + bSizerLocalFilterSettings->Add( m_staticText144, 0, wxALL, 10 ); + + m_staticline61 = new wxStaticLine( m_panelFilterSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerLocalFilterSettings->Add( m_staticline61, 0, wxEXPAND, 5 ); + + + bSizer278->Add( bSizerLocalFilterSettings, 0, wxEXPAND, 5 ); + + m_panelFilterSettings = new wxPanel( m_panelFilterSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelFilterSettings->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer1591; + bSizer1591 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer166; + bSizer166 = new wxBoxSizer( wxVERTICAL ); + + + bSizer166->Add( 0, 10, 0, 0, 5 ); + + wxBoxSizer* bSizer1661; + bSizer1661 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapInclude = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); + bSizer1661->Add( m_bitmapInclude, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); + + wxBoxSizer* bSizer1731; + bSizer1731 = new wxBoxSizer( wxVERTICAL ); + + m_staticText78 = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("Include:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText78->Wrap( -1 ); + bSizer1731->Add( m_staticText78, 0, 0, 5 ); + + m_textCtrlInclude = new wxTextCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE ); + m_textCtrlInclude->SetMinSize( wxSize( 280,-1 ) ); + + bSizer1731->Add( m_textCtrlInclude, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxTOP, 5 ); + + + bSizer1661->Add( bSizer1731, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer166->Add( bSizer1661, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxLEFT, 5 ); + + m_staticline22 = new wxStaticLine( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer166->Add( m_staticline22, 0, wxEXPAND, 5 ); + + + bSizer166->Add( 0, 10, 0, 0, 5 ); + + wxBoxSizer* bSizer1651; + bSizer1651 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapExclude = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); + bSizer1651->Add( m_bitmapExclude, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + wxBoxSizer* bSizer1742; + bSizer1742 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer189; + bSizer189 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText77 = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("Exclude:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText77->Wrap( -1 ); + bSizer189->Add( m_staticText77, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer189->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_hyperlink171 = new wxHyperlinkCtrl( m_panelFilterSettings, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + bSizer189->Add( m_hyperlink171, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + + bSizer1742->Add( bSizer189, 0, wxEXPAND, 5 ); + + m_textCtrlExclude = new wxTextCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE ); + bSizer1742->Add( m_textCtrlExclude, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxTOP, 5 ); + + + bSizer1651->Add( bSizer1742, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer166->Add( bSizer1651, 2, wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxLEFT, 5 ); + + + bSizer1591->Add( bSizer166, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_staticline24 = new wxStaticLine( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer1591->Add( m_staticline24, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + wxBoxSizer* bSizer160; + bSizer160 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer167; + bSizer167 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapFilterDate = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 34,34 ), 0 ); + bSizer167->Add( m_bitmapFilterDate, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + wxBoxSizer* bSizer165; + bSizer165 = new wxBoxSizer( wxVERTICAL ); + + m_staticText79 = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("Time span:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText79->Wrap( -1 ); + bSizer165->Add( m_staticText79, 0, wxBOTTOM, 5 ); + + m_spinCtrlTimespan = new wxSpinCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 ); + bSizer165->Add( m_spinCtrlTimespan, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + wxArrayString m_choiceUnitTimespanChoices; + m_choiceUnitTimespan = new wxChoice( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceUnitTimespanChoices, 0 ); + m_choiceUnitTimespan->SetSelection( 0 ); + bSizer165->Add( m_choiceUnitTimespan, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer167->Add( bSizer165, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer160->Add( bSizer167, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 5 ); + + m_staticline23 = new wxStaticLine( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer160->Add( m_staticline23, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer168; + bSizer168 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapFilterSize = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), 0 ); + bSizer168->Add( m_bitmapFilterSize, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); + + wxBoxSizer* bSizer158; + bSizer158 = new wxBoxSizer( wxVERTICAL ); + + m_staticText80 = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("File size:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText80->Wrap( -1 ); + bSizer158->Add( m_staticText80, 0, wxBOTTOM, 5 ); + + wxBoxSizer* bSizer162; + bSizer162 = new wxBoxSizer( wxVERTICAL ); + + m_staticText101 = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("Minimum:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText101->Wrap( -1 ); + bSizer162->Add( m_staticText101, 0, wxBOTTOM, 2 ); + + m_spinCtrlMinSize = new wxSpinCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 ); + bSizer162->Add( m_spinCtrlMinSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + wxArrayString m_choiceUnitMinSizeChoices; + m_choiceUnitMinSize = new wxChoice( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceUnitMinSizeChoices, 0 ); + m_choiceUnitMinSize->SetSelection( 0 ); + bSizer162->Add( m_choiceUnitMinSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer158->Add( bSizer162, 0, wxBOTTOM|wxEXPAND, 5 ); + + wxBoxSizer* bSizer163; + bSizer163 = new wxBoxSizer( wxVERTICAL ); + + m_staticText102 = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("Maximum:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText102->Wrap( -1 ); + bSizer163->Add( m_staticText102, 0, wxBOTTOM, 2 ); + + m_spinCtrlMaxSize = new wxSpinCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 ); + bSizer163->Add( m_spinCtrlMaxSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + wxArrayString m_choiceUnitMaxSizeChoices; + m_choiceUnitMaxSize = new wxChoice( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceUnitMaxSizeChoices, 0 ); + m_choiceUnitMaxSize->SetSelection( 0 ); + bSizer163->Add( m_choiceUnitMaxSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer158->Add( bSizer163, 0, wxEXPAND, 5 ); + + + bSizer168->Add( bSizer158, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer160->Add( bSizer168, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 5 ); + + + bSizer1591->Add( bSizer160, 0, wxEXPAND, 5 ); + + + m_panelFilterSettings->SetSizer( bSizer1591 ); + m_panelFilterSettings->Layout(); + bSizer1591->Fit( m_panelFilterSettings ); + bSizer278->Add( m_panelFilterSettings, 1, wxEXPAND, 5 ); + + m_staticline62 = new wxStaticLine( m_panelFilterSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer278->Add( m_staticline62, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer280; + bSizer280 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText44 = new wxStaticText( m_panelFilterSettingsHolder, wxID_ANY, _("Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair."), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_staticText44->Wrap( 590 ); + bSizer280->Add( m_staticText44, 0, wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + + + bSizer280->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticline46 = new wxStaticLine( m_panelFilterSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer280->Add( m_staticline46, 0, wxEXPAND, 5 ); + + m_buttonClear = new wxButton( m_panelFilterSettingsHolder, wxID_DEFAULT, _("C&lear"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer280->Add( m_buttonClear, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); + + + bSizer278->Add( bSizer280, 0, wxEXPAND, 5 ); + + + m_panelFilterSettingsHolder->SetSizer( bSizer278 ); + m_panelFilterSettingsHolder->Layout(); + bSizer278->Fit( m_panelFilterSettingsHolder ); + m_notebook->AddPage( m_panelFilterSettingsHolder, _("dummy"), false ); + m_panelSyncSettingsHolder = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelSyncSettingsHolder->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer276; + bSizer276 = new wxBoxSizer( wxVERTICAL ); + + bSizerLocalSyncSettings = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxUseLocalSyncOptions = new wxCheckBox( m_panelSyncSettingsHolder, wxID_ANY, _("Use local settings:"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerLocalSyncSettings->Add( m_checkBoxUseLocalSyncOptions, 0, wxALL|wxEXPAND, 10 ); + + m_staticline60 = new wxStaticLine( m_panelSyncSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerLocalSyncSettings->Add( m_staticline60, 0, wxEXPAND, 5 ); + + + bSizer276->Add( bSizerLocalSyncSettings, 0, wxEXPAND, 5 ); + + m_panelSyncSettings = new wxPanel( m_panelSyncSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelSyncSettings->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer232; + bSizer232 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer237; + bSizer237 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer235; + bSizer235 = new wxBoxSizer( wxVERTICAL ); + + m_staticText86 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Select a variant:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText86->Wrap( -1 ); + bSizer235->Add( m_staticText86, 0, wxALL, 5 ); + + wxBoxSizer* bSizer236; + bSizer236 = new wxBoxSizer( wxVERTICAL ); + + m_toggleBtnTwoWay = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_toggleBtnTwoWay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer236->Add( m_toggleBtnTwoWay, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxBOTTOM, 5 ); + + m_toggleBtnMirror = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_toggleBtnMirror->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer236->Add( m_toggleBtnMirror, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxBOTTOM, 5 ); + + m_toggleBtnUpdate = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_toggleBtnUpdate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer236->Add( m_toggleBtnUpdate, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxBOTTOM, 5 ); + + m_toggleBtnCustom = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_toggleBtnCustom->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer236->Add( m_toggleBtnCustom, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer235->Add( bSizer236, 0, wxRIGHT|wxLEFT, 5 ); + + m_checkBoxDetectMove = new wxCheckBox( m_panelSyncSettings, wxID_ANY, _("Detect moved files"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxDetectMove->SetValue(true); + m_checkBoxDetectMove->SetToolTip( _("- Detection active after initial sync\n- Requires and creates database files\n- Not supported by all file systems") ); + + bSizer235->Add( m_checkBoxDetectMove, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALL, 5 ); + + + bSizer237->Add( bSizer235, 0, wxALL, 5 ); + + m_staticline53 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer237->Add( m_staticline53, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer238; + bSizer238 = new wxBoxSizer( wxVERTICAL ); + + m_textCtrlSyncVarDescription = new wxTextCtrl( m_panelSyncSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); + bSizer238->Add( m_textCtrlSyncVarDescription, 1, wxEXPAND|wxLEFT, 5 ); + + m_staticline43 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer238->Add( m_staticline43, 0, wxEXPAND, 5 ); + + bSizerSyncConfig = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer173; + bSizer173 = new wxBoxSizer( wxVERTICAL ); + + + bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticText119 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Category"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText119->Wrap( -1 ); + bSizer173->Add( m_staticText119, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); + + + bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticText120 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Action"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText120->Wrap( -1 ); + bSizer173->Add( m_staticText120, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); + + + bSizerSyncConfig->Add( bSizer173, 0, wxEXPAND|wxRIGHT, 5 ); + + fgSizerSyncDirections = new wxFlexGridSizer( 2, 0, 5, 5 ); + fgSizerSyncDirections->SetFlexibleDirection( wxBOTH ); + fgSizerSyncDirections->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_bitmapLeftOnly = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapLeftOnly->SetToolTip( _("Item exists on left side only") ); + + fgSizerSyncDirections->Add( m_bitmapLeftOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bitmapLeftNewer = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapLeftNewer->SetToolTip( _("Left side is newer") ); + + fgSizerSyncDirections->Add( m_bitmapLeftNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bitmapDifferent = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapDifferent->SetToolTip( _("Items have different content") ); + + fgSizerSyncDirections->Add( m_bitmapDifferent, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bitmapConflict = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapConflict->SetToolTip( _("Conflict/item cannot be categorized") ); + + fgSizerSyncDirections->Add( m_bitmapConflict, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bitmapRightNewer = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapRightNewer->SetToolTip( _("Right side is newer") ); + + fgSizerSyncDirections->Add( m_bitmapRightNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bitmapRightOnly = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapRightOnly->SetToolTip( _("Item exists on right side only") ); + + fgSizerSyncDirections->Add( m_bitmapRightOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonLeftOnly = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + fgSizerSyncDirections->Add( m_bpButtonLeftOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonLeftNewer = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + fgSizerSyncDirections->Add( m_bpButtonLeftNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonDifferent = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + fgSizerSyncDirections->Add( m_bpButtonDifferent, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonConflict = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + fgSizerSyncDirections->Add( m_bpButtonConflict, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonRightNewer = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + fgSizerSyncDirections->Add( m_bpButtonRightNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonRightOnly = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + fgSizerSyncDirections->Add( m_bpButtonRightOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerSyncConfig->Add( fgSizerSyncDirections, 0, 0, 5 ); + + m_bitmapDatabase = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_bitmapDatabase->SetToolTip( _("Detect synchronization directions with the help of database files") ); + + bSizerSyncConfig->Add( m_bitmapDatabase, 0, wxALIGN_CENTER_HORIZONTAL|wxLEFT|wxALIGN_CENTER_VERTICAL, 10 ); + + wxBoxSizer* bSizerKeepVerticalHeightWhenSyncDirsNotShown; + bSizerKeepVerticalHeightWhenSyncDirsNotShown = new wxBoxSizer( wxVERTICAL ); + + + bSizerKeepVerticalHeightWhenSyncDirsNotShown->Add( 0, 45, 0, 0, 5 ); + + + bSizerKeepVerticalHeightWhenSyncDirsNotShown->Add( 0, 5, 1, 0, 5 ); + + + bSizerKeepVerticalHeightWhenSyncDirsNotShown->Add( 0, 46, 0, 0, 5 ); + + + bSizerSyncConfig->Add( bSizerKeepVerticalHeightWhenSyncDirsNotShown, 0, 0, 5 ); + + + bSizer238->Add( bSizerSyncConfig, 0, wxALL, 10 ); + + + bSizer237->Add( bSizer238, 1, wxEXPAND, 5 ); + + + bSizer232->Add( bSizer237, 0, wxEXPAND, 5 ); + + m_staticline54 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer232->Add( m_staticline54, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer184; + bSizer184 = new wxBoxSizer( wxVERTICAL ); + + m_staticText87 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Delete files:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText87->Wrap( -1 ); + bSizer184->Add( m_staticText87, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM, 5 ); + + wxBoxSizer* bSizer180; + bSizer180 = new wxBoxSizer( wxHORIZONTAL ); + + m_toggleBtnPermanent = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("&Permanent"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnPermanent->SetToolTip( _("Delete or overwrite files permanently") ); + + bSizer180->Add( m_toggleBtnPermanent, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxRIGHT, 5 ); + + m_toggleBtnRecycler = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("&Recycle bin"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnRecycler->SetToolTip( _("Back up deleted and overwritten files in the recycle bin") ); + + bSizer180->Add( m_toggleBtnRecycler, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxRIGHT, 5 ); + + m_toggleBtnVersioning = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("&Versioning"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnVersioning->SetToolTip( _("Move files to a user-defined folder") ); + + bSizer180->Add( m_toggleBtnVersioning, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer184->Add( bSizer180, 0, 0, 5 ); + + bSizerVersioning = new wxBoxSizer( wxHORIZONTAL ); + + m_panelVersioning = new wxPanel( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelVersioning->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer191; + bSizer191 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer156; + bSizer156 = new wxBoxSizer( wxHORIZONTAL ); + + m_versioningFolder = new FolderHistoryBox( m_panelVersioning, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer156->Add( m_versioningFolder, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectDirVersioning = new wxButton( m_panelVersioning, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectDirVersioning->SetToolTip( _("Select a folder") ); + + bSizer156->Add( m_buttonSelectDirVersioning, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer191->Add( bSizer156, 0, wxEXPAND|wxBOTTOM, 5 ); + + bSizer192 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText93 = new wxStaticText( m_panelVersioning, wxID_ANY, _("Naming convention:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText93->Wrap( -1 ); + bSizer192->Add( m_staticText93, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + wxArrayString m_choiceVersioningStyleChoices; + m_choiceVersioningStyle = new wxChoice( m_panelVersioning, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceVersioningStyleChoices, 0 ); + m_choiceVersioningStyle->SetSelection( 0 ); + bSizer192->Add( m_choiceVersioningStyle, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_staticTextNamingCvtPart1 = new wxStaticText( m_panelVersioning, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextNamingCvtPart1->Wrap( -1 ); + m_staticTextNamingCvtPart1->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizer192->Add( m_staticTextNamingCvtPart1, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextNamingCvtPart2Bold = new wxStaticText( m_panelVersioning, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextNamingCvtPart2Bold->Wrap( -1 ); + m_staticTextNamingCvtPart2Bold->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + m_staticTextNamingCvtPart2Bold->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizer192->Add( m_staticTextNamingCvtPart2Bold, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextNamingCvtPart3 = new wxStaticText( m_panelVersioning, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextNamingCvtPart3->Wrap( -1 ); + m_staticTextNamingCvtPart3->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizer192->Add( m_staticTextNamingCvtPart3, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer192->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_hyperlink17 = new wxHyperlinkCtrl( m_panelVersioning, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + bSizer192->Add( m_hyperlink17, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); + + + bSizer191->Add( bSizer192, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + m_panelVersioning->SetSizer( bSizer191 ); + m_panelVersioning->Layout(); + bSizer191->Fit( m_panelVersioning ); + bSizerVersioning->Add( m_panelVersioning, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer184->Add( bSizerVersioning, 0, wxTOP|wxEXPAND, 5 ); + + + bSizer232->Add( bSizer184, 0, wxALL|wxEXPAND, 10 ); + + bSizerMiscConfig = new wxBoxSizer( wxVERTICAL ); + + m_staticline582 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerMiscConfig->Add( m_staticline582, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer1732; + bSizer1732 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer174; + bSizer174 = new wxBoxSizer( wxVERTICAL ); + + m_staticText88 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Handle errors:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText88->Wrap( -1 ); + bSizer174->Add( m_staticText88, 0, wxBOTTOM, 5 ); + + wxBoxSizer* bSizer175; + bSizer175 = new wxBoxSizer( wxHORIZONTAL ); + + m_toggleBtnErrorIgnore = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("&Ignore"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnErrorIgnore->SetToolTip( _("Hide all error and warning messages") ); + + bSizer175->Add( m_toggleBtnErrorIgnore, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_toggleBtnErrorPopup = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("&Pop-up"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnErrorPopup->SetToolTip( _("Show pop-up on errors or warnings") ); + + bSizer175->Add( m_toggleBtnErrorPopup, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer174->Add( bSizer175, 0, 0, 5 ); + + + bSizer1732->Add( bSizer174, 0, wxALL, 10 ); + + m_staticline57 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer1732->Add( m_staticline57, 0, wxEXPAND, 5 ); + + bSizerOnCompletion = new wxBoxSizer( wxVERTICAL ); + + m_staticText89 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("On completion:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText89->Wrap( -1 ); + bSizerOnCompletion->Add( m_staticText89, 0, wxBOTTOM, 5 ); + + m_comboBoxOnCompletion = new OnCompletionBox( m_panelSyncSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizerOnCompletion->Add( m_comboBoxOnCompletion, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer1732->Add( bSizerOnCompletion, 1, wxALL, 10 ); + + + bSizerMiscConfig->Add( bSizer1732, 1, wxEXPAND, 5 ); + + + bSizer232->Add( bSizerMiscConfig, 1, wxEXPAND, 5 ); + + + m_panelSyncSettings->SetSizer( bSizer232 ); + m_panelSyncSettings->Layout(); + bSizer232->Fit( m_panelSyncSettings ); + bSizer276->Add( m_panelSyncSettings, 1, wxEXPAND, 5 ); + + + m_panelSyncSettingsHolder->SetSizer( bSizer276 ); + m_panelSyncSettingsHolder->Layout(); + bSizer276->Fit( m_panelSyncSettingsHolder ); + m_notebook->AddPage( m_panelSyncSettingsHolder, _("dummy"), false ); + + bSizer7->Add( m_notebook, 1, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonOkay->SetDefault(); + m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer7->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + + + this->SetSizer( bSizer7 ); + this->Layout(); + bSizer7->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( ConfigDlgGenerated::OnClose ) ); + m_checkBoxUseLocalCmpOptions->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleLocalCompSettings ), NULL, this ); + m_toggleBtnTimeSize->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( ConfigDlgGenerated::OnTimeSizeDouble ), NULL, this ); + m_toggleBtnTimeSize->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnTimeSize ), NULL, this ); + m_toggleBtnContent->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( ConfigDlgGenerated::OnContentDouble ), NULL, this ); + m_toggleBtnContent->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnContent ), NULL, this ); + m_checkBoxTimeShift->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeCompOption ), NULL, this ); + m_hyperlink241->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpTimeShift ), NULL, this ); + m_checkBoxSymlinksInclude->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeCompOption ), NULL, this ); + m_hyperlink24->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpComparisonSettings ), NULL, this ); + m_textCtrlInclude->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this ); + m_hyperlink171->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpShowExamples ), NULL, this ); + m_textCtrlExclude->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this ); + m_choiceUnitTimespan->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this ); + m_choiceUnitMinSize->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this ); + m_choiceUnitMaxSize->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this ); + m_buttonClear->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnFilterReset ), NULL, this ); + m_checkBoxUseLocalSyncOptions->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleLocalSyncSettings ), NULL, this ); + m_toggleBtnTwoWay->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( ConfigDlgGenerated::OnSyncTwoWayDouble ), NULL, this ); + m_toggleBtnTwoWay->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnSyncTwoWay ), NULL, this ); + m_toggleBtnMirror->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( ConfigDlgGenerated::OnSyncMirrorDouble ), NULL, this ); + m_toggleBtnMirror->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnSyncMirror ), NULL, this ); + m_toggleBtnUpdate->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( ConfigDlgGenerated::OnSyncUpdateDouble ), NULL, this ); + m_toggleBtnUpdate->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnSyncUpdate ), NULL, this ); + m_toggleBtnCustom->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( ConfigDlgGenerated::OnSyncCustomDouble ), NULL, this ); + m_toggleBtnCustom->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnSyncCustom ), NULL, this ); + m_checkBoxDetectMove->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleDetectMovedFiles ), NULL, this ); + m_bpButtonLeftOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnExLeftSideOnly ), NULL, this ); + m_bpButtonLeftNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnLeftNewer ), NULL, this ); + m_bpButtonDifferent->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnDifferent ), NULL, this ); + m_bpButtonConflict->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnConflict ), NULL, this ); + m_bpButtonRightNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnRightNewer ), NULL, this ); + m_bpButtonRightOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnExRightSideOnly ), NULL, this ); + m_toggleBtnPermanent->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnDeletionPermanent ), NULL, this ); + m_toggleBtnRecycler->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnDeletionRecycler ), NULL, this ); + m_toggleBtnVersioning->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnDeletionVersioning ), NULL, this ); + m_choiceVersioningStyle->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeSyncOption ), NULL, this ); + m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpVersioning ), NULL, this ); + m_toggleBtnErrorIgnore->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnErrorIgnore ), NULL, this ); + m_toggleBtnErrorPopup->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnErrorPopup ), NULL, this ); + m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnOkay ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnCancel ), NULL, this ); } ConfigDlgGenerated::~ConfigDlgGenerated() @@ -1809,221 +1809,221 @@ ConfigDlgGenerated::~ConfigDlgGenerated() SyncConfirmationDlgGenerated::SyncConfirmationDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxDefaultSize, wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer134; - bSizer134 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer72; - bSizer72 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapSync = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizer72->Add( m_bitmapSync, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); - - m_staticTextHeader = new wxStaticText( this, wxID_ANY, _("Start synchronization now?"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextHeader->Wrap( -1 ); - bSizer72->Add( m_staticTextHeader, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); - - - bSizer134->Add( bSizer72, 0, 0, 5 ); - - m_staticline371 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer134->Add( m_staticline371, 0, wxEXPAND, 5 ); - - m_panelStatistics = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - m_panelStatistics->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer185; - bSizer185 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer185->Add( 40, 0, 0, 0, 5 ); - - - bSizer185->Add( 0, 0, 1, 0, 5 ); - - m_staticline38 = new wxStaticLine( m_panelStatistics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer185->Add( m_staticline38, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer162; - bSizer162 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer182; - bSizer182 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText84 = new wxStaticText( m_panelStatistics, wxID_ANY, _("Variant:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText84->Wrap( -1 ); - bSizer182->Add( m_staticText84, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - - - bSizer182->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextVariant = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextVariant->Wrap( -1 ); - m_staticTextVariant->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer182->Add( m_staticTextVariant, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - - bSizer182->Add( 0, 0, 1, wxEXPAND, 5 ); - - - bSizer162->Add( bSizer182, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 5 ); - - m_staticline14 = new wxStaticLine( m_panelStatistics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer162->Add( m_staticline14, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer181; - bSizer181 = new wxBoxSizer( wxVERTICAL ); - - m_staticText83 = new wxStaticText( m_panelStatistics, wxID_ANY, _("Statistics:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText83->Wrap( -1 ); - bSizer181->Add( m_staticText83, 0, wxALL, 5 ); - - wxFlexGridSizer* fgSizer11; - fgSizer11 = new wxFlexGridSizer( 2, 7, 2, 5 ); - fgSizer11->SetFlexibleDirection( wxBOTH ); - fgSizer11->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - - m_bitmapDeleteLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); - - fgSizer11->Add( m_bitmapDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bitmapUpdateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapUpdateLeft->SetToolTip( _("Number of files that will be updated") ); - - fgSizer11->Add( m_bitmapUpdateLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bitmapCreateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); - - fgSizer11->Add( m_bitmapCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bitmapData = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapData->SetToolTip( _("Total bytes to copy") ); - - fgSizer11->Add( m_bitmapData, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bitmapCreateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapCreateRight->SetToolTip( _("Number of files and folders that will be created") ); - - fgSizer11->Add( m_bitmapCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bitmapUpdateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapUpdateRight->SetToolTip( _("Number of files that will be updated") ); - - fgSizer11->Add( m_bitmapUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bitmapDeleteRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); - - fgSizer11->Add( m_bitmapDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextDeleteLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDeleteLeft->Wrap( -1 ); - m_staticTextDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); - - fgSizer11->Add( m_staticTextDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextUpdateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextUpdateLeft->Wrap( -1 ); - m_staticTextUpdateLeft->SetToolTip( _("Number of files that will be updated") ); - - fgSizer11->Add( m_staticTextUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_staticTextCreateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextCreateLeft->Wrap( -1 ); - m_staticTextCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); - - fgSizer11->Add( m_staticTextCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_staticTextData = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextData->Wrap( -1 ); - m_staticTextData->SetToolTip( _("Total bytes to copy") ); - - fgSizer11->Add( m_staticTextData, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextCreateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextCreateRight->Wrap( -1 ); - m_staticTextCreateRight->SetToolTip( _("Number of files and folders that will be created") ); - - fgSizer11->Add( m_staticTextCreateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextUpdateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextUpdateRight->Wrap( -1 ); - m_staticTextUpdateRight->SetToolTip( _("Number of files that will be updated") ); - - fgSizer11->Add( m_staticTextUpdateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextDeleteRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDeleteRight->Wrap( -1 ); - m_staticTextDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); - - fgSizer11->Add( m_staticTextDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer181->Add( fgSizer11, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer162->Add( bSizer181, 0, wxEXPAND|wxALL, 5 ); - - - bSizer185->Add( bSizer162, 0, 0, 5 ); - - m_staticline381 = new wxStaticLine( m_panelStatistics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer185->Add( m_staticline381, 0, wxEXPAND, 5 ); - - - bSizer185->Add( 0, 0, 1, 0, 5 ); - - - bSizer185->Add( 40, 0, 0, 0, 5 ); - - - m_panelStatistics->SetSizer( bSizer185 ); - m_panelStatistics->Layout(); - bSizer185->Fit( m_panelStatistics ); - bSizer134->Add( m_panelStatistics, 0, wxEXPAND, 5 ); - - m_staticline12 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer134->Add( m_staticline12, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer164; - bSizer164 = new wxBoxSizer( wxVERTICAL ); - - m_checkBoxDontShowAgain = new wxCheckBox( this, wxID_ANY, _("&Don't show this dialog again"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer164->Add( m_checkBoxDontShowAgain, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonStartSync = new wxButton( this, wxID_OK, _("&Start"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonStartSync->SetDefault(); - m_buttonStartSync->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerStdButtons->Add( m_buttonStartSync, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer164->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); - - - bSizer134->Add( bSizer164, 1, wxEXPAND, 5 ); - - - this->SetSizer( bSizer134 ); - this->Layout(); - bSizer134->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncConfirmationDlgGenerated::OnClose ) ); - m_buttonStartSync->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnStartSync ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnCancel ), NULL, this ); + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer134; + bSizer134 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer72; + bSizer72 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapSync = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer72->Add( m_bitmapSync, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); + + m_staticTextHeader = new wxStaticText( this, wxID_ANY, _("Start synchronization now?"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextHeader->Wrap( -1 ); + bSizer72->Add( m_staticTextHeader, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + + + bSizer134->Add( bSizer72, 0, 0, 5 ); + + m_staticline371 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer134->Add( m_staticline371, 0, wxEXPAND, 5 ); + + m_panelStatistics = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_panelStatistics->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer185; + bSizer185 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer185->Add( 40, 0, 0, 0, 5 ); + + + bSizer185->Add( 0, 0, 1, 0, 5 ); + + m_staticline38 = new wxStaticLine( m_panelStatistics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer185->Add( m_staticline38, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer162; + bSizer162 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer182; + bSizer182 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText84 = new wxStaticText( m_panelStatistics, wxID_ANY, _("Variant:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText84->Wrap( -1 ); + bSizer182->Add( m_staticText84, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); + + + bSizer182->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextVariant = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextVariant->Wrap( -1 ); + m_staticTextVariant->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer182->Add( m_staticTextVariant, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + bSizer182->Add( 0, 0, 1, wxEXPAND, 5 ); + + + bSizer162->Add( bSizer182, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 5 ); + + m_staticline14 = new wxStaticLine( m_panelStatistics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer162->Add( m_staticline14, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer181; + bSizer181 = new wxBoxSizer( wxVERTICAL ); + + m_staticText83 = new wxStaticText( m_panelStatistics, wxID_ANY, _("Statistics:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText83->Wrap( -1 ); + bSizer181->Add( m_staticText83, 0, wxALL, 5 ); + + wxFlexGridSizer* fgSizer11; + fgSizer11 = new wxFlexGridSizer( 2, 7, 2, 5 ); + fgSizer11->SetFlexibleDirection( wxBOTH ); + fgSizer11->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_bitmapDeleteLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); + + fgSizer11->Add( m_bitmapDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bitmapUpdateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapUpdateLeft->SetToolTip( _("Number of files that will be updated") ); + + fgSizer11->Add( m_bitmapUpdateLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bitmapCreateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); + + fgSizer11->Add( m_bitmapCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bitmapData = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapData->SetToolTip( _("Total bytes to copy") ); + + fgSizer11->Add( m_bitmapData, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bitmapCreateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapCreateRight->SetToolTip( _("Number of files and folders that will be created") ); + + fgSizer11->Add( m_bitmapCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bitmapUpdateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapUpdateRight->SetToolTip( _("Number of files that will be updated") ); + + fgSizer11->Add( m_bitmapUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bitmapDeleteRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); + + fgSizer11->Add( m_bitmapDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextDeleteLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDeleteLeft->Wrap( -1 ); + m_staticTextDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); + + fgSizer11->Add( m_staticTextDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextUpdateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextUpdateLeft->Wrap( -1 ); + m_staticTextUpdateLeft->SetToolTip( _("Number of files that will be updated") ); + + fgSizer11->Add( m_staticTextUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_staticTextCreateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextCreateLeft->Wrap( -1 ); + m_staticTextCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); + + fgSizer11->Add( m_staticTextCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_staticTextData = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextData->Wrap( -1 ); + m_staticTextData->SetToolTip( _("Total bytes to copy") ); + + fgSizer11->Add( m_staticTextData, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextCreateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextCreateRight->Wrap( -1 ); + m_staticTextCreateRight->SetToolTip( _("Number of files and folders that will be created") ); + + fgSizer11->Add( m_staticTextCreateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextUpdateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextUpdateRight->Wrap( -1 ); + m_staticTextUpdateRight->SetToolTip( _("Number of files that will be updated") ); + + fgSizer11->Add( m_staticTextUpdateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextDeleteRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDeleteRight->Wrap( -1 ); + m_staticTextDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); + + fgSizer11->Add( m_staticTextDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer181->Add( fgSizer11, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer162->Add( bSizer181, 0, wxEXPAND|wxALL, 5 ); + + + bSizer185->Add( bSizer162, 0, 0, 5 ); + + m_staticline381 = new wxStaticLine( m_panelStatistics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer185->Add( m_staticline381, 0, wxEXPAND, 5 ); + + + bSizer185->Add( 0, 0, 1, 0, 5 ); + + + bSizer185->Add( 40, 0, 0, 0, 5 ); + + + m_panelStatistics->SetSizer( bSizer185 ); + m_panelStatistics->Layout(); + bSizer185->Fit( m_panelStatistics ); + bSizer134->Add( m_panelStatistics, 0, wxEXPAND, 5 ); + + m_staticline12 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer134->Add( m_staticline12, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer164; + bSizer164 = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxDontShowAgain = new wxCheckBox( this, wxID_ANY, _("&Don't show this dialog again"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer164->Add( m_checkBoxDontShowAgain, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonStartSync = new wxButton( this, wxID_OK, _("&Start"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonStartSync->SetDefault(); + m_buttonStartSync->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerStdButtons->Add( m_buttonStartSync, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer164->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + + + bSizer134->Add( bSizer164, 1, wxEXPAND, 5 ); + + + this->SetSizer( bSizer134 ); + this->Layout(); + bSizer134->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncConfirmationDlgGenerated::OnClose ) ); + m_buttonStartSync->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnStartSync ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnCancel ), NULL, this ); } SyncConfirmationDlgGenerated::~SyncConfirmationDlgGenerated() @@ -2032,88 +2032,88 @@ SyncConfirmationDlgGenerated::~SyncConfirmationDlgGenerated() FolderPairPanelGenerated::FolderPairPanelGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) { - wxBoxSizer* bSizer74; - bSizer74 = new wxBoxSizer( wxHORIZONTAL ); - - m_panelLeft = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelLeft->SetMinSize( wxSize( 1,-1 ) ); - - wxBoxSizer* bSizer134; - bSizer134 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonFolderPairOptions = new wxBitmapButton( m_panelLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - m_bpButtonFolderPairOptions->SetToolTip( _("Arrange folder pair") ); - - bSizer134->Add( m_bpButtonFolderPairOptions, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonRemovePair = new wxBitmapButton( m_panelLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - m_bpButtonRemovePair->SetToolTip( _("Remove folder pair") ); - - bSizer134->Add( m_bpButtonRemovePair, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_directoryLeft = new FolderHistoryBox( m_panelLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer134->Add( m_directoryLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectDirLeft = new wxButton( m_panelLeft, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectDirLeft->SetToolTip( _("Select a folder") ); - - bSizer134->Add( m_buttonSelectDirLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - m_panelLeft->SetSizer( bSizer134 ); - m_panelLeft->Layout(); - bSizer134->Fit( m_panelLeft ); - bSizer74->Add( m_panelLeft, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxEXPAND, 5 ); - - m_panel20 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer95; - bSizer95 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer95->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_bpButtonAltCompCfg = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer95->Add( m_bpButtonAltCompCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonLocalFilter = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer95->Add( m_bpButtonLocalFilter, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 2 ); - - m_bpButtonAltSyncCfg = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer95->Add( m_bpButtonAltSyncCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer95->Add( 0, 0, 1, wxEXPAND, 5 ); - - - m_panel20->SetSizer( bSizer95 ); - m_panel20->Layout(); - bSizer95->Fit( m_panel20 ); - bSizer74->Add( m_panel20, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - - m_panelRight = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelRight->SetMinSize( wxSize( 1,-1 ) ); - - wxBoxSizer* bSizer135; - bSizer135 = new wxBoxSizer( wxHORIZONTAL ); - - m_directoryRight = new FolderHistoryBox( m_panelRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer135->Add( m_directoryRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectDirRight = new wxButton( m_panelRight, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectDirRight->SetToolTip( _("Select a folder") ); - - bSizer135->Add( m_buttonSelectDirRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - m_panelRight->SetSizer( bSizer135 ); - m_panelRight->Layout(); - bSizer135->Fit( m_panelRight ); - bSizer74->Add( m_panelRight, 1, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxEXPAND, 5 ); - - - this->SetSizer( bSizer74 ); - this->Layout(); - bSizer74->Fit( this ); + wxBoxSizer* bSizer74; + bSizer74 = new wxBoxSizer( wxHORIZONTAL ); + + m_panelLeft = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelLeft->SetMinSize( wxSize( 1,-1 ) ); + + wxBoxSizer* bSizer134; + bSizer134 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonFolderPairOptions = new wxBitmapButton( m_panelLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonFolderPairOptions->SetToolTip( _("Arrange folder pair") ); + + bSizer134->Add( m_bpButtonFolderPairOptions, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonRemovePair = new wxBitmapButton( m_panelLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonRemovePair->SetToolTip( _("Remove folder pair") ); + + bSizer134->Add( m_bpButtonRemovePair, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_directoryLeft = new FolderHistoryBox( m_panelLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer134->Add( m_directoryLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectDirLeft = new wxButton( m_panelLeft, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectDirLeft->SetToolTip( _("Select a folder") ); + + bSizer134->Add( m_buttonSelectDirLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + m_panelLeft->SetSizer( bSizer134 ); + m_panelLeft->Layout(); + bSizer134->Fit( m_panelLeft ); + bSizer74->Add( m_panelLeft, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxEXPAND, 5 ); + + m_panel20 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer95; + bSizer95 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer95->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bpButtonAltCompCfg = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer95->Add( m_bpButtonAltCompCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonLocalFilter = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer95->Add( m_bpButtonLocalFilter, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 2 ); + + m_bpButtonAltSyncCfg = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer95->Add( m_bpButtonAltSyncCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer95->Add( 0, 0, 1, wxEXPAND, 5 ); + + + m_panel20->SetSizer( bSizer95 ); + m_panel20->Layout(); + bSizer95->Fit( m_panel20 ); + bSizer74->Add( m_panel20, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT|wxEXPAND, 5 ); + + m_panelRight = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelRight->SetMinSize( wxSize( 1,-1 ) ); + + wxBoxSizer* bSizer135; + bSizer135 = new wxBoxSizer( wxHORIZONTAL ); + + m_directoryRight = new FolderHistoryBox( m_panelRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer135->Add( m_directoryRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectDirRight = new wxButton( m_panelRight, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectDirRight->SetToolTip( _("Select a folder") ); + + bSizer135->Add( m_buttonSelectDirRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + m_panelRight->SetSizer( bSizer135 ); + m_panelRight->Layout(); + bSizer135->Fit( m_panelRight ); + bSizer74->Add( m_panelRight, 1, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxEXPAND, 5 ); + + + this->SetSizer( bSizer74 ); + this->Layout(); + bSizer74->Fit( this ); } FolderPairPanelGenerated::~FolderPairPanelGenerated() @@ -2122,110 +2122,110 @@ FolderPairPanelGenerated::~FolderPairPanelGenerated() CompareProgressDlgGenerated::CompareProgressDlgGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) { - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer40; - bSizer40 = new wxBoxSizer( wxHORIZONTAL ); - - m_panelStatistics = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - m_panelStatistics->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer187; - bSizer187 = new wxBoxSizer( wxVERTICAL ); - - - bSizer187->Add( 0, 5, 0, 0, 5 ); - - wxFlexGridSizer* fgSizer7; - fgSizer7 = new wxFlexGridSizer( 0, 2, 5, 5 ); - fgSizer7->SetFlexibleDirection( wxHORIZONTAL ); - fgSizer7->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - - m_staticTextItemsFoundLabel = new wxStaticText( m_panelStatistics, wxID_ANY, _("Items found:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextItemsFoundLabel->Wrap( -1 ); - fgSizer7->Add( m_staticTextItemsFoundLabel, 0, wxALIGN_BOTTOM, 5 ); - - m_staticTextItemsFound = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextItemsFound->Wrap( -1 ); - m_staticTextItemsFound->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - fgSizer7->Add( m_staticTextItemsFound, 0, wxALIGN_BOTTOM, 5 ); - - m_staticTextItemsRemainingLabel = new wxStaticText( m_panelStatistics, wxID_ANY, _("Items remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextItemsRemainingLabel->Wrap( -1 ); - fgSizer7->Add( m_staticTextItemsRemainingLabel, 0, wxALIGN_BOTTOM, 5 ); - - bSizerItemsRemaining = new wxBoxSizer( wxHORIZONTAL ); - - m_staticTextItemsRemaining = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextItemsRemaining->Wrap( -1 ); - m_staticTextItemsRemaining->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerItemsRemaining->Add( m_staticTextItemsRemaining, 0, wxALIGN_BOTTOM, 5 ); - - m_staticTextDataRemaining = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDataRemaining->Wrap( -1 ); - bSizerItemsRemaining->Add( m_staticTextDataRemaining, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); - - - fgSizer7->Add( bSizerItemsRemaining, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextTimeRemainingLabel = new wxStaticText( m_panelStatistics, wxID_ANY, _("Time remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextTimeRemainingLabel->Wrap( -1 ); - fgSizer7->Add( m_staticTextTimeRemainingLabel, 0, wxALIGN_BOTTOM, 5 ); - - m_staticTextTimeRemaining = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextTimeRemaining->Wrap( -1 ); - m_staticTextTimeRemaining->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - fgSizer7->Add( m_staticTextTimeRemaining, 0, wxALIGN_BOTTOM, 5 ); - - wxStaticText* m_staticText37; - m_staticText37 = new wxStaticText( m_panelStatistics, wxID_ANY, _("Time elapsed:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText37->Wrap( -1 ); - fgSizer7->Add( m_staticText37, 0, wxALIGN_BOTTOM, 5 ); - - m_staticTextTimeElapsed = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextTimeElapsed->Wrap( -1 ); - m_staticTextTimeElapsed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - fgSizer7->Add( m_staticTextTimeElapsed, 0, wxALIGN_BOTTOM, 5 ); - - - bSizer187->Add( fgSizer7, 0, wxRIGHT|wxLEFT, 5 ); - - - bSizer187->Add( 0, 5, 0, 0, 5 ); - - - m_panelStatistics->SetSizer( bSizer187 ); - m_panelStatistics->Layout(); - bSizer187->Fit( m_panelStatistics ); - bSizer40->Add( m_panelStatistics, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - wxBoxSizer* bSizer181; - bSizer181 = new wxBoxSizer( wxVERTICAL ); - - m_staticTextStatus = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatus->Wrap( -1 ); - bSizer181->Add( m_staticTextStatus, 0, 0, 5 ); - - m_gauge2 = new wxGauge( this, wxID_ANY, 100, wxDefaultPosition, wxSize( -1,14 ), wxGA_HORIZONTAL|wxGA_SMOOTH ); - bSizer181->Add( m_gauge2, 0, wxEXPAND|wxTOP, 5 ); - - m_staticTextSpeed = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextSpeed->Wrap( -1 ); - m_staticTextSpeed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer181->Add( m_staticTextSpeed, 0, wxALIGN_BOTTOM|wxTOP, 5 ); - - - bSizer40->Add( bSizer181, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - - this->SetSizer( bSizer40 ); - this->Layout(); - bSizer40->Fit( this ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer40; + bSizer40 = new wxBoxSizer( wxHORIZONTAL ); + + m_panelStatistics = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_panelStatistics->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer187; + bSizer187 = new wxBoxSizer( wxVERTICAL ); + + + bSizer187->Add( 0, 5, 0, 0, 5 ); + + wxFlexGridSizer* fgSizer7; + fgSizer7 = new wxFlexGridSizer( 0, 2, 5, 5 ); + fgSizer7->SetFlexibleDirection( wxHORIZONTAL ); + fgSizer7->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_staticTextItemsFoundLabel = new wxStaticText( m_panelStatistics, wxID_ANY, _("Items found:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextItemsFoundLabel->Wrap( -1 ); + fgSizer7->Add( m_staticTextItemsFoundLabel, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextItemsFound = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextItemsFound->Wrap( -1 ); + m_staticTextItemsFound->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + fgSizer7->Add( m_staticTextItemsFound, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextItemsRemainingLabel = new wxStaticText( m_panelStatistics, wxID_ANY, _("Items remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextItemsRemainingLabel->Wrap( -1 ); + fgSizer7->Add( m_staticTextItemsRemainingLabel, 0, wxALIGN_BOTTOM, 5 ); + + bSizerItemsRemaining = new wxBoxSizer( wxHORIZONTAL ); + + m_staticTextItemsRemaining = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextItemsRemaining->Wrap( -1 ); + m_staticTextItemsRemaining->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerItemsRemaining->Add( m_staticTextItemsRemaining, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextDataRemaining = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDataRemaining->Wrap( -1 ); + bSizerItemsRemaining->Add( m_staticTextDataRemaining, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); + + + fgSizer7->Add( bSizerItemsRemaining, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextTimeRemainingLabel = new wxStaticText( m_panelStatistics, wxID_ANY, _("Time remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextTimeRemainingLabel->Wrap( -1 ); + fgSizer7->Add( m_staticTextTimeRemainingLabel, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextTimeRemaining = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextTimeRemaining->Wrap( -1 ); + m_staticTextTimeRemaining->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + fgSizer7->Add( m_staticTextTimeRemaining, 0, wxALIGN_BOTTOM, 5 ); + + wxStaticText* m_staticText37; + m_staticText37 = new wxStaticText( m_panelStatistics, wxID_ANY, _("Time elapsed:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText37->Wrap( -1 ); + fgSizer7->Add( m_staticText37, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextTimeElapsed = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextTimeElapsed->Wrap( -1 ); + m_staticTextTimeElapsed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + fgSizer7->Add( m_staticTextTimeElapsed, 0, wxALIGN_BOTTOM, 5 ); + + + bSizer187->Add( fgSizer7, 0, wxRIGHT|wxLEFT, 5 ); + + + bSizer187->Add( 0, 5, 0, 0, 5 ); + + + m_panelStatistics->SetSizer( bSizer187 ); + m_panelStatistics->Layout(); + bSizer187->Fit( m_panelStatistics ); + bSizer40->Add( m_panelStatistics, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + wxBoxSizer* bSizer181; + bSizer181 = new wxBoxSizer( wxVERTICAL ); + + m_staticTextStatus = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatus->Wrap( -1 ); + bSizer181->Add( m_staticTextStatus, 0, 0, 5 ); + + m_gauge2 = new wxGauge( this, wxID_ANY, 100, wxDefaultPosition, wxSize( -1,14 ), wxGA_HORIZONTAL|wxGA_SMOOTH ); + bSizer181->Add( m_gauge2, 0, wxEXPAND|wxTOP, 5 ); + + m_staticTextSpeed = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextSpeed->Wrap( -1 ); + m_staticTextSpeed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer181->Add( m_staticTextSpeed, 0, wxALIGN_BOTTOM|wxTOP, 5 ); + + + bSizer40->Add( bSizer181, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + this->SetSizer( bSizer40 ); + this->Layout(); + bSizer40->Fit( this ); } CompareProgressDlgGenerated::~CompareProgressDlgGenerated() @@ -2234,320 +2234,320 @@ CompareProgressDlgGenerated::~CompareProgressDlgGenerated() SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) { - bSizerRoot = new wxBoxSizer( wxVERTICAL ); - - bSizer42 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer42->Add( 32, 0, 0, 0, 5 ); - - - bSizer42->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_bitmapStatus = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), 0 ); - bSizer42->Add( m_bitmapStatus, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); - - m_staticTextPhase = new wxStaticText( this, wxID_ANY, _("Synchronizing..."), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextPhase->Wrap( -1 ); - m_staticTextPhase->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer42->Add( m_staticTextPhase, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); - - m_animCtrlSyncing = new wxAnimationCtrl( this, wxID_ANY, wxNullAnimation, wxDefaultPosition, wxSize( 32,32 ), wxAC_DEFAULT_STYLE ); - bSizer42->Add( m_animCtrlSyncing, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); - - - bSizer42->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_bpButtonMinimizeToTray = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), wxBU_AUTODRAW ); - m_bpButtonMinimizeToTray->SetToolTip( _("Minimize to notification area") ); - - bSizer42->Add( m_bpButtonMinimizeToTray, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerRoot->Add( bSizer42, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - - bSizerStatusText = new wxBoxSizer( wxVERTICAL ); - - m_staticTextStatus = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatus->Wrap( -1 ); - bSizerStatusText->Add( m_staticTextStatus, 0, wxEXPAND|wxLEFT, 10 ); - - - bSizerStatusText->Add( 0, 5, 0, 0, 5 ); - - - bSizerRoot->Add( bSizerStatusText, 0, wxEXPAND, 5 ); - - wxStaticLine* m_staticlineHeader; - m_staticlineHeader = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizerRoot->Add( m_staticlineHeader, 0, wxEXPAND, 5 ); - - m_panelProgress = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelProgress->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer173; - bSizer173 = new wxBoxSizer( wxVERTICAL ); - - bSizer171 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer171->Add( 10, 0, 0, 0, 5 ); - - wxBoxSizer* bSizer164; - bSizer164 = new wxBoxSizer( wxVERTICAL ); - - m_panelItemsProcessed = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - m_panelItemsProcessed->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer165; - bSizer165 = new wxBoxSizer( wxVERTICAL ); - - - bSizer165->Add( 0, 5, 0, 0, 5 ); - - wxStaticText* m_staticText96; - m_staticText96 = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("Items processed:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText96->Wrap( -1 ); - bSizer165->Add( m_staticText96, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - wxBoxSizer* bSizer169; - bSizer169 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticTextProcessedObj = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_staticTextProcessedObj->Wrap( -1 ); - m_staticTextProcessedObj->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer169->Add( m_staticTextProcessedObj, 0, wxALIGN_BOTTOM, 5 ); - - m_staticTextDataProcessed = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDataProcessed->Wrap( -1 ); - bSizer169->Add( m_staticTextDataProcessed, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); - - - bSizer165->Add( bSizer169, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer165->Add( 0, 5, 0, 0, 5 ); - - - m_panelItemsProcessed->SetSizer( bSizer165 ); - m_panelItemsProcessed->Layout(); - bSizer165->Fit( m_panelItemsProcessed ); - bSizer164->Add( m_panelItemsProcessed, 0, wxEXPAND|wxTOP, 7 ); - - m_panelItemsRemaining = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - m_panelItemsRemaining->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer166; - bSizer166 = new wxBoxSizer( wxVERTICAL ); - - - bSizer166->Add( 0, 5, 0, 0, 5 ); - - wxStaticText* m_staticText97; - m_staticText97 = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("Items remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText97->Wrap( -1 ); - bSizer166->Add( m_staticText97, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - wxBoxSizer* bSizer170; - bSizer170 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticTextRemainingObj = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_staticTextRemainingObj->Wrap( -1 ); - m_staticTextRemainingObj->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer170->Add( m_staticTextRemainingObj, 0, wxALIGN_BOTTOM, 5 ); - - m_staticTextDataRemaining = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDataRemaining->Wrap( -1 ); - bSizer170->Add( m_staticTextDataRemaining, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); - - - bSizer166->Add( bSizer170, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer166->Add( 0, 5, 0, 0, 5 ); - - - m_panelItemsRemaining->SetSizer( bSizer166 ); - m_panelItemsRemaining->Layout(); - bSizer166->Fit( m_panelItemsRemaining ); - bSizer164->Add( m_panelItemsRemaining, 0, wxTOP|wxEXPAND, 7 ); - - m_panelTimeRemaining = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - m_panelTimeRemaining->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer167; - bSizer167 = new wxBoxSizer( wxVERTICAL ); - - - bSizer167->Add( 0, 5, 0, 0, 5 ); - - wxStaticText* m_staticText98; - m_staticText98 = new wxStaticText( m_panelTimeRemaining, wxID_ANY, _("Time remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText98->Wrap( -1 ); - bSizer167->Add( m_staticText98, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - m_staticTextRemTime = new wxStaticText( m_panelTimeRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextRemTime->Wrap( -1 ); - m_staticTextRemTime->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer167->Add( m_staticTextRemTime, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer167->Add( 0, 5, 0, 0, 5 ); - - - m_panelTimeRemaining->SetSizer( bSizer167 ); - m_panelTimeRemaining->Layout(); - bSizer167->Fit( m_panelTimeRemaining ); - bSizer164->Add( m_panelTimeRemaining, 0, wxTOP|wxEXPAND, 7 ); - - wxPanel* m_panelTimeElapsed; - m_panelTimeElapsed = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - m_panelTimeElapsed->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer168; - bSizer168 = new wxBoxSizer( wxVERTICAL ); - - - bSizer168->Add( 0, 5, 0, 0, 5 ); - - wxStaticText* m_staticText961; - m_staticText961 = new wxStaticText( m_panelTimeElapsed, wxID_ANY, _("Time elapsed:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText961->Wrap( -1 ); - bSizer168->Add( m_staticText961, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - m_staticTextTimeElapsed = new wxStaticText( m_panelTimeElapsed, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextTimeElapsed->Wrap( -1 ); - m_staticTextTimeElapsed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer168->Add( m_staticTextTimeElapsed, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer168->Add( 0, 5, 0, 0, 5 ); - - - m_panelTimeElapsed->SetSizer( bSizer168 ); - m_panelTimeElapsed->Layout(); - bSizer168->Fit( m_panelTimeElapsed ); - bSizer164->Add( m_panelTimeElapsed, 0, wxTOP|wxEXPAND, 7 ); - - - bSizer171->Add( bSizer164, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer171->Add( 10, 0, 0, 0, 5 ); - - wxBoxSizer* bSizer161; - bSizer161 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer175; - bSizer175 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapGraphKeyBytes = new wxStaticBitmap( m_panelProgress, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizer175->Add( m_bitmapGraphKeyBytes, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); - - wxStaticText* m_staticText99; - m_staticText99 = new wxStaticText( m_panelProgress, wxID_ANY, _("Bytes copied:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText99->Wrap( -1 ); - bSizer175->Add( m_staticText99, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer161->Add( bSizer175, 0, 0, 5 ); - - m_panelGraphBytes = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_panelGraphBytes->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer161->Add( m_panelGraphBytes, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 15 ); - - wxBoxSizer* bSizer174; - bSizer174 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapGraphKeyItems = new wxStaticBitmap( m_panelProgress, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizer174->Add( m_bitmapGraphKeyItems, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); - - wxStaticText* m_staticText100; - m_staticText100 = new wxStaticText( m_panelProgress, wxID_ANY, _("Items processed:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText100->Wrap( -1 ); - bSizer174->Add( m_staticText100, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer161->Add( bSizer174, 0, 0, 5 ); - - m_panelGraphItems = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_panelGraphItems->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer161->Add( m_panelGraphItems, 1, wxEXPAND, 15 ); - - - bSizer161->Add( 450, 0, 0, 0, 5 ); - - - bSizer171->Add( bSizer161, 1, wxEXPAND, 5 ); - - - bSizer171->Add( 0, 310, 0, 0, 5 ); - - - bSizer173->Add( bSizer171, 1, wxEXPAND, 5 ); - - - m_panelProgress->SetSizer( bSizer173 ); - m_panelProgress->Layout(); - bSizer173->Fit( m_panelProgress ); - bSizerRoot->Add( m_panelProgress, 1, wxEXPAND, 5 ); - - m_notebookResult = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_FIXEDWIDTH ); - - bSizerRoot->Add( m_notebookResult, 1, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); - - m_staticlineFooter = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizerRoot->Add( m_staticlineFooter, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer160; - bSizer160 = new wxBoxSizer( wxHORIZONTAL ); - - bSizerOnCompletion = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText87 = new wxStaticText( this, wxID_ANY, _("On completion:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText87->Wrap( -1 ); - bSizerOnCompletion->Add( m_staticText87, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_comboBoxOnCompletion = new OnCompletionBox( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizerOnCompletion->Add( m_comboBoxOnCompletion, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer160->Add( bSizerOnCompletion, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer160->Add( 0, 0, 0, 0, 5 ); - - - bSizerStdButtons->Add( bSizer160, 1, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); - - m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonClose->SetDefault(); - m_buttonClose->Enable( false ); - - bSizerStdButtons->Add( m_buttonClose, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonPause = new wxButton( this, wxID_ANY, _("&Pause"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonPause, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - m_buttonStop = new wxButton( this, wxID_CANCEL, _("Stop"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonStop, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizerRoot->Add( bSizerStdButtons, 0, wxALIGN_RIGHT|wxEXPAND, 5 ); - - - this->SetSizer( bSizerRoot ); - this->Layout(); - bSizerRoot->Fit( this ); + bSizerRoot = new wxBoxSizer( wxVERTICAL ); + + bSizer42 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer42->Add( 32, 0, 0, 0, 5 ); + + + bSizer42->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bitmapStatus = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), 0 ); + bSizer42->Add( m_bitmapStatus, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); + + m_staticTextPhase = new wxStaticText( this, wxID_ANY, _("Synchronizing..."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextPhase->Wrap( -1 ); + m_staticTextPhase->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer42->Add( m_staticTextPhase, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); + + m_animCtrlSyncing = new wxAnimationCtrl( this, wxID_ANY, wxNullAnimation, wxDefaultPosition, wxSize( 32,32 ), wxAC_DEFAULT_STYLE ); + bSizer42->Add( m_animCtrlSyncing, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); + + + bSizer42->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bpButtonMinimizeToTray = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), wxBU_AUTODRAW ); + m_bpButtonMinimizeToTray->SetToolTip( _("Minimize to notification area") ); + + bSizer42->Add( m_bpButtonMinimizeToTray, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerRoot->Add( bSizer42, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT|wxEXPAND, 5 ); + + bSizerStatusText = new wxBoxSizer( wxVERTICAL ); + + m_staticTextStatus = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatus->Wrap( -1 ); + bSizerStatusText->Add( m_staticTextStatus, 0, wxEXPAND|wxLEFT, 10 ); + + + bSizerStatusText->Add( 0, 5, 0, 0, 5 ); + + + bSizerRoot->Add( bSizerStatusText, 0, wxEXPAND, 5 ); + + wxStaticLine* m_staticlineHeader; + m_staticlineHeader = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerRoot->Add( m_staticlineHeader, 0, wxEXPAND, 5 ); + + m_panelProgress = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelProgress->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer173; + bSizer173 = new wxBoxSizer( wxVERTICAL ); + + bSizer171 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer171->Add( 10, 0, 0, 0, 5 ); + + wxBoxSizer* bSizer164; + bSizer164 = new wxBoxSizer( wxVERTICAL ); + + m_panelItemsProcessed = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_panelItemsProcessed->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer165; + bSizer165 = new wxBoxSizer( wxVERTICAL ); + + + bSizer165->Add( 0, 5, 0, 0, 5 ); + + wxStaticText* m_staticText96; + m_staticText96 = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("Items processed:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText96->Wrap( -1 ); + bSizer165->Add( m_staticText96, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizer169; + bSizer169 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticTextProcessedObj = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_staticTextProcessedObj->Wrap( -1 ); + m_staticTextProcessedObj->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer169->Add( m_staticTextProcessedObj, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextDataProcessed = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDataProcessed->Wrap( -1 ); + bSizer169->Add( m_staticTextDataProcessed, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); + + + bSizer165->Add( bSizer169, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer165->Add( 0, 5, 0, 0, 5 ); + + + m_panelItemsProcessed->SetSizer( bSizer165 ); + m_panelItemsProcessed->Layout(); + bSizer165->Fit( m_panelItemsProcessed ); + bSizer164->Add( m_panelItemsProcessed, 0, wxEXPAND|wxTOP, 7 ); + + m_panelItemsRemaining = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_panelItemsRemaining->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer166; + bSizer166 = new wxBoxSizer( wxVERTICAL ); + + + bSizer166->Add( 0, 5, 0, 0, 5 ); + + wxStaticText* m_staticText97; + m_staticText97 = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("Items remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText97->Wrap( -1 ); + bSizer166->Add( m_staticText97, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizer170; + bSizer170 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticTextRemainingObj = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_staticTextRemainingObj->Wrap( -1 ); + m_staticTextRemainingObj->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer170->Add( m_staticTextRemainingObj, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextDataRemaining = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDataRemaining->Wrap( -1 ); + bSizer170->Add( m_staticTextDataRemaining, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); + + + bSizer166->Add( bSizer170, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer166->Add( 0, 5, 0, 0, 5 ); + + + m_panelItemsRemaining->SetSizer( bSizer166 ); + m_panelItemsRemaining->Layout(); + bSizer166->Fit( m_panelItemsRemaining ); + bSizer164->Add( m_panelItemsRemaining, 0, wxTOP|wxEXPAND, 7 ); + + m_panelTimeRemaining = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_panelTimeRemaining->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer167; + bSizer167 = new wxBoxSizer( wxVERTICAL ); + + + bSizer167->Add( 0, 5, 0, 0, 5 ); + + wxStaticText* m_staticText98; + m_staticText98 = new wxStaticText( m_panelTimeRemaining, wxID_ANY, _("Time remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText98->Wrap( -1 ); + bSizer167->Add( m_staticText98, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_staticTextRemTime = new wxStaticText( m_panelTimeRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextRemTime->Wrap( -1 ); + m_staticTextRemTime->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer167->Add( m_staticTextRemTime, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer167->Add( 0, 5, 0, 0, 5 ); + + + m_panelTimeRemaining->SetSizer( bSizer167 ); + m_panelTimeRemaining->Layout(); + bSizer167->Fit( m_panelTimeRemaining ); + bSizer164->Add( m_panelTimeRemaining, 0, wxTOP|wxEXPAND, 7 ); + + wxPanel* m_panelTimeElapsed; + m_panelTimeElapsed = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_panelTimeElapsed->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer168; + bSizer168 = new wxBoxSizer( wxVERTICAL ); + + + bSizer168->Add( 0, 5, 0, 0, 5 ); + + wxStaticText* m_staticText961; + m_staticText961 = new wxStaticText( m_panelTimeElapsed, wxID_ANY, _("Time elapsed:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText961->Wrap( -1 ); + bSizer168->Add( m_staticText961, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_staticTextTimeElapsed = new wxStaticText( m_panelTimeElapsed, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextTimeElapsed->Wrap( -1 ); + m_staticTextTimeElapsed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer168->Add( m_staticTextTimeElapsed, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer168->Add( 0, 5, 0, 0, 5 ); + + + m_panelTimeElapsed->SetSizer( bSizer168 ); + m_panelTimeElapsed->Layout(); + bSizer168->Fit( m_panelTimeElapsed ); + bSizer164->Add( m_panelTimeElapsed, 0, wxTOP|wxEXPAND, 7 ); + + + bSizer171->Add( bSizer164, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer171->Add( 10, 0, 0, 0, 5 ); + + wxBoxSizer* bSizer161; + bSizer161 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer175; + bSizer175 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapGraphKeyBytes = new wxStaticBitmap( m_panelProgress, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer175->Add( m_bitmapGraphKeyBytes, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); + + wxStaticText* m_staticText99; + m_staticText99 = new wxStaticText( m_panelProgress, wxID_ANY, _("Bytes copied:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText99->Wrap( -1 ); + bSizer175->Add( m_staticText99, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer161->Add( bSizer175, 0, 0, 5 ); + + m_panelGraphBytes = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_panelGraphBytes->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer161->Add( m_panelGraphBytes, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 15 ); + + wxBoxSizer* bSizer174; + bSizer174 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapGraphKeyItems = new wxStaticBitmap( m_panelProgress, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer174->Add( m_bitmapGraphKeyItems, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); + + wxStaticText* m_staticText100; + m_staticText100 = new wxStaticText( m_panelProgress, wxID_ANY, _("Items processed:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText100->Wrap( -1 ); + bSizer174->Add( m_staticText100, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer161->Add( bSizer174, 0, 0, 5 ); + + m_panelGraphItems = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_panelGraphItems->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer161->Add( m_panelGraphItems, 1, wxEXPAND, 15 ); + + + bSizer161->Add( 450, 0, 0, 0, 5 ); + + + bSizer171->Add( bSizer161, 1, wxEXPAND, 5 ); + + + bSizer171->Add( 0, 310, 0, 0, 5 ); + + + bSizer173->Add( bSizer171, 1, wxEXPAND, 5 ); + + + m_panelProgress->SetSizer( bSizer173 ); + m_panelProgress->Layout(); + bSizer173->Fit( m_panelProgress ); + bSizerRoot->Add( m_panelProgress, 1, wxEXPAND, 5 ); + + m_notebookResult = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_FIXEDWIDTH ); + + bSizerRoot->Add( m_notebookResult, 1, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_staticlineFooter = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerRoot->Add( m_staticlineFooter, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer160; + bSizer160 = new wxBoxSizer( wxHORIZONTAL ); + + bSizerOnCompletion = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText87 = new wxStaticText( this, wxID_ANY, _("On completion:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText87->Wrap( -1 ); + bSizerOnCompletion->Add( m_staticText87, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_comboBoxOnCompletion = new OnCompletionBox( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizerOnCompletion->Add( m_comboBoxOnCompletion, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer160->Add( bSizerOnCompletion, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer160->Add( 0, 0, 0, 0, 5 ); + + + bSizerStdButtons->Add( bSizer160, 1, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); + + m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonClose->SetDefault(); + m_buttonClose->Enable( false ); + + bSizerStdButtons->Add( m_buttonClose, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonPause = new wxButton( this, wxID_ANY, _("&Pause"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizerStdButtons->Add( m_buttonPause, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + m_buttonStop = new wxButton( this, wxID_CANCEL, _("Stop"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizerStdButtons->Add( m_buttonStop, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizerRoot->Add( bSizerStdButtons, 0, wxALIGN_RIGHT|wxEXPAND, 5 ); + + + this->SetSizer( bSizerRoot ); + this->Layout(); + bSizerRoot->Fit( this ); } SyncProgressPanelGenerated::~SyncProgressPanelGenerated() @@ -2556,48 +2556,48 @@ SyncProgressPanelGenerated::~SyncProgressPanelGenerated() LogPanelGenerated::LogPanelGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) { - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer179; - bSizer179 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer153; - bSizer153 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer154; - bSizer154 = new wxBoxSizer( wxVERTICAL ); - - m_bpButtonErrors = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); - bSizer154->Add( m_bpButtonErrors, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonWarnings = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); - bSizer154->Add( m_bpButtonWarnings, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonInfo = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); - bSizer154->Add( m_bpButtonInfo, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer153->Add( bSizer154, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - - m_staticline13 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer153->Add( m_staticline13, 0, wxEXPAND, 5 ); - - m_gridMessages = new zen::Grid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); - m_gridMessages->SetScrollRate( 5, 5 ); - bSizer153->Add( m_gridMessages, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer179->Add( bSizer153, 1, wxEXPAND, 5 ); - - - this->SetSizer( bSizer179 ); - this->Layout(); - bSizer179->Fit( this ); - - // Connect Events - m_bpButtonErrors->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( LogPanelGenerated::OnErrors ), NULL, this ); - m_bpButtonWarnings->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( LogPanelGenerated::OnWarnings ), NULL, this ); - m_bpButtonInfo->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( LogPanelGenerated::OnInfo ), NULL, this ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer179; + bSizer179 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer153; + bSizer153 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer154; + bSizer154 = new wxBoxSizer( wxVERTICAL ); + + m_bpButtonErrors = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); + bSizer154->Add( m_bpButtonErrors, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonWarnings = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); + bSizer154->Add( m_bpButtonWarnings, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonInfo = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); + bSizer154->Add( m_bpButtonInfo, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer153->Add( bSizer154, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + m_staticline13 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer153->Add( m_staticline13, 0, wxEXPAND, 5 ); + + m_gridMessages = new zen::Grid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); + m_gridMessages->SetScrollRate( 5, 5 ); + bSizer153->Add( m_gridMessages, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer179->Add( bSizer153, 1, wxEXPAND, 5 ); + + + this->SetSizer( bSizer179 ); + this->Layout(); + bSizer179->Fit( this ); + + // Connect Events + m_bpButtonErrors->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( LogPanelGenerated::OnErrors ), NULL, this ); + m_bpButtonWarnings->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( LogPanelGenerated::OnWarnings ), NULL, this ); + m_bpButtonInfo->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( LogPanelGenerated::OnInfo ), NULL, this ); } LogPanelGenerated::~LogPanelGenerated() @@ -2606,184 +2606,184 @@ LogPanelGenerated::~LogPanelGenerated() BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer54; - bSizer54 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer72; - bSizer72 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapBatchJob = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer72->Add( m_bitmapBatchJob, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); - - m_staticTextDescr = new wxStaticText( this, wxID_ANY, _("Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDescr->Wrap( 520 ); - bSizer72->Add( m_staticTextDescr, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); - - - bSizer54->Add( bSizer72, 0, 0, 5 ); - - m_staticline18 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer54->Add( m_staticline18, 0, wxEXPAND, 5 ); - - m_panel35 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel35->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer172; - bSizer172 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer180; - bSizer180 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer171; - bSizer171 = new wxBoxSizer( wxVERTICAL ); - - m_staticText82 = new wxStaticText( m_panel35, wxID_ANY, _("Handle errors:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText82->Wrap( -1 ); - bSizer171->Add( m_staticText82, 0, wxALL, 5 ); - - wxBoxSizer* bSizer169; - bSizer169 = new wxBoxSizer( wxHORIZONTAL ); - - m_toggleBtnErrorIgnore = new wxToggleButton( m_panel35, wxID_ANY, _("&Ignore"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnErrorIgnore->SetToolTip( _("Hide all error and warning messages") ); - - bSizer169->Add( m_toggleBtnErrorIgnore, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); - - m_toggleBtnErrorPopup = new wxToggleButton( m_panel35, wxID_ANY, _("&Pop-up"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnErrorPopup->SetToolTip( _("Show pop-up on errors or warnings") ); - - bSizer169->Add( m_toggleBtnErrorPopup, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_toggleBtnErrorStop = new wxToggleButton( m_panel35, wxID_ANY, _("&Stop"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnErrorStop->SetToolTip( _("Stop synchronization at first error") ); - - bSizer169->Add( m_toggleBtnErrorStop, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer171->Add( bSizer169, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer180->Add( bSizer171, 0, wxALL, 5 ); - - m_staticline26 = new wxStaticLine( m_panel35, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer180->Add( m_staticline26, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer170; - bSizer170 = new wxBoxSizer( wxVERTICAL ); - - m_checkBoxRunMinimized = new wxCheckBox( m_panel35, wxID_ANY, _("Run minimized"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer170->Add( m_checkBoxRunMinimized, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALL, 5 ); - - wxBoxSizer* bSizer179; - bSizer179 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText81 = new wxStaticText( m_panel35, wxID_ANY, _("On completion:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText81->Wrap( -1 ); - bSizer179->Add( m_staticText81, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_comboBoxOnCompletion = new OnCompletionBox( m_panel35, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer179->Add( m_comboBoxOnCompletion, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer170->Add( bSizer179, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer180->Add( bSizer170, 1, wxALL, 5 ); - - - bSizer172->Add( bSizer180, 0, wxEXPAND, 5 ); - - m_staticline25 = new wxStaticLine( m_panel35, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer172->Add( m_staticline25, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer191; - bSizer191 = new wxBoxSizer( wxVERTICAL ); - - m_checkBoxGenerateLogfile = new wxCheckBox( m_panel35, wxID_ANY, _("Save log:"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer191->Add( m_checkBoxGenerateLogfile, 0, wxEXPAND|wxALL, 5 ); - - m_panelLogfile = new wxPanel( m_panel35, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelLogfile->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer1721; - bSizer1721 = new wxBoxSizer( wxHORIZONTAL ); - - m_logfileDir = new FolderHistoryBox( m_panelLogfile, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer1721->Add( m_logfileDir, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectLogfileDir = new wxButton( m_panelLogfile, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectLogfileDir->SetToolTip( _("Select a folder") ); - - bSizer1721->Add( m_buttonSelectLogfileDir, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_checkBoxLogfilesLimit = new wxCheckBox( m_panelLogfile, wxID_ANY, _("Limit:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_checkBoxLogfilesLimit->SetToolTip( _("Limit maximum number of log files") ); - - bSizer1721->Add( m_checkBoxLogfilesLimit, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_spinCtrlLogfileLimit = new wxSpinCtrl( m_panelLogfile, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70,-1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); - m_spinCtrlLogfileLimit->SetToolTip( _("Limit maximum number of log files") ); - - bSizer1721->Add( m_spinCtrlLogfileLimit, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - m_panelLogfile->SetSizer( bSizer1721 ); - m_panelLogfile->Layout(); - bSizer1721->Fit( m_panelLogfile ); - bSizer191->Add( m_panelLogfile, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer172->Add( bSizer191, 0, wxEXPAND|wxALL, 5 ); - - m_hyperlink17 = new wxHyperlinkCtrl( m_panel35, wxID_ANY, _("How can I schedule a batch job?"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - bSizer172->Add( m_hyperlink17, 0, wxALIGN_CENTER_VERTICAL|wxALL, 10 ); - - - m_panel35->SetSizer( bSizer172 ); - m_panel35->Layout(); - bSizer172->Fit( m_panel35 ); - bSizer54->Add( m_panel35, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - m_staticline13 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer54->Add( m_staticline13, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - - bSizerStdButtons->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_buttonSaveAs = new wxButton( this, wxID_SAVE, _("Save &as..."), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonSaveAs->SetDefault(); - m_buttonSaveAs->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerStdButtons->Add( m_buttonSaveAs, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer54->Add( bSizerStdButtons, 0, wxALIGN_RIGHT|wxEXPAND, 5 ); - - - this->SetSizer( bSizer54 ); - this->Layout(); - bSizer54->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( BatchDlgGenerated::OnClose ) ); - m_toggleBtnErrorIgnore->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnErrorIgnore ), NULL, this ); - m_toggleBtnErrorPopup->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnErrorPopup ), NULL, this ); - m_toggleBtnErrorStop->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnErrorStop ), NULL, this ); - m_checkBoxGenerateLogfile->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnToggleGenerateLogfile ), NULL, this ); - m_checkBoxLogfilesLimit->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnToggleLogfilesLimit ), NULL, this ); - m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( BatchDlgGenerated::OnHelpScheduleBatch ), NULL, this ); - m_buttonSaveAs->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnSaveBatchJob ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnCancel ), NULL, this ); + this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer54; + bSizer54 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer72; + bSizer72 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapBatchJob = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer72->Add( m_bitmapBatchJob, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); + + m_staticTextDescr = new wxStaticText( this, wxID_ANY, _("Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDescr->Wrap( 520 ); + bSizer72->Add( m_staticTextDescr, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + + + bSizer54->Add( bSizer72, 0, 0, 5 ); + + m_staticline18 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer54->Add( m_staticline18, 0, wxEXPAND, 5 ); + + m_panel35 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel35->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer172; + bSizer172 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer180; + bSizer180 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer171; + bSizer171 = new wxBoxSizer( wxVERTICAL ); + + m_staticText82 = new wxStaticText( m_panel35, wxID_ANY, _("Handle errors:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText82->Wrap( -1 ); + bSizer171->Add( m_staticText82, 0, wxALL, 5 ); + + wxBoxSizer* bSizer169; + bSizer169 = new wxBoxSizer( wxHORIZONTAL ); + + m_toggleBtnErrorIgnore = new wxToggleButton( m_panel35, wxID_ANY, _("&Ignore"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnErrorIgnore->SetToolTip( _("Hide all error and warning messages") ); + + bSizer169->Add( m_toggleBtnErrorIgnore, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); + + m_toggleBtnErrorPopup = new wxToggleButton( m_panel35, wxID_ANY, _("&Pop-up"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnErrorPopup->SetToolTip( _("Show pop-up on errors or warnings") ); + + bSizer169->Add( m_toggleBtnErrorPopup, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_toggleBtnErrorStop = new wxToggleButton( m_panel35, wxID_ANY, _("&Stop"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnErrorStop->SetToolTip( _("Stop synchronization at first error") ); + + bSizer169->Add( m_toggleBtnErrorStop, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer171->Add( bSizer169, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer180->Add( bSizer171, 0, wxALL, 5 ); + + m_staticline26 = new wxStaticLine( m_panel35, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer180->Add( m_staticline26, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer170; + bSizer170 = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxRunMinimized = new wxCheckBox( m_panel35, wxID_ANY, _("Run minimized"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer170->Add( m_checkBoxRunMinimized, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALL, 5 ); + + wxBoxSizer* bSizer179; + bSizer179 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText81 = new wxStaticText( m_panel35, wxID_ANY, _("On completion:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText81->Wrap( -1 ); + bSizer179->Add( m_staticText81, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_comboBoxOnCompletion = new OnCompletionBox( m_panel35, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer179->Add( m_comboBoxOnCompletion, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer170->Add( bSizer179, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer180->Add( bSizer170, 1, wxALL, 5 ); + + + bSizer172->Add( bSizer180, 0, wxEXPAND, 5 ); + + m_staticline25 = new wxStaticLine( m_panel35, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer172->Add( m_staticline25, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer191; + bSizer191 = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxGenerateLogfile = new wxCheckBox( m_panel35, wxID_ANY, _("Save log:"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer191->Add( m_checkBoxGenerateLogfile, 0, wxEXPAND|wxALL, 5 ); + + m_panelLogfile = new wxPanel( m_panel35, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelLogfile->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer1721; + bSizer1721 = new wxBoxSizer( wxHORIZONTAL ); + + m_logfileDir = new FolderHistoryBox( m_panelLogfile, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer1721->Add( m_logfileDir, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectLogfileDir = new wxButton( m_panelLogfile, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectLogfileDir->SetToolTip( _("Select a folder") ); + + bSizer1721->Add( m_buttonSelectLogfileDir, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_checkBoxLogfilesLimit = new wxCheckBox( m_panelLogfile, wxID_ANY, _("Limit:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxLogfilesLimit->SetToolTip( _("Limit maximum number of log files") ); + + bSizer1721->Add( m_checkBoxLogfilesLimit, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_spinCtrlLogfileLimit = new wxSpinCtrl( m_panelLogfile, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70,-1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); + m_spinCtrlLogfileLimit->SetToolTip( _("Limit maximum number of log files") ); + + bSizer1721->Add( m_spinCtrlLogfileLimit, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + m_panelLogfile->SetSizer( bSizer1721 ); + m_panelLogfile->Layout(); + bSizer1721->Fit( m_panelLogfile ); + bSizer191->Add( m_panelLogfile, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer172->Add( bSizer191, 0, wxEXPAND|wxALL, 5 ); + + m_hyperlink17 = new wxHyperlinkCtrl( m_panel35, wxID_ANY, _("How can I schedule a batch job?"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + bSizer172->Add( m_hyperlink17, 0, wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + + + m_panel35->SetSizer( bSizer172 ); + m_panel35->Layout(); + bSizer172->Fit( m_panel35 ); + bSizer54->Add( m_panel35, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_staticline13 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer54->Add( m_staticline13, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + + bSizerStdButtons->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_buttonSaveAs = new wxButton( this, wxID_SAVE, _("Save &as..."), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonSaveAs->SetDefault(); + m_buttonSaveAs->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerStdButtons->Add( m_buttonSaveAs, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer54->Add( bSizerStdButtons, 0, wxALIGN_RIGHT|wxEXPAND, 5 ); + + + this->SetSizer( bSizer54 ); + this->Layout(); + bSizer54->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( BatchDlgGenerated::OnClose ) ); + m_toggleBtnErrorIgnore->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnErrorIgnore ), NULL, this ); + m_toggleBtnErrorPopup->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnErrorPopup ), NULL, this ); + m_toggleBtnErrorStop->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnErrorStop ), NULL, this ); + m_checkBoxGenerateLogfile->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnToggleGenerateLogfile ), NULL, this ); + m_checkBoxLogfilesLimit->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnToggleLogfilesLimit ), NULL, this ); + m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( BatchDlgGenerated::OnHelpScheduleBatch ), NULL, this ); + m_buttonSaveAs->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnSaveBatchJob ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnCancel ), NULL, this ); } BatchDlgGenerated::~BatchDlgGenerated() @@ -2792,81 +2792,81 @@ BatchDlgGenerated::~BatchDlgGenerated() DeleteDlgGenerated::DeleteDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer24; - bSizer24 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer72; - bSizer72 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapDeleteType = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizer72->Add( m_bitmapDeleteType, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); - - m_staticTextHeader = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0|wxNO_BORDER ); - m_staticTextHeader->Wrap( -1 ); - bSizer72->Add( m_staticTextHeader, 0, wxALIGN_CENTER_VERTICAL|wxALL, 10 ); - - - bSizer24->Add( bSizer72, 0, 0, 5 ); - - m_staticline91 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer24->Add( m_staticline91, 0, wxEXPAND, 5 ); - - m_panel31 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel31->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer185; - bSizer185 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer185->Add( 60, 0, 0, 0, 5 ); - - m_staticline42 = new wxStaticLine( m_panel31, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer185->Add( m_staticline42, 0, wxEXPAND, 5 ); - - m_textCtrlFileList = new wxTextCtrl( m_panel31, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 500,200 ), wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); - bSizer185->Add( m_textCtrlFileList, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - m_panel31->SetSizer( bSizer185 ); - m_panel31->Layout(); - bSizer185->Fit( m_panel31 ); - bSizer24->Add( m_panel31, 1, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_staticline9 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer24->Add( m_staticline9, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_checkBoxUseRecycler = new wxCheckBox( this, wxID_ANY, _("&Recycle bin"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizerStdButtons->Add( m_checkBoxUseRecycler, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonOK = new wxButton( this, wxID_OK, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonOK->SetDefault(); - m_buttonOK->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerStdButtons->Add( m_buttonOK, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer24->Add( bSizerStdButtons, 0, wxEXPAND, 5 ); - - - this->SetSizer( bSizer24 ); - this->Layout(); - bSizer24->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DeleteDlgGenerated::OnClose ) ); - m_checkBoxUseRecycler->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnUseRecycler ), NULL, this ); - m_buttonOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnOK ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnCancel ), NULL, this ); + this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer24; + bSizer24 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer72; + bSizer72 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapDeleteType = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer72->Add( m_bitmapDeleteType, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); + + m_staticTextHeader = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0|wxNO_BORDER ); + m_staticTextHeader->Wrap( -1 ); + bSizer72->Add( m_staticTextHeader, 0, wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + + + bSizer24->Add( bSizer72, 0, 0, 5 ); + + m_staticline91 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer24->Add( m_staticline91, 0, wxEXPAND, 5 ); + + m_panel31 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel31->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer185; + bSizer185 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer185->Add( 60, 0, 0, 0, 5 ); + + m_staticline42 = new wxStaticLine( m_panel31, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer185->Add( m_staticline42, 0, wxEXPAND, 5 ); + + m_textCtrlFileList = new wxTextCtrl( m_panel31, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 500,200 ), wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); + bSizer185->Add( m_textCtrlFileList, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + m_panel31->SetSizer( bSizer185 ); + m_panel31->Layout(); + bSizer185->Fit( m_panel31 ); + bSizer24->Add( m_panel31, 1, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_staticline9 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer24->Add( m_staticline9, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_checkBoxUseRecycler = new wxCheckBox( this, wxID_ANY, _("&Recycle bin"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerStdButtons->Add( m_checkBoxUseRecycler, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonOK = new wxButton( this, wxID_OK, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonOK->SetDefault(); + m_buttonOK->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerStdButtons->Add( m_buttonOK, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer24->Add( bSizerStdButtons, 0, wxEXPAND, 5 ); + + + this->SetSizer( bSizer24 ); + this->Layout(); + bSizer24->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DeleteDlgGenerated::OnClose ) ); + m_checkBoxUseRecycler->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnUseRecycler ), NULL, this ); + m_buttonOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnOK ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnCancel ), NULL, this ); } DeleteDlgGenerated::~DeleteDlgGenerated() @@ -2875,258 +2875,258 @@ DeleteDlgGenerated::~DeleteDlgGenerated() OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer95; - bSizer95 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer72; - bSizer72 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapSettings = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer72->Add( m_bitmapSettings, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); - - m_staticText44 = new wxStaticText( this, wxID_ANY, _("The following settings are used for all synchronization jobs."), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_staticText44->Wrap( 500 ); - bSizer72->Add( m_staticText44, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); - - - bSizer95->Add( bSizer72, 0, 0, 5 ); - - m_staticline20 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer95->Add( m_staticline20, 0, wxEXPAND, 5 ); - - m_panel39 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel39->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer166; - bSizer166 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer186; - bSizer186 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer160; - bSizer160 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer176; - bSizer176 = new wxBoxSizer( wxHORIZONTAL ); - - m_checkBoxFailSafe = new wxCheckBox( m_panel39, wxID_ANY, _("Fail-safe file copy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_checkBoxFailSafe->SetValue(true); - m_checkBoxFailSafe->SetToolTip( _("Copy to a temporary file (*.ffs_tmp) before overwriting target.\nThis guarantees a consistent state even in case of a serious error.") ); - - bSizer176->Add( m_checkBoxFailSafe, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticText91 = new wxStaticText( m_panel39, wxID_ANY, _("(recommended)"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText91->Wrap( -1 ); - m_staticText91->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - - bSizer176->Add( m_staticText91, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer160->Add( bSizer176, 0, wxEXPAND, 5 ); - - bSizerLockedFiles = new wxBoxSizer( wxHORIZONTAL ); - - m_checkBoxCopyLocked = new wxCheckBox( m_panel39, wxID_ANY, _("Copy locked files"), wxDefaultPosition, wxDefaultSize, 0 ); - m_checkBoxCopyLocked->SetValue(true); - m_checkBoxCopyLocked->SetToolTip( _("Copy shared or locked files using the Volume Shadow Copy Service.") ); - - bSizerLockedFiles->Add( m_checkBoxCopyLocked, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticText92 = new wxStaticText( m_panel39, wxID_ANY, _("(requires administrator rights)"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText92->Wrap( -1 ); - m_staticText92->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - - bSizerLockedFiles->Add( m_staticText92, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer160->Add( bSizerLockedFiles, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer178; - bSizer178 = new wxBoxSizer( wxHORIZONTAL ); - - m_checkBoxCopyPermissions = new wxCheckBox( m_panel39, wxID_ANY, _("Copy file access permissions"), wxDefaultPosition, wxDefaultSize, 0 ); - m_checkBoxCopyPermissions->SetValue(true); - m_checkBoxCopyPermissions->SetToolTip( _("Transfer file and folder permissions.") ); - - bSizer178->Add( m_checkBoxCopyPermissions, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_staticText93 = new wxStaticText( m_panel39, wxID_ANY, _("(requires administrator rights)"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText93->Wrap( -1 ); - m_staticText93->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - - bSizer178->Add( m_staticText93, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer160->Add( bSizer178, 0, wxEXPAND, 5 ); - - - bSizer186->Add( bSizer160, 0, wxEXPAND|wxALL, 5 ); - - m_staticline39 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer186->Add( m_staticline39, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer188; - bSizer188 = new wxBoxSizer( wxVERTICAL ); - - m_staticText95 = new wxStaticText( m_panel39, wxID_ANY, _("Automatic retry on error:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText95->Wrap( -1 ); - bSizer188->Add( m_staticText95, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM, 5 ); - - wxFlexGridSizer* fgSizer6; - fgSizer6 = new wxFlexGridSizer( 0, 2, 5, 5 ); - fgSizer6->SetFlexibleDirection( wxBOTH ); - fgSizer6->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - - m_staticText96 = new wxStaticText( m_panel39, wxID_ANY, _("Retry count:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText96->Wrap( -1 ); - fgSizer6->Add( m_staticText96, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_spinCtrlAutoRetryCount = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60,-1 ), wxSP_ARROW_KEYS, 0, 2000000000, 4 ); - fgSizer6->Add( m_spinCtrlAutoRetryCount, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextAutoRetryDelay = new wxStaticText( m_panel39, wxID_ANY, _("Delay (in seconds):"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextAutoRetryDelay->Wrap( -1 ); - fgSizer6->Add( m_staticTextAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_spinCtrlAutoRetryDelay = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60,-1 ), wxSP_ARROW_KEYS, 0, 2000000000, 0 ); - fgSizer6->Add( m_spinCtrlAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer188->Add( fgSizer6, 0, wxLEFT, 10 ); - - - bSizer186->Add( bSizer188, 0, wxALL, 10 ); - - - bSizer166->Add( bSizer186, 0, wxEXPAND, 5 ); - - m_staticline191 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer166->Add( m_staticline191, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer181; - bSizer181 = new wxBoxSizer( wxVERTICAL ); - - m_staticText85 = new wxStaticText( m_panel39, wxID_ANY, _("Customize context menu:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText85->Wrap( -1 ); - bSizer181->Add( m_staticText85, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM, 5 ); - - m_gridCustomCommand = new wxGrid( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - - // Grid - m_gridCustomCommand->CreateGrid( 5, 2 ); - m_gridCustomCommand->EnableEditing( true ); - m_gridCustomCommand->EnableGridLines( true ); - m_gridCustomCommand->EnableDragGridSize( false ); - m_gridCustomCommand->SetMargins( 0, 0 ); - - // Columns - m_gridCustomCommand->SetColSize( 0, 165 ); - m_gridCustomCommand->SetColSize( 1, 196 ); - m_gridCustomCommand->EnableDragColMove( false ); - m_gridCustomCommand->EnableDragColSize( true ); - m_gridCustomCommand->SetColLabelSize( 20 ); - m_gridCustomCommand->SetColLabelValue( 0, _("Description") ); - m_gridCustomCommand->SetColLabelValue( 1, _("Command line") ); - m_gridCustomCommand->SetColLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE ); - - // Rows - m_gridCustomCommand->EnableDragRowSize( false ); - m_gridCustomCommand->SetRowLabelSize( 1 ); - m_gridCustomCommand->SetRowLabelAlignment( wxALIGN_LEFT, wxALIGN_CENTRE ); - - // Label Appearance - - // Cell Defaults - m_gridCustomCommand->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_TOP ); - bSizer181->Add( m_gridCustomCommand, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - wxBoxSizer* bSizer193; - bSizer193 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonAddRow = new wxBitmapButton( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer193->Add( m_bpButtonAddRow, 0, 0, 5 ); - - m_bpButtonRemoveRow = new wxBitmapButton( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer193->Add( m_bpButtonRemoveRow, 0, 0, 5 ); - - - bSizer193->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_hyperlink17 = new wxHyperlinkCtrl( m_panel39, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - bSizer193->Add( m_hyperlink17, 0, wxLEFT, 5 ); - - - bSizer181->Add( bSizer193, 0, wxTOP|wxEXPAND, 5 ); - - - bSizer166->Add( bSizer181, 1, wxEXPAND|wxALL, 10 ); - - m_staticline192 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer166->Add( m_staticline192, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer1881; - bSizer1881 = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonResetDialogs = new zen::BitmapTextButton( m_panel39, wxID_ANY, _("Show hidden dialogs again"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonResetDialogs->SetToolTip( _("Show all permanently hidden dialogs and warning messages again") ); - - bSizer1881->Add( m_buttonResetDialogs, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); - - m_staticline40 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer1881->Add( m_staticline40, 0, wxEXPAND, 5 ); - - - bSizer166->Add( bSizer1881, 0, 0, 5 ); - - - m_panel39->SetSizer( bSizer166 ); - m_panel39->Layout(); - bSizer166->Fit( m_panel39 ); - bSizer95->Add( m_panel39, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - m_staticline36 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer95->Add( m_staticline36, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonDefault = new wxButton( this, wxID_DEFAULT, _("&Default"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonDefault, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStdButtons->Add( 0, 0, 1, 0, 5 ); - - m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonOkay->SetDefault(); - m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer95->Add( bSizerStdButtons, 0, wxEXPAND, 5 ); - - - this->SetSizer( bSizer95 ); - this->Layout(); - bSizer95->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( OptionsDlgGenerated::OnClose ) ); - m_spinCtrlAutoRetryCount->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( OptionsDlgGenerated::OnToggleAutoRetryCount ), NULL, this ); - m_bpButtonAddRow->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::OnAddRow ), NULL, this ); - m_bpButtonRemoveRow->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::OnRemoveRow ), NULL, this ); - m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( OptionsDlgGenerated::OnHelpShowExamples ), NULL, this ); - m_buttonResetDialogs->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::OnResetDialogs ), NULL, this ); - m_buttonDefault->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::OnDefault ), NULL, this ); - m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::OnOkay ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::OnCancel ), NULL, this ); + this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer95; + bSizer95 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer72; + bSizer72 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapSettings = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer72->Add( m_bitmapSettings, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); + + m_staticText44 = new wxStaticText( this, wxID_ANY, _("The following settings are used for all synchronization jobs."), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_staticText44->Wrap( 500 ); + bSizer72->Add( m_staticText44, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + + + bSizer95->Add( bSizer72, 0, 0, 5 ); + + m_staticline20 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer95->Add( m_staticline20, 0, wxEXPAND, 5 ); + + m_panel39 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel39->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer166; + bSizer166 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer186; + bSizer186 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer160; + bSizer160 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer176; + bSizer176 = new wxBoxSizer( wxHORIZONTAL ); + + m_checkBoxFailSafe = new wxCheckBox( m_panel39, wxID_ANY, _("Fail-safe file copy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxFailSafe->SetValue(true); + m_checkBoxFailSafe->SetToolTip( _("Copy to a temporary file (*.ffs_tmp) before overwriting target.\nThis guarantees a consistent state even in case of a serious error.") ); + + bSizer176->Add( m_checkBoxFailSafe, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText91 = new wxStaticText( m_panel39, wxID_ANY, _("(recommended)"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText91->Wrap( -1 ); + m_staticText91->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizer176->Add( m_staticText91, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer160->Add( bSizer176, 0, wxEXPAND, 5 ); + + bSizerLockedFiles = new wxBoxSizer( wxHORIZONTAL ); + + m_checkBoxCopyLocked = new wxCheckBox( m_panel39, wxID_ANY, _("Copy locked files"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxCopyLocked->SetValue(true); + m_checkBoxCopyLocked->SetToolTip( _("Copy shared or locked files using the Volume Shadow Copy Service.") ); + + bSizerLockedFiles->Add( m_checkBoxCopyLocked, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText92 = new wxStaticText( m_panel39, wxID_ANY, _("(requires administrator rights)"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText92->Wrap( -1 ); + m_staticText92->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizerLockedFiles->Add( m_staticText92, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer160->Add( bSizerLockedFiles, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer178; + bSizer178 = new wxBoxSizer( wxHORIZONTAL ); + + m_checkBoxCopyPermissions = new wxCheckBox( m_panel39, wxID_ANY, _("Copy file access permissions"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxCopyPermissions->SetValue(true); + m_checkBoxCopyPermissions->SetToolTip( _("Transfer file and folder permissions.") ); + + bSizer178->Add( m_checkBoxCopyPermissions, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_staticText93 = new wxStaticText( m_panel39, wxID_ANY, _("(requires administrator rights)"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText93->Wrap( -1 ); + m_staticText93->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizer178->Add( m_staticText93, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer160->Add( bSizer178, 0, wxEXPAND, 5 ); + + + bSizer186->Add( bSizer160, 0, wxEXPAND|wxALL, 5 ); + + m_staticline39 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer186->Add( m_staticline39, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer188; + bSizer188 = new wxBoxSizer( wxVERTICAL ); + + m_staticText95 = new wxStaticText( m_panel39, wxID_ANY, _("Automatic retry on error:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText95->Wrap( -1 ); + bSizer188->Add( m_staticText95, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM, 5 ); + + wxFlexGridSizer* fgSizer6; + fgSizer6 = new wxFlexGridSizer( 0, 2, 5, 5 ); + fgSizer6->SetFlexibleDirection( wxBOTH ); + fgSizer6->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_staticText96 = new wxStaticText( m_panel39, wxID_ANY, _("Retry count:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText96->Wrap( -1 ); + fgSizer6->Add( m_staticText96, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_spinCtrlAutoRetryCount = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60,-1 ), wxSP_ARROW_KEYS, 0, 2000000000, 4 ); + fgSizer6->Add( m_spinCtrlAutoRetryCount, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextAutoRetryDelay = new wxStaticText( m_panel39, wxID_ANY, _("Delay (in seconds):"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextAutoRetryDelay->Wrap( -1 ); + fgSizer6->Add( m_staticTextAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_spinCtrlAutoRetryDelay = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60,-1 ), wxSP_ARROW_KEYS, 0, 2000000000, 0 ); + fgSizer6->Add( m_spinCtrlAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer188->Add( fgSizer6, 0, wxLEFT, 10 ); + + + bSizer186->Add( bSizer188, 0, wxALL, 10 ); + + + bSizer166->Add( bSizer186, 0, wxEXPAND, 5 ); + + m_staticline191 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer166->Add( m_staticline191, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer181; + bSizer181 = new wxBoxSizer( wxVERTICAL ); + + m_staticText85 = new wxStaticText( m_panel39, wxID_ANY, _("Customize context menu:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText85->Wrap( -1 ); + bSizer181->Add( m_staticText85, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM, 5 ); + + m_gridCustomCommand = new wxGrid( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + + // Grid + m_gridCustomCommand->CreateGrid( 5, 2 ); + m_gridCustomCommand->EnableEditing( true ); + m_gridCustomCommand->EnableGridLines( true ); + m_gridCustomCommand->EnableDragGridSize( false ); + m_gridCustomCommand->SetMargins( 0, 0 ); + + // Columns + m_gridCustomCommand->SetColSize( 0, 165 ); + m_gridCustomCommand->SetColSize( 1, 196 ); + m_gridCustomCommand->EnableDragColMove( false ); + m_gridCustomCommand->EnableDragColSize( true ); + m_gridCustomCommand->SetColLabelSize( 20 ); + m_gridCustomCommand->SetColLabelValue( 0, _("Description") ); + m_gridCustomCommand->SetColLabelValue( 1, _("Command line") ); + m_gridCustomCommand->SetColLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE ); + + // Rows + m_gridCustomCommand->EnableDragRowSize( false ); + m_gridCustomCommand->SetRowLabelSize( 1 ); + m_gridCustomCommand->SetRowLabelAlignment( wxALIGN_LEFT, wxALIGN_CENTRE ); + + // Label Appearance + + // Cell Defaults + m_gridCustomCommand->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_TOP ); + bSizer181->Add( m_gridCustomCommand, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + wxBoxSizer* bSizer193; + bSizer193 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonAddRow = new wxBitmapButton( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer193->Add( m_bpButtonAddRow, 0, 0, 5 ); + + m_bpButtonRemoveRow = new wxBitmapButton( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer193->Add( m_bpButtonRemoveRow, 0, 0, 5 ); + + + bSizer193->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_hyperlink17 = new wxHyperlinkCtrl( m_panel39, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + bSizer193->Add( m_hyperlink17, 0, wxLEFT, 5 ); + + + bSizer181->Add( bSizer193, 0, wxTOP|wxEXPAND, 5 ); + + + bSizer166->Add( bSizer181, 1, wxEXPAND|wxALL, 10 ); + + m_staticline192 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer166->Add( m_staticline192, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer1881; + bSizer1881 = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonResetDialogs = new zen::BitmapTextButton( m_panel39, wxID_ANY, _("Show hidden dialogs again"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonResetDialogs->SetToolTip( _("Show all permanently hidden dialogs and warning messages again") ); + + bSizer1881->Add( m_buttonResetDialogs, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); + + m_staticline40 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer1881->Add( m_staticline40, 0, wxEXPAND, 5 ); + + + bSizer166->Add( bSizer1881, 0, 0, 5 ); + + + m_panel39->SetSizer( bSizer166 ); + m_panel39->Layout(); + bSizer166->Fit( m_panel39 ); + bSizer95->Add( m_panel39, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_staticline36 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer95->Add( m_staticline36, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonDefault = new wxButton( this, wxID_DEFAULT, _("&Default"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizerStdButtons->Add( m_buttonDefault, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStdButtons->Add( 0, 0, 1, 0, 5 ); + + m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonOkay->SetDefault(); + m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer95->Add( bSizerStdButtons, 0, wxEXPAND, 5 ); + + + this->SetSizer( bSizer95 ); + this->Layout(); + bSizer95->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( OptionsDlgGenerated::OnClose ) ); + m_spinCtrlAutoRetryCount->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( OptionsDlgGenerated::OnToggleAutoRetryCount ), NULL, this ); + m_bpButtonAddRow->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::OnAddRow ), NULL, this ); + m_bpButtonRemoveRow->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::OnRemoveRow ), NULL, this ); + m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( OptionsDlgGenerated::OnHelpShowExamples ), NULL, this ); + m_buttonResetDialogs->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::OnResetDialogs ), NULL, this ); + m_buttonDefault->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::OnDefault ), NULL, this ); + m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::OnOkay ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::OnCancel ), NULL, this ); } OptionsDlgGenerated::~OptionsDlgGenerated() @@ -3135,22 +3135,22 @@ OptionsDlgGenerated::~OptionsDlgGenerated() TooltipDialogGenerated::TooltipDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxDefaultSize, wxDefaultSize ); - - wxBoxSizer* bSizer158; - bSizer158 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapLeft = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizer158->Add( m_bitmapLeft, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextMain = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextMain->Wrap( 600 ); - bSizer158->Add( m_staticTextMain, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - this->SetSizer( bSizer158 ); - this->Layout(); - bSizer158->Fit( this ); + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + + wxBoxSizer* bSizer158; + bSizer158 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapLeft = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer158->Add( m_bitmapLeft, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextMain = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextMain->Wrap( 600 ); + bSizer158->Add( m_staticTextMain, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + this->SetSizer( bSizer158 ); + this->Layout(); + bSizer158->Fit( this ); } TooltipDialogGenerated::~TooltipDialogGenerated() @@ -3159,60 +3159,60 @@ TooltipDialogGenerated::~TooltipDialogGenerated() SelectTimespanDlgGenerated::SelectTimespanDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxDefaultSize, wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer96; - bSizer96 = new wxBoxSizer( wxVERTICAL ); - - m_panel35 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel35->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer98; - bSizer98 = new wxBoxSizer( wxHORIZONTAL ); - - m_calendarFrom = new wxCalendarCtrl( m_panel35, wxID_ANY, wxDefaultDateTime, wxDefaultPosition, wxDefaultSize, wxCAL_SHOW_HOLIDAYS|wxNO_BORDER ); - bSizer98->Add( m_calendarFrom, 0, wxTOP|wxBOTTOM|wxLEFT, 10 ); - - m_calendarTo = new wxCalendarCtrl( m_panel35, wxID_ANY, wxDefaultDateTime, wxDefaultPosition, wxDefaultSize, wxCAL_SHOW_HOLIDAYS|wxNO_BORDER ); - bSizer98->Add( m_calendarTo, 0, wxALL, 10 ); - - - m_panel35->SetSizer( bSizer98 ); - m_panel35->Layout(); - bSizer98->Fit( m_panel35 ); - bSizer96->Add( m_panel35, 0, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_staticline21 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer96->Add( m_staticline21, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonOkay->SetDefault(); - m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer96->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); - - - this->SetSizer( bSizer96 ); - this->Layout(); - bSizer96->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SelectTimespanDlgGenerated::OnClose ) ); - m_calendarFrom->Connect( wxEVT_CALENDAR_SEL_CHANGED, wxCalendarEventHandler( SelectTimespanDlgGenerated::OnChangeSelectionFrom ), NULL, this ); - m_calendarTo->Connect( wxEVT_CALENDAR_SEL_CHANGED, wxCalendarEventHandler( SelectTimespanDlgGenerated::OnChangeSelectionTo ), NULL, this ); - m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SelectTimespanDlgGenerated::OnOkay ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SelectTimespanDlgGenerated::OnCancel ), NULL, this ); + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer96; + bSizer96 = new wxBoxSizer( wxVERTICAL ); + + m_panel35 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel35->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer98; + bSizer98 = new wxBoxSizer( wxHORIZONTAL ); + + m_calendarFrom = new wxCalendarCtrl( m_panel35, wxID_ANY, wxDefaultDateTime, wxDefaultPosition, wxDefaultSize, wxCAL_SHOW_HOLIDAYS|wxNO_BORDER ); + bSizer98->Add( m_calendarFrom, 0, wxTOP|wxBOTTOM|wxLEFT, 10 ); + + m_calendarTo = new wxCalendarCtrl( m_panel35, wxID_ANY, wxDefaultDateTime, wxDefaultPosition, wxDefaultSize, wxCAL_SHOW_HOLIDAYS|wxNO_BORDER ); + bSizer98->Add( m_calendarTo, 0, wxALL, 10 ); + + + m_panel35->SetSizer( bSizer98 ); + m_panel35->Layout(); + bSizer98->Fit( m_panel35 ); + bSizer96->Add( m_panel35, 0, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_staticline21 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer96->Add( m_staticline21, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonOkay->SetDefault(); + m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer96->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + + + this->SetSizer( bSizer96 ); + this->Layout(); + bSizer96->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SelectTimespanDlgGenerated::OnClose ) ); + m_calendarFrom->Connect( wxEVT_CALENDAR_SEL_CHANGED, wxCalendarEventHandler( SelectTimespanDlgGenerated::OnChangeSelectionFrom ), NULL, this ); + m_calendarTo->Connect( wxEVT_CALENDAR_SEL_CHANGED, wxCalendarEventHandler( SelectTimespanDlgGenerated::OnChangeSelectionTo ), NULL, this ); + m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SelectTimespanDlgGenerated::OnOkay ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SelectTimespanDlgGenerated::OnCancel ), NULL, this ); } SelectTimespanDlgGenerated::~SelectTimespanDlgGenerated() @@ -3221,306 +3221,306 @@ SelectTimespanDlgGenerated::~SelectTimespanDlgGenerated() AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxDefaultSize, wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer31; - bSizer31 = new wxBoxSizer( wxVERTICAL ); - - m_panel41 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel41->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer162; - bSizer162 = new wxBoxSizer( wxVERTICAL ); - - m_bitmapLogo = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer162->Add( m_bitmapLogo, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticline341 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer162->Add( m_staticline341, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer174; - bSizer174 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer181; - bSizer181 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer187; - bSizer187 = new wxBoxSizer( wxVERTICAL ); - - m_staticText96 = new wxStaticText( m_panel41, wxID_ANY, _("Source code written in C++ using:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText96->Wrap( -1 ); - bSizer187->Add( m_staticText96, 0, wxALL, 5 ); - - wxBoxSizer* bSizer171; - bSizer171 = new wxBoxSizer( wxHORIZONTAL ); - - m_hyperlink11 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("MS Visual C++"), wxT("http://www.visualstudio.com"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink11->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink11->SetToolTip( _("http://www.visualstudio.com") ); - - bSizer171->Add( m_hyperlink11, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink9 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("MinGW"), wxT("http://www.mingw.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink9->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink9->SetToolTip( _("http://www.mingw.org") ); - - bSizer171->Add( m_hyperlink9, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink10 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Code::Blocks"), wxT("http://www.codeblocks.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink10->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink10->SetToolTip( _("http://www.codeblocks.org") ); - - bSizer171->Add( m_hyperlink10, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink7 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("wxWidgets"), wxT("http://www.wxwidgets.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink7->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink7->SetToolTip( _("http://www.wxwidgets.org") ); - - bSizer171->Add( m_hyperlink7, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink14 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("wxFormBuilder"), wxT("http://wxformbuilder.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink14->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink14->SetToolTip( _("http://wxformbuilder.org") ); - - bSizer171->Add( m_hyperlink14, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer187->Add( bSizer171, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - wxBoxSizer* bSizer172; - bSizer172 = new wxBoxSizer( wxHORIZONTAL ); - - m_hyperlink15 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("zen::Xml"), wxT("http://zenxml.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink15->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink15->SetToolTip( _("http://zenxml.sourceforge.net") ); - - bSizer172->Add( m_hyperlink15, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink13 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Boost"), wxT("http://www.boost.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink13->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink13->SetToolTip( _("http://www.boost.org") ); - - bSizer172->Add( m_hyperlink13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink16 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Artistic Style"), wxT("http://astyle.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink16->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink16->SetToolTip( _("http://astyle.sourceforge.net") ); - - bSizer172->Add( m_hyperlink16, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink12 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Google Test"), wxT("http://code.google.com/p/googletest"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink12->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink12->SetToolTip( _("http://code.google.com/p/googletest") ); - - bSizer172->Add( m_hyperlink12, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink18 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Unicode NSIS"), wxT("http://www.scratchpaper.com"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink18->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink18->SetToolTip( _("http://www.scratchpaper.com") ); - - bSizer172->Add( m_hyperlink18, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer187->Add( bSizer172, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer181->Add( bSizer187, 0, wxALL|wxEXPAND, 5 ); - - m_panelDonate = new wxPanel( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelDonate->SetBackgroundColour( wxColour( 153, 170, 187 ) ); - - wxBoxSizer* bSizer183; - bSizer183 = new wxBoxSizer( wxVERTICAL ); - - m_panel39 = new wxPanel( m_panelDonate, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel39->SetBackgroundColour( wxColour( 221, 221, 255 ) ); - - wxBoxSizer* bSizer184; - bSizer184 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer184->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_animCtrlWink = new wxAnimationCtrl( m_panel39, wxID_ANY, wxNullAnimation, wxDefaultPosition, wxSize( 48,48 ), wxAC_DEFAULT_STYLE ); - bSizer184->Add( m_animCtrlWink, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - wxBoxSizer* bSizer178; - bSizer178 = new wxBoxSizer( wxVERTICAL ); - - m_staticText83 = new wxStaticText( m_panel39, wxID_ANY, _("If you like FreeFileSync"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText83->Wrap( -1 ); - m_staticText83->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - m_staticText83->SetForegroundColour( wxColour( 0, 0, 0 ) ); - - bSizer178->Add( m_staticText83, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_buttonDonate = new wxButton( m_panel39, wxID_ANY, _("Donate with PayPal"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonDonate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) ); - m_buttonDonate->SetToolTip( _("http://www.freefilesync.org/donate.php") ); - - bSizer178->Add( m_buttonDonate, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer184->Add( bSizer178, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer184->Add( 0, 0, 1, wxEXPAND, 5 ); - - - m_panel39->SetSizer( bSizer184 ); - m_panel39->Layout(); - bSizer184->Fit( m_panel39 ); - bSizer183->Add( m_panel39, 0, wxEXPAND|wxALL, 5 ); - - - m_panelDonate->SetSizer( bSizer183 ); - m_panelDonate->Layout(); - bSizer183->Fit( m_panelDonate ); - bSizer181->Add( m_panelDonate, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxRIGHT|wxLEFT, 5 ); - - wxBoxSizer* bSizer186; - bSizer186 = new wxBoxSizer( wxVERTICAL ); - - m_staticText94 = new wxStaticText( m_panel41, wxID_ANY, _("Feedback and suggestions are welcome"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText94->Wrap( -1 ); - bSizer186->Add( m_staticText94, 0, wxALL, 5 ); - - wxBoxSizer* bSizer166; - bSizer166 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer166->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_bitmap9 = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_bitmap9->SetToolTip( _("Homepage") ); - - bSizer166->Add( m_bitmap9, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - - m_hyperlink1 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("FreeFileSync.org"), wxT("http://www.freefilesync.org/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink1->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, true, wxEmptyString ) ); - m_hyperlink1->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer166->Add( m_hyperlink1, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer166->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_bitmap10 = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_bitmap10->SetToolTip( _("Email") ); - - bSizer166->Add( m_bitmap10, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - - m_hyperlink2 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("zenju@gmx.de"), wxT("mailto:zenju@gmx.de"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink2->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, true, wxEmptyString ) ); - m_hyperlink2->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer166->Add( m_hyperlink2, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer166->Add( 0, 0, 1, wxEXPAND, 5 ); - - - bSizer186->Add( bSizer166, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer181->Add( bSizer186, 0, wxALL|wxEXPAND, 5 ); - - m_staticline34 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer181->Add( m_staticline34, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer185; - bSizer185 = new wxBoxSizer( wxVERTICAL ); - - m_staticText93 = new wxStaticText( m_panel41, wxID_ANY, _("Published under the GNU General Public License"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText93->Wrap( -1 ); - bSizer185->Add( m_staticText93, 0, wxALL, 5 ); - - wxBoxSizer* bSizer1671; - bSizer1671 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmap13 = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer1671->Add( m_bitmap13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink5 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("http://www.gnu.org/licenses/gpl-3.0"), wxT("http://www.gnu.org/licenses/gpl-3.0"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink5->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer1671->Add( m_hyperlink5, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer185->Add( bSizer1671, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer181->Add( bSizer185, 0, wxALL|wxEXPAND, 5 ); - - - bSizer174->Add( bSizer181, 0, 0, 5 ); - - m_staticline37 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer174->Add( m_staticline37, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer177; - bSizer177 = new wxBoxSizer( wxVERTICAL ); - - m_staticText54 = new wxStaticText( m_panel41, wxID_ANY, _("Many thanks for localization:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText54->Wrap( 200 ); - m_staticText54->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer177->Add( m_staticText54, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); - - - bSizer177->Add( 0, 5, 0, 0, 5 ); - - m_scrolledWindowTranslators = new wxScrolledWindow( m_panel41, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), wxVSCROLL ); - m_scrolledWindowTranslators->SetScrollRate( 10, 10 ); - m_scrolledWindowTranslators->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_scrolledWindowTranslators->SetMinSize( wxSize( 220,-1 ) ); - - fgSizerTranslators = new wxFlexGridSizer( 0, 2, 2, 10 ); - fgSizerTranslators->SetFlexibleDirection( wxBOTH ); - fgSizerTranslators->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - - - m_scrolledWindowTranslators->SetSizer( fgSizerTranslators ); - m_scrolledWindowTranslators->Layout(); - fgSizerTranslators->Fit( m_scrolledWindowTranslators ); - bSizer177->Add( m_scrolledWindowTranslators, 1, wxALIGN_CENTER_HORIZONTAL|wxLEFT|wxEXPAND, 5 ); - - - bSizer174->Add( bSizer177, 0, wxEXPAND|wxTOP|wxLEFT, 5 ); - - - bSizer162->Add( bSizer174, 0, 0, 5 ); - - - m_panel41->SetSizer( bSizer162 ); - m_panel41->Layout(); - bSizer162->Fit( m_panel41 ); - bSizer31->Add( m_panel41, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - m_staticline36 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer31->Add( m_staticline36, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonClose->SetDefault(); - bSizerStdButtons->Add( m_buttonClose, 0, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer31->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); - - - this->SetSizer( bSizer31 ); - this->Layout(); - bSizer31->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( AboutDlgGenerated::OnClose ) ); - m_buttonDonate->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AboutDlgGenerated::OnDonate ), NULL, this ); - m_buttonClose->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AboutDlgGenerated::OnOK ), NULL, this ); + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer31; + bSizer31 = new wxBoxSizer( wxVERTICAL ); + + m_panel41 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel41->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer162; + bSizer162 = new wxBoxSizer( wxVERTICAL ); + + m_bitmapLogo = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer162->Add( m_bitmapLogo, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticline341 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer162->Add( m_staticline341, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer174; + bSizer174 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer181; + bSizer181 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer187; + bSizer187 = new wxBoxSizer( wxVERTICAL ); + + m_staticText96 = new wxStaticText( m_panel41, wxID_ANY, _("Source code written in C++ using:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText96->Wrap( -1 ); + bSizer187->Add( m_staticText96, 0, wxALL, 5 ); + + wxBoxSizer* bSizer171; + bSizer171 = new wxBoxSizer( wxHORIZONTAL ); + + m_hyperlink11 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("MS Visual C++"), wxT("http://www.visualstudio.com"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink11->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink11->SetToolTip( _("http://www.visualstudio.com") ); + + bSizer171->Add( m_hyperlink11, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink9 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("MinGW"), wxT("http://www.mingw.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink9->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink9->SetToolTip( _("http://www.mingw.org") ); + + bSizer171->Add( m_hyperlink9, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink10 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Code::Blocks"), wxT("http://www.codeblocks.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink10->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink10->SetToolTip( _("http://www.codeblocks.org") ); + + bSizer171->Add( m_hyperlink10, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink7 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("wxWidgets"), wxT("http://www.wxwidgets.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink7->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink7->SetToolTip( _("http://www.wxwidgets.org") ); + + bSizer171->Add( m_hyperlink7, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink14 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("wxFormBuilder"), wxT("http://wxformbuilder.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink14->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink14->SetToolTip( _("http://wxformbuilder.org") ); + + bSizer171->Add( m_hyperlink14, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer187->Add( bSizer171, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizer172; + bSizer172 = new wxBoxSizer( wxHORIZONTAL ); + + m_hyperlink15 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("zen::Xml"), wxT("http://zenxml.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink15->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink15->SetToolTip( _("http://zenxml.sourceforge.net") ); + + bSizer172->Add( m_hyperlink15, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink13 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Boost"), wxT("http://www.boost.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink13->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink13->SetToolTip( _("http://www.boost.org") ); + + bSizer172->Add( m_hyperlink13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink16 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Artistic Style"), wxT("http://astyle.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink16->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink16->SetToolTip( _("http://astyle.sourceforge.net") ); + + bSizer172->Add( m_hyperlink16, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink12 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Google Test"), wxT("http://code.google.com/p/googletest"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink12->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink12->SetToolTip( _("http://code.google.com/p/googletest") ); + + bSizer172->Add( m_hyperlink12, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink18 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Unicode NSIS"), wxT("http://www.scratchpaper.com"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink18->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink18->SetToolTip( _("http://www.scratchpaper.com") ); + + bSizer172->Add( m_hyperlink18, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer187->Add( bSizer172, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer181->Add( bSizer187, 0, wxALL|wxEXPAND, 5 ); + + m_panelDonate = new wxPanel( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelDonate->SetBackgroundColour( wxColour( 153, 170, 187 ) ); + + wxBoxSizer* bSizer183; + bSizer183 = new wxBoxSizer( wxVERTICAL ); + + m_panel39 = new wxPanel( m_panelDonate, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel39->SetBackgroundColour( wxColour( 221, 221, 255 ) ); + + wxBoxSizer* bSizer184; + bSizer184 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer184->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_animCtrlWink = new wxAnimationCtrl( m_panel39, wxID_ANY, wxNullAnimation, wxDefaultPosition, wxSize( 48,48 ), wxAC_DEFAULT_STYLE ); + bSizer184->Add( m_animCtrlWink, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + wxBoxSizer* bSizer178; + bSizer178 = new wxBoxSizer( wxVERTICAL ); + + m_staticText83 = new wxStaticText( m_panel39, wxID_ANY, _("If you like FreeFileSync:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText83->Wrap( -1 ); + m_staticText83->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + m_staticText83->SetForegroundColour( wxColour( 0, 0, 0 ) ); + + bSizer178->Add( m_staticText83, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_buttonDonate = new wxButton( m_panel39, wxID_ANY, _("Donate with PayPal"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonDonate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) ); + m_buttonDonate->SetToolTip( _("http://www.freefilesync.org/donate.php") ); + + bSizer178->Add( m_buttonDonate, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer184->Add( bSizer178, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer184->Add( 0, 0, 1, wxEXPAND, 5 ); + + + m_panel39->SetSizer( bSizer184 ); + m_panel39->Layout(); + bSizer184->Fit( m_panel39 ); + bSizer183->Add( m_panel39, 0, wxEXPAND|wxALL, 5 ); + + + m_panelDonate->SetSizer( bSizer183 ); + m_panelDonate->Layout(); + bSizer183->Fit( m_panelDonate ); + bSizer181->Add( m_panelDonate, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizer186; + bSizer186 = new wxBoxSizer( wxVERTICAL ); + + m_staticText94 = new wxStaticText( m_panel41, wxID_ANY, _("Feedback and suggestions are welcome"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText94->Wrap( -1 ); + bSizer186->Add( m_staticText94, 0, wxALL, 5 ); + + wxBoxSizer* bSizer166; + bSizer166 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer166->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bitmap9 = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_bitmap9->SetToolTip( _("Homepage") ); + + bSizer166->Add( m_bitmap9, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + m_hyperlink1 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("FreeFileSync.org"), wxT("http://www.freefilesync.org/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink1->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, true, wxEmptyString ) ); + m_hyperlink1->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer166->Add( m_hyperlink1, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer166->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bitmap10 = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_bitmap10->SetToolTip( _("Email") ); + + bSizer166->Add( m_bitmap10, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + m_hyperlink2 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("zenju@gmx.de"), wxT("mailto:zenju@gmx.de"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink2->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, true, wxEmptyString ) ); + m_hyperlink2->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer166->Add( m_hyperlink2, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer166->Add( 0, 0, 1, wxEXPAND, 5 ); + + + bSizer186->Add( bSizer166, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer181->Add( bSizer186, 0, wxALL|wxEXPAND, 5 ); + + m_staticline34 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer181->Add( m_staticline34, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer185; + bSizer185 = new wxBoxSizer( wxVERTICAL ); + + m_staticText93 = new wxStaticText( m_panel41, wxID_ANY, _("Published under the GNU General Public License"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText93->Wrap( -1 ); + bSizer185->Add( m_staticText93, 0, wxALL, 5 ); + + wxBoxSizer* bSizer1671; + bSizer1671 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmap13 = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer1671->Add( m_bitmap13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink5 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("http://www.gnu.org/licenses/gpl-3.0"), wxT("http://www.gnu.org/licenses/gpl-3.0"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink5->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer1671->Add( m_hyperlink5, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer185->Add( bSizer1671, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer181->Add( bSizer185, 0, wxALL|wxEXPAND, 5 ); + + + bSizer174->Add( bSizer181, 0, 0, 5 ); + + m_staticline37 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer174->Add( m_staticline37, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer177; + bSizer177 = new wxBoxSizer( wxVERTICAL ); + + m_staticText54 = new wxStaticText( m_panel41, wxID_ANY, _("Many thanks for localization:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText54->Wrap( 200 ); + m_staticText54->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer177->Add( m_staticText54, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); + + + bSizer177->Add( 0, 5, 0, 0, 5 ); + + m_scrolledWindowTranslators = new wxScrolledWindow( m_panel41, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), wxVSCROLL ); + m_scrolledWindowTranslators->SetScrollRate( 10, 10 ); + m_scrolledWindowTranslators->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_scrolledWindowTranslators->SetMinSize( wxSize( 220,-1 ) ); + + fgSizerTranslators = new wxFlexGridSizer( 0, 2, 2, 10 ); + fgSizerTranslators->SetFlexibleDirection( wxBOTH ); + fgSizerTranslators->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + + m_scrolledWindowTranslators->SetSizer( fgSizerTranslators ); + m_scrolledWindowTranslators->Layout(); + fgSizerTranslators->Fit( m_scrolledWindowTranslators ); + bSizer177->Add( m_scrolledWindowTranslators, 1, wxALIGN_CENTER_HORIZONTAL|wxLEFT|wxEXPAND, 5 ); + + + bSizer174->Add( bSizer177, 0, wxEXPAND|wxTOP|wxLEFT, 5 ); + + + bSizer162->Add( bSizer174, 0, 0, 5 ); + + + m_panel41->SetSizer( bSizer162 ); + m_panel41->Layout(); + bSizer162->Fit( m_panel41 ); + bSizer31->Add( m_panel41, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_staticline36 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer31->Add( m_staticline36, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonClose->SetDefault(); + bSizerStdButtons->Add( m_buttonClose, 0, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer31->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + + + this->SetSizer( bSizer31 ); + this->Layout(); + bSizer31->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( AboutDlgGenerated::OnClose ) ); + m_buttonDonate->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AboutDlgGenerated::OnDonate ), NULL, this ); + m_buttonClose->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AboutDlgGenerated::OnOK ), NULL, this ); } AboutDlgGenerated::~AboutDlgGenerated() diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h index e95f5fe5..be5e4436 100644 --- a/FreeFileSync/Source/ui/gui_generated.h +++ b/FreeFileSync/Source/ui/gui_generated.h @@ -14,10 +14,10 @@ class FolderHistoryBox; class OnCompletionBox; class ToggleButton; -namespace zen{ class BitmapTextButton; } -namespace zen{ class Graph2D; } -namespace zen{ class Grid; } -namespace zen{ class TripleSplitter; } +namespace zen { class BitmapTextButton; } +namespace zen { class Graph2D; } +namespace zen { class Grid; } +namespace zen { class TripleSplitter; } #include <wx/string.h> #include <wx/bitmap.h> @@ -61,793 +61,793 @@ namespace zen{ class TripleSplitter; } /////////////////////////////////////////////////////////////////////////////// /// Class MainDialogGenerated /////////////////////////////////////////////////////////////////////////////// -class MainDialogGenerated : public wxFrame +class MainDialogGenerated : public wxFrame { - private: - - protected: - wxMenuBar* m_menubar1; - wxMenu* m_menuFile; - wxMenuItem* m_menuItemNew; - wxMenuItem* m_menuItemLoad; - wxMenuItem* m_menuItemSave; - wxMenuItem* m_menuItemSaveAs; - wxMenuItem* m_menuItemSaveAsBatch; - wxMenu* m_menu4; - wxMenuItem* m_menuItemCompare; - wxMenuItem* m_menuItemCompSettings; - wxMenuItem* m_menuItemFilter; - wxMenuItem* m_menuItemSyncSettings; - wxMenuItem* m_menuItemSynchronize; - wxMenu* m_menuTools; - wxMenuItem* m_menuItemOptions; - wxMenu* m_menuLanguages; - wxMenu* m_menuHelp; - wxMenuItem* m_menuItemHelp; - wxMenu* m_menuCheckVersion; - wxMenuItem* m_menuItemCheckVersionNow; - wxMenuItem* m_menuItemCheckVersionAuto; - wxMenuItem* m_menuItemAbout; - wxBoxSizer* bSizerPanelHolder; - wxPanel* m_panelTopButtons; - wxBoxSizer* bSizerTopButtons; - zen::BitmapTextButton* m_buttonCancel; - zen::BitmapTextButton* m_buttonCompare; - wxBitmapButton* m_bpButtonCmpConfig; - wxBitmapButton* m_bpButtonFilter; - wxBitmapButton* m_bpButtonSyncConfig; - zen::BitmapTextButton* m_buttonSync; - wxPanel* m_panelDirectoryPairs; - wxStaticText* m_staticTextResolvedPathL; - wxBitmapButton* m_bpButtonAddPair; - wxButton* m_buttonSelectDirLeft; - wxPanel* m_panelTopMiddle; - wxBitmapButton* m_bpButtonSwapSides; - wxStaticText* m_staticTextResolvedPathR; - wxButton* m_buttonSelectDirRight; - wxScrolledWindow* m_scrolledWindowFolderPairs; - wxBoxSizer* bSizerAddFolderPairs; - zen::Grid* m_gridNavi; - wxPanel* m_panelCenter; - zen::TripleSplitter* m_splitterMain; - zen::Grid* m_gridMainL; - zen::Grid* m_gridMainC; - zen::Grid* m_gridMainR; - wxPanel* m_panelStatusBar; - wxBoxSizer* bSizerFileStatus; - wxBoxSizer* bSizerStatusLeft; - wxBoxSizer* bSizerStatusLeftDirectories; - wxStaticBitmap* m_bitmapSmallDirectoryLeft; - wxStaticText* m_staticTextStatusLeftDirs; - wxBoxSizer* bSizerStatusLeftFiles; - wxStaticBitmap* m_bitmapSmallFileLeft; - wxStaticText* m_staticTextStatusLeftFiles; - wxStaticText* m_staticTextStatusLeftBytes; - wxStaticLine* m_staticline9; - wxStaticText* m_staticTextStatusMiddle; - wxBoxSizer* bSizerStatusRight; - wxStaticLine* m_staticline10; - wxBoxSizer* bSizerStatusRightDirectories; - wxStaticBitmap* m_bitmapSmallDirectoryRight; - wxStaticText* m_staticTextStatusRightDirs; - wxBoxSizer* bSizerStatusRightFiles; - wxStaticBitmap* m_bitmapSmallFileRight; - wxStaticText* m_staticTextStatusRightFiles; - wxStaticText* m_staticTextStatusRightBytes; - wxStaticText* m_staticTextFullStatus; - wxPanel* m_panelSearch; - wxBitmapButton* m_bpButtonHideSearch; - wxStaticText* m_staticText101; - wxTextCtrl* m_textCtrlSearchTxt; - wxCheckBox* m_checkBoxMatchCase; - wxPanel* m_panelConfig; - wxBoxSizer* bSizerConfig; - wxBitmapButton* m_bpButtonNew; - wxStaticText* m_staticText951; - wxBitmapButton* m_bpButtonOpen; - wxStaticText* m_staticText95; - wxBitmapButton* m_bpButtonSave; - wxStaticText* m_staticText961; - wxBitmapButton* m_bpButtonSaveAs; - wxBitmapButton* m_bpButtonSaveAsBatch; - wxStaticText* m_staticText97; - wxListBox* m_listBoxHistory; - wxPanel* m_panelViewFilter; - wxBoxSizer* bSizerViewFilter; - wxStaticText* m_staticTextViewType; - ToggleButton* m_bpButtonViewTypeSyncAction; - ToggleButton* m_bpButtonShowExcluded; - wxStaticText* m_staticTextSelectView; - ToggleButton* m_bpButtonShowDeleteLeft; - ToggleButton* m_bpButtonShowUpdateLeft; - ToggleButton* m_bpButtonShowCreateLeft; - ToggleButton* m_bpButtonShowLeftOnly; - ToggleButton* m_bpButtonShowLeftNewer; - ToggleButton* m_bpButtonShowEqual; - ToggleButton* m_bpButtonShowDoNothing; - ToggleButton* m_bpButtonShowDifferent; - ToggleButton* m_bpButtonShowRightNewer; - ToggleButton* m_bpButtonShowRightOnly; - ToggleButton* m_bpButtonShowCreateRight; - ToggleButton* m_bpButtonShowUpdateRight; - ToggleButton* m_bpButtonShowDeleteRight; - ToggleButton* m_bpButtonShowConflict; - wxStaticText* m_staticText96; - wxPanel* m_panelStatistics; - wxBoxSizer* bSizer1801; - wxStaticBitmap* m_bitmapDeleteLeft; - wxStaticText* m_staticTextDeleteLeft; - wxStaticBitmap* m_bitmapUpdateLeft; - wxStaticText* m_staticTextUpdateLeft; - wxStaticBitmap* m_bitmapCreateLeft; - wxStaticText* m_staticTextCreateLeft; - wxStaticBitmap* m_bitmapData; - wxStaticText* m_staticTextData; - wxStaticBitmap* m_bitmapCreateRight; - wxStaticText* m_staticTextCreateRight; - wxStaticBitmap* m_bitmapUpdateRight; - wxStaticText* m_staticTextUpdateRight; - wxStaticBitmap* m_bitmapDeleteRight; - wxStaticText* m_staticTextDeleteRight; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnConfigNew( wxCommandEvent& event ) { event.Skip(); } - virtual void OnConfigLoad( wxCommandEvent& event ) { event.Skip(); } - virtual void OnConfigSave( wxCommandEvent& event ) { event.Skip(); } - virtual void OnConfigSaveAs( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSaveAsBatchJob( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuQuit( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCompare( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCmpSettings( wxCommandEvent& event ) { event.Skip(); } - virtual void OnConfigureFilter( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncSettings( wxCommandEvent& event ) { event.Skip(); } - virtual void OnStartSync( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuOptions( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuFindItem( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuResetLayout( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuExportFileList( wxCommandEvent& event ) { event.Skip(); } - virtual void OnShowHelp( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuCheckVersion( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuCheckVersionAutomatically( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuAbout( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCompSettingsContext( wxMouseEvent& event ) { event.Skip(); } - virtual void OnGlobalFilterContext( wxMouseEvent& event ) { event.Skip(); } - virtual void OnSyncSettingsContext( wxMouseEvent& event ) { event.Skip(); } - virtual void OnTopFolderPairAdd( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTopFolderPairRemove( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSwapSides( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHideSearchPanel( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSearchGridEnter( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCfgHistoryKeyEvent( wxKeyEvent& event ) { event.Skip(); } - virtual void OnLoadFromHistory( wxCommandEvent& event ) { event.Skip(); } - virtual void OnLoadFromHistoryDoubleClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCfgHistoryRightClick( wxMouseEvent& event ) { event.Skip(); } - virtual void OnToggleViewType( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToggleViewButton( wxCommandEvent& event ) { event.Skip(); } - virtual void OnViewButtonRightClick( wxMouseEvent& event ) { event.Skip(); } - - - public: - wxPanel* m_panelTopLeft; - wxBitmapButton* m_bpButtonRemovePair; - FolderHistoryBox* m_directoryLeft; - wxBitmapButton* m_bpButtonAltCompCfg; - wxBitmapButton* m_bpButtonLocalFilter; - wxBitmapButton* m_bpButtonAltSyncCfg; - wxPanel* m_panelTopRight; - FolderHistoryBox* m_directoryRight; - wxBoxSizer* bSizerStatistics; - wxBoxSizer* bSizerData; - - MainDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 900,600 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); - - ~MainDialogGenerated(); - +private: + +protected: + wxMenuBar* m_menubar1; + wxMenu* m_menuFile; + wxMenuItem* m_menuItemNew; + wxMenuItem* m_menuItemLoad; + wxMenuItem* m_menuItemSave; + wxMenuItem* m_menuItemSaveAs; + wxMenuItem* m_menuItemSaveAsBatch; + wxMenu* m_menu4; + wxMenuItem* m_menuItemCompare; + wxMenuItem* m_menuItemCompSettings; + wxMenuItem* m_menuItemFilter; + wxMenuItem* m_menuItemSyncSettings; + wxMenuItem* m_menuItemSynchronize; + wxMenu* m_menuTools; + wxMenuItem* m_menuItemOptions; + wxMenu* m_menuLanguages; + wxMenu* m_menuHelp; + wxMenuItem* m_menuItemHelp; + wxMenu* m_menuCheckVersion; + wxMenuItem* m_menuItemCheckVersionNow; + wxMenuItem* m_menuItemCheckVersionAuto; + wxMenuItem* m_menuItemAbout; + wxBoxSizer* bSizerPanelHolder; + wxPanel* m_panelTopButtons; + wxBoxSizer* bSizerTopButtons; + zen::BitmapTextButton* m_buttonCancel; + zen::BitmapTextButton* m_buttonCompare; + wxBitmapButton* m_bpButtonCmpConfig; + wxBitmapButton* m_bpButtonFilter; + wxBitmapButton* m_bpButtonSyncConfig; + zen::BitmapTextButton* m_buttonSync; + wxPanel* m_panelDirectoryPairs; + wxStaticText* m_staticTextResolvedPathL; + wxBitmapButton* m_bpButtonAddPair; + wxButton* m_buttonSelectDirLeft; + wxPanel* m_panelTopMiddle; + wxBitmapButton* m_bpButtonSwapSides; + wxStaticText* m_staticTextResolvedPathR; + wxButton* m_buttonSelectDirRight; + wxScrolledWindow* m_scrolledWindowFolderPairs; + wxBoxSizer* bSizerAddFolderPairs; + zen::Grid* m_gridNavi; + wxPanel* m_panelCenter; + zen::TripleSplitter* m_splitterMain; + zen::Grid* m_gridMainL; + zen::Grid* m_gridMainC; + zen::Grid* m_gridMainR; + wxPanel* m_panelStatusBar; + wxBoxSizer* bSizerFileStatus; + wxBoxSizer* bSizerStatusLeft; + wxBoxSizer* bSizerStatusLeftDirectories; + wxStaticBitmap* m_bitmapSmallDirectoryLeft; + wxStaticText* m_staticTextStatusLeftDirs; + wxBoxSizer* bSizerStatusLeftFiles; + wxStaticBitmap* m_bitmapSmallFileLeft; + wxStaticText* m_staticTextStatusLeftFiles; + wxStaticText* m_staticTextStatusLeftBytes; + wxStaticLine* m_staticline9; + wxStaticText* m_staticTextStatusMiddle; + wxBoxSizer* bSizerStatusRight; + wxStaticLine* m_staticline10; + wxBoxSizer* bSizerStatusRightDirectories; + wxStaticBitmap* m_bitmapSmallDirectoryRight; + wxStaticText* m_staticTextStatusRightDirs; + wxBoxSizer* bSizerStatusRightFiles; + wxStaticBitmap* m_bitmapSmallFileRight; + wxStaticText* m_staticTextStatusRightFiles; + wxStaticText* m_staticTextStatusRightBytes; + wxStaticText* m_staticTextFullStatus; + wxPanel* m_panelSearch; + wxBitmapButton* m_bpButtonHideSearch; + wxStaticText* m_staticText101; + wxTextCtrl* m_textCtrlSearchTxt; + wxCheckBox* m_checkBoxMatchCase; + wxPanel* m_panelConfig; + wxBoxSizer* bSizerConfig; + wxBitmapButton* m_bpButtonNew; + wxStaticText* m_staticText951; + wxBitmapButton* m_bpButtonOpen; + wxStaticText* m_staticText95; + wxBitmapButton* m_bpButtonSave; + wxStaticText* m_staticText961; + wxBitmapButton* m_bpButtonSaveAs; + wxBitmapButton* m_bpButtonSaveAsBatch; + wxStaticText* m_staticText97; + wxListBox* m_listBoxHistory; + wxPanel* m_panelViewFilter; + wxBoxSizer* bSizerViewFilter; + wxStaticText* m_staticTextViewType; + ToggleButton* m_bpButtonViewTypeSyncAction; + ToggleButton* m_bpButtonShowExcluded; + wxStaticText* m_staticTextSelectView; + ToggleButton* m_bpButtonShowDeleteLeft; + ToggleButton* m_bpButtonShowUpdateLeft; + ToggleButton* m_bpButtonShowCreateLeft; + ToggleButton* m_bpButtonShowLeftOnly; + ToggleButton* m_bpButtonShowLeftNewer; + ToggleButton* m_bpButtonShowEqual; + ToggleButton* m_bpButtonShowDoNothing; + ToggleButton* m_bpButtonShowDifferent; + ToggleButton* m_bpButtonShowRightNewer; + ToggleButton* m_bpButtonShowRightOnly; + ToggleButton* m_bpButtonShowCreateRight; + ToggleButton* m_bpButtonShowUpdateRight; + ToggleButton* m_bpButtonShowDeleteRight; + ToggleButton* m_bpButtonShowConflict; + wxStaticText* m_staticText96; + wxPanel* m_panelStatistics; + wxBoxSizer* bSizer1801; + wxStaticBitmap* m_bitmapDeleteLeft; + wxStaticText* m_staticTextDeleteLeft; + wxStaticBitmap* m_bitmapUpdateLeft; + wxStaticText* m_staticTextUpdateLeft; + wxStaticBitmap* m_bitmapCreateLeft; + wxStaticText* m_staticTextCreateLeft; + wxStaticBitmap* m_bitmapData; + wxStaticText* m_staticTextData; + wxStaticBitmap* m_bitmapCreateRight; + wxStaticText* m_staticTextCreateRight; + wxStaticBitmap* m_bitmapUpdateRight; + wxStaticText* m_staticTextUpdateRight; + wxStaticBitmap* m_bitmapDeleteRight; + wxStaticText* m_staticTextDeleteRight; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnConfigNew( wxCommandEvent& event ) { event.Skip(); } + virtual void OnConfigLoad( wxCommandEvent& event ) { event.Skip(); } + virtual void OnConfigSave( wxCommandEvent& event ) { event.Skip(); } + virtual void OnConfigSaveAs( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSaveAsBatchJob( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuQuit( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCompare( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCmpSettings( wxCommandEvent& event ) { event.Skip(); } + virtual void OnConfigureFilter( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSyncSettings( wxCommandEvent& event ) { event.Skip(); } + virtual void OnStartSync( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuOptions( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuFindItem( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuResetLayout( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuExportFileList( wxCommandEvent& event ) { event.Skip(); } + virtual void OnShowHelp( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuCheckVersion( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuCheckVersionAutomatically( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuAbout( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCompSettingsContext( wxMouseEvent& event ) { event.Skip(); } + virtual void OnGlobalFilterContext( wxMouseEvent& event ) { event.Skip(); } + virtual void OnSyncSettingsContext( wxMouseEvent& event ) { event.Skip(); } + virtual void OnTopFolderPairAdd( wxCommandEvent& event ) { event.Skip(); } + virtual void OnTopFolderPairRemove( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSwapSides( wxCommandEvent& event ) { event.Skip(); } + virtual void OnHideSearchPanel( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSearchGridEnter( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCfgHistoryKeyEvent( wxKeyEvent& event ) { event.Skip(); } + virtual void OnLoadFromHistory( wxCommandEvent& event ) { event.Skip(); } + virtual void OnLoadFromHistoryDoubleClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCfgHistoryRightClick( wxMouseEvent& event ) { event.Skip(); } + virtual void OnToggleViewType( wxCommandEvent& event ) { event.Skip(); } + virtual void OnToggleViewButton( wxCommandEvent& event ) { event.Skip(); } + virtual void OnViewButtonRightClick( wxMouseEvent& event ) { event.Skip(); } + + +public: + wxPanel* m_panelTopLeft; + wxBitmapButton* m_bpButtonRemovePair; + FolderHistoryBox* m_directoryLeft; + wxBitmapButton* m_bpButtonAltCompCfg; + wxBitmapButton* m_bpButtonLocalFilter; + wxBitmapButton* m_bpButtonAltSyncCfg; + wxPanel* m_panelTopRight; + FolderHistoryBox* m_directoryRight; + wxBoxSizer* bSizerStatistics; + wxBoxSizer* bSizerData; + + MainDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 900,600 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); + + ~MainDialogGenerated(); + }; /////////////////////////////////////////////////////////////////////////////// /// Class ConfigDlgGenerated /////////////////////////////////////////////////////////////////////////////// -class ConfigDlgGenerated : public wxDialog +class ConfigDlgGenerated : public wxDialog { - private: - - protected: - wxNotebook* m_notebook; - wxPanel* m_panelCompSettingsHolder; - wxBoxSizer* bSizerLocalCompSettings; - wxCheckBox* m_checkBoxUseLocalCmpOptions; - wxStaticLine* m_staticline59; - wxPanel* m_panelComparisonSettings; - wxStaticText* m_staticText91; - wxStaticBitmap* m_bitmapByTime; - wxToggleButton* m_toggleBtnTimeSize; - wxStaticBitmap* m_bitmapByContent; - wxToggleButton* m_toggleBtnContent; - wxStaticLine* m_staticline42; - wxTextCtrl* m_textCtrlCompVarDescription; - wxStaticLine* m_staticline33; - wxCheckBox* m_checkBoxTimeShift; - wxSpinCtrl* m_spinCtrlTimeShift; - wxHyperlinkCtrl* m_hyperlink241; - wxStaticLine* m_staticline44; - wxCheckBox* m_checkBoxSymlinksInclude; - wxRadioButton* m_radioBtnSymlinksFollow; - wxRadioButton* m_radioBtnSymlinksDirect; - wxHyperlinkCtrl* m_hyperlink24; - wxStaticLine* m_staticline441; - wxStaticLine* m_staticline331; - wxPanel* m_panelFilterSettingsHolder; - wxBoxSizer* bSizerLocalFilterSettings; - wxStaticText* m_staticText144; - wxStaticLine* m_staticline61; - wxPanel* m_panelFilterSettings; - wxStaticBitmap* m_bitmapInclude; - wxStaticText* m_staticText78; - wxTextCtrl* m_textCtrlInclude; - wxStaticLine* m_staticline22; - wxStaticBitmap* m_bitmapExclude; - wxStaticText* m_staticText77; - wxHyperlinkCtrl* m_hyperlink171; - wxTextCtrl* m_textCtrlExclude; - wxStaticLine* m_staticline24; - wxStaticBitmap* m_bitmapFilterDate; - wxStaticText* m_staticText79; - wxSpinCtrl* m_spinCtrlTimespan; - wxChoice* m_choiceUnitTimespan; - wxStaticLine* m_staticline23; - wxStaticBitmap* m_bitmapFilterSize; - wxStaticText* m_staticText80; - wxStaticText* m_staticText101; - wxSpinCtrl* m_spinCtrlMinSize; - wxChoice* m_choiceUnitMinSize; - wxStaticText* m_staticText102; - wxSpinCtrl* m_spinCtrlMaxSize; - wxChoice* m_choiceUnitMaxSize; - wxStaticLine* m_staticline62; - wxStaticText* m_staticText44; - wxStaticLine* m_staticline46; - wxButton* m_buttonClear; - wxPanel* m_panelSyncSettingsHolder; - wxBoxSizer* bSizerLocalSyncSettings; - wxCheckBox* m_checkBoxUseLocalSyncOptions; - wxStaticLine* m_staticline60; - wxPanel* m_panelSyncSettings; - wxStaticText* m_staticText86; - wxToggleButton* m_toggleBtnTwoWay; - wxToggleButton* m_toggleBtnMirror; - wxToggleButton* m_toggleBtnUpdate; - wxToggleButton* m_toggleBtnCustom; - wxCheckBox* m_checkBoxDetectMove; - wxStaticLine* m_staticline53; - wxTextCtrl* m_textCtrlSyncVarDescription; - wxStaticLine* m_staticline43; - wxBoxSizer* bSizerSyncConfig; - wxStaticText* m_staticText119; - wxStaticText* m_staticText120; - wxFlexGridSizer* fgSizerSyncDirections; - wxStaticBitmap* m_bitmapLeftOnly; - wxStaticBitmap* m_bitmapLeftNewer; - wxStaticBitmap* m_bitmapDifferent; - wxStaticBitmap* m_bitmapConflict; - wxStaticBitmap* m_bitmapRightNewer; - wxStaticBitmap* m_bitmapRightOnly; - wxBitmapButton* m_bpButtonLeftOnly; - wxBitmapButton* m_bpButtonLeftNewer; - wxBitmapButton* m_bpButtonDifferent; - wxBitmapButton* m_bpButtonConflict; - wxBitmapButton* m_bpButtonRightNewer; - wxBitmapButton* m_bpButtonRightOnly; - wxStaticBitmap* m_bitmapDatabase; - wxStaticLine* m_staticline54; - wxStaticText* m_staticText87; - wxToggleButton* m_toggleBtnPermanent; - wxToggleButton* m_toggleBtnRecycler; - wxToggleButton* m_toggleBtnVersioning; - wxBoxSizer* bSizerVersioning; - wxPanel* m_panelVersioning; - FolderHistoryBox* m_versioningFolder; - wxButton* m_buttonSelectDirVersioning; - wxBoxSizer* bSizer192; - wxStaticText* m_staticText93; - wxChoice* m_choiceVersioningStyle; - wxStaticText* m_staticTextNamingCvtPart1; - wxStaticText* m_staticTextNamingCvtPart2Bold; - wxStaticText* m_staticTextNamingCvtPart3; - wxHyperlinkCtrl* m_hyperlink17; - wxBoxSizer* bSizerMiscConfig; - wxStaticLine* m_staticline582; - wxStaticText* m_staticText88; - wxToggleButton* m_toggleBtnErrorIgnore; - wxToggleButton* m_toggleBtnErrorPopup; - wxStaticLine* m_staticline57; - wxBoxSizer* bSizerOnCompletion; - wxStaticText* m_staticText89; - OnCompletionBox* m_comboBoxOnCompletion; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonOkay; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnToggleLocalCompSettings( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTimeSizeDouble( wxMouseEvent& event ) { event.Skip(); } - virtual void OnTimeSize( wxCommandEvent& event ) { event.Skip(); } - virtual void OnContentDouble( wxMouseEvent& event ) { event.Skip(); } - virtual void OnContent( wxCommandEvent& event ) { event.Skip(); } - virtual void OnChangeCompOption( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHelpTimeShift( wxHyperlinkEvent& event ) { event.Skip(); } - virtual void OnHelpComparisonSettings( wxHyperlinkEvent& event ) { event.Skip(); } - virtual void OnChangeFilterOption( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHelpShowExamples( wxHyperlinkEvent& event ) { event.Skip(); } - virtual void OnFilterReset( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToggleLocalSyncSettings( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncTwoWayDouble( wxMouseEvent& event ) { event.Skip(); } - virtual void OnSyncTwoWay( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncMirrorDouble( wxMouseEvent& event ) { event.Skip(); } - virtual void OnSyncMirror( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncUpdateDouble( wxMouseEvent& event ) { event.Skip(); } - virtual void OnSyncUpdate( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncCustomDouble( wxMouseEvent& event ) { event.Skip(); } - virtual void OnSyncCustom( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToggleDetectMovedFiles( wxCommandEvent& event ) { event.Skip(); } - virtual void OnExLeftSideOnly( wxCommandEvent& event ) { event.Skip(); } - virtual void OnLeftNewer( wxCommandEvent& event ) { event.Skip(); } - virtual void OnDifferent( wxCommandEvent& event ) { event.Skip(); } - virtual void OnConflict( wxCommandEvent& event ) { event.Skip(); } - virtual void OnRightNewer( wxCommandEvent& event ) { event.Skip(); } - virtual void OnExRightSideOnly( wxCommandEvent& event ) { event.Skip(); } - virtual void OnDeletionPermanent( wxCommandEvent& event ) { event.Skip(); } - virtual void OnDeletionRecycler( wxCommandEvent& event ) { event.Skip(); } - virtual void OnDeletionVersioning( wxCommandEvent& event ) { event.Skip(); } - virtual void OnChangeSyncOption( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHelpVersioning( wxHyperlinkEvent& event ) { event.Skip(); } - virtual void OnErrorIgnore( wxCommandEvent& event ) { event.Skip(); } - virtual void OnErrorPopup( wxCommandEvent& event ) { event.Skip(); } - virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - - public: - - ConfigDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); - ~ConfigDlgGenerated(); - +private: + +protected: + wxNotebook* m_notebook; + wxPanel* m_panelCompSettingsHolder; + wxBoxSizer* bSizerLocalCompSettings; + wxCheckBox* m_checkBoxUseLocalCmpOptions; + wxStaticLine* m_staticline59; + wxPanel* m_panelComparisonSettings; + wxStaticText* m_staticText91; + wxStaticBitmap* m_bitmapByTime; + wxToggleButton* m_toggleBtnTimeSize; + wxStaticBitmap* m_bitmapByContent; + wxToggleButton* m_toggleBtnContent; + wxStaticLine* m_staticline42; + wxTextCtrl* m_textCtrlCompVarDescription; + wxStaticLine* m_staticline33; + wxCheckBox* m_checkBoxTimeShift; + wxSpinCtrl* m_spinCtrlTimeShift; + wxHyperlinkCtrl* m_hyperlink241; + wxStaticLine* m_staticline44; + wxCheckBox* m_checkBoxSymlinksInclude; + wxRadioButton* m_radioBtnSymlinksFollow; + wxRadioButton* m_radioBtnSymlinksDirect; + wxHyperlinkCtrl* m_hyperlink24; + wxStaticLine* m_staticline441; + wxStaticLine* m_staticline331; + wxPanel* m_panelFilterSettingsHolder; + wxBoxSizer* bSizerLocalFilterSettings; + wxStaticText* m_staticText144; + wxStaticLine* m_staticline61; + wxPanel* m_panelFilterSettings; + wxStaticBitmap* m_bitmapInclude; + wxStaticText* m_staticText78; + wxTextCtrl* m_textCtrlInclude; + wxStaticLine* m_staticline22; + wxStaticBitmap* m_bitmapExclude; + wxStaticText* m_staticText77; + wxHyperlinkCtrl* m_hyperlink171; + wxTextCtrl* m_textCtrlExclude; + wxStaticLine* m_staticline24; + wxStaticBitmap* m_bitmapFilterDate; + wxStaticText* m_staticText79; + wxSpinCtrl* m_spinCtrlTimespan; + wxChoice* m_choiceUnitTimespan; + wxStaticLine* m_staticline23; + wxStaticBitmap* m_bitmapFilterSize; + wxStaticText* m_staticText80; + wxStaticText* m_staticText101; + wxSpinCtrl* m_spinCtrlMinSize; + wxChoice* m_choiceUnitMinSize; + wxStaticText* m_staticText102; + wxSpinCtrl* m_spinCtrlMaxSize; + wxChoice* m_choiceUnitMaxSize; + wxStaticLine* m_staticline62; + wxStaticText* m_staticText44; + wxStaticLine* m_staticline46; + wxButton* m_buttonClear; + wxPanel* m_panelSyncSettingsHolder; + wxBoxSizer* bSizerLocalSyncSettings; + wxCheckBox* m_checkBoxUseLocalSyncOptions; + wxStaticLine* m_staticline60; + wxPanel* m_panelSyncSettings; + wxStaticText* m_staticText86; + wxToggleButton* m_toggleBtnTwoWay; + wxToggleButton* m_toggleBtnMirror; + wxToggleButton* m_toggleBtnUpdate; + wxToggleButton* m_toggleBtnCustom; + wxCheckBox* m_checkBoxDetectMove; + wxStaticLine* m_staticline53; + wxTextCtrl* m_textCtrlSyncVarDescription; + wxStaticLine* m_staticline43; + wxBoxSizer* bSizerSyncConfig; + wxStaticText* m_staticText119; + wxStaticText* m_staticText120; + wxFlexGridSizer* fgSizerSyncDirections; + wxStaticBitmap* m_bitmapLeftOnly; + wxStaticBitmap* m_bitmapLeftNewer; + wxStaticBitmap* m_bitmapDifferent; + wxStaticBitmap* m_bitmapConflict; + wxStaticBitmap* m_bitmapRightNewer; + wxStaticBitmap* m_bitmapRightOnly; + wxBitmapButton* m_bpButtonLeftOnly; + wxBitmapButton* m_bpButtonLeftNewer; + wxBitmapButton* m_bpButtonDifferent; + wxBitmapButton* m_bpButtonConflict; + wxBitmapButton* m_bpButtonRightNewer; + wxBitmapButton* m_bpButtonRightOnly; + wxStaticBitmap* m_bitmapDatabase; + wxStaticLine* m_staticline54; + wxStaticText* m_staticText87; + wxToggleButton* m_toggleBtnPermanent; + wxToggleButton* m_toggleBtnRecycler; + wxToggleButton* m_toggleBtnVersioning; + wxBoxSizer* bSizerVersioning; + wxPanel* m_panelVersioning; + FolderHistoryBox* m_versioningFolder; + wxButton* m_buttonSelectDirVersioning; + wxBoxSizer* bSizer192; + wxStaticText* m_staticText93; + wxChoice* m_choiceVersioningStyle; + wxStaticText* m_staticTextNamingCvtPart1; + wxStaticText* m_staticTextNamingCvtPart2Bold; + wxStaticText* m_staticTextNamingCvtPart3; + wxHyperlinkCtrl* m_hyperlink17; + wxBoxSizer* bSizerMiscConfig; + wxStaticLine* m_staticline582; + wxStaticText* m_staticText88; + wxToggleButton* m_toggleBtnErrorIgnore; + wxToggleButton* m_toggleBtnErrorPopup; + wxStaticLine* m_staticline57; + wxBoxSizer* bSizerOnCompletion; + wxStaticText* m_staticText89; + OnCompletionBox* m_comboBoxOnCompletion; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonOkay; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnToggleLocalCompSettings( wxCommandEvent& event ) { event.Skip(); } + virtual void OnTimeSizeDouble( wxMouseEvent& event ) { event.Skip(); } + virtual void OnTimeSize( wxCommandEvent& event ) { event.Skip(); } + virtual void OnContentDouble( wxMouseEvent& event ) { event.Skip(); } + virtual void OnContent( wxCommandEvent& event ) { event.Skip(); } + virtual void OnChangeCompOption( wxCommandEvent& event ) { event.Skip(); } + virtual void OnHelpTimeShift( wxHyperlinkEvent& event ) { event.Skip(); } + virtual void OnHelpComparisonSettings( wxHyperlinkEvent& event ) { event.Skip(); } + virtual void OnChangeFilterOption( wxCommandEvent& event ) { event.Skip(); } + virtual void OnHelpShowExamples( wxHyperlinkEvent& event ) { event.Skip(); } + virtual void OnFilterReset( wxCommandEvent& event ) { event.Skip(); } + virtual void OnToggleLocalSyncSettings( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSyncTwoWayDouble( wxMouseEvent& event ) { event.Skip(); } + virtual void OnSyncTwoWay( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSyncMirrorDouble( wxMouseEvent& event ) { event.Skip(); } + virtual void OnSyncMirror( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSyncUpdateDouble( wxMouseEvent& event ) { event.Skip(); } + virtual void OnSyncUpdate( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSyncCustomDouble( wxMouseEvent& event ) { event.Skip(); } + virtual void OnSyncCustom( wxCommandEvent& event ) { event.Skip(); } + virtual void OnToggleDetectMovedFiles( wxCommandEvent& event ) { event.Skip(); } + virtual void OnExLeftSideOnly( wxCommandEvent& event ) { event.Skip(); } + virtual void OnLeftNewer( wxCommandEvent& event ) { event.Skip(); } + virtual void OnDifferent( wxCommandEvent& event ) { event.Skip(); } + virtual void OnConflict( wxCommandEvent& event ) { event.Skip(); } + virtual void OnRightNewer( wxCommandEvent& event ) { event.Skip(); } + virtual void OnExRightSideOnly( wxCommandEvent& event ) { event.Skip(); } + virtual void OnDeletionPermanent( wxCommandEvent& event ) { event.Skip(); } + virtual void OnDeletionRecycler( wxCommandEvent& event ) { event.Skip(); } + virtual void OnDeletionVersioning( wxCommandEvent& event ) { event.Skip(); } + virtual void OnChangeSyncOption( wxCommandEvent& event ) { event.Skip(); } + virtual void OnHelpVersioning( wxHyperlinkEvent& event ) { event.Skip(); } + virtual void OnErrorIgnore( wxCommandEvent& event ) { event.Skip(); } + virtual void OnErrorPopup( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + +public: + + ConfigDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); + ~ConfigDlgGenerated(); + }; /////////////////////////////////////////////////////////////////////////////// /// Class SyncConfirmationDlgGenerated /////////////////////////////////////////////////////////////////////////////// -class SyncConfirmationDlgGenerated : public wxDialog +class SyncConfirmationDlgGenerated : public wxDialog { - private: - - protected: - wxStaticBitmap* m_bitmapSync; - wxStaticText* m_staticTextHeader; - wxStaticLine* m_staticline371; - wxPanel* m_panelStatistics; - wxStaticLine* m_staticline38; - wxStaticText* m_staticText84; - wxStaticText* m_staticTextVariant; - wxStaticLine* m_staticline14; - wxStaticText* m_staticText83; - wxStaticBitmap* m_bitmapDeleteLeft; - wxStaticBitmap* m_bitmapUpdateLeft; - wxStaticBitmap* m_bitmapCreateLeft; - wxStaticBitmap* m_bitmapData; - wxStaticBitmap* m_bitmapCreateRight; - wxStaticBitmap* m_bitmapUpdateRight; - wxStaticBitmap* m_bitmapDeleteRight; - wxStaticText* m_staticTextDeleteLeft; - wxStaticText* m_staticTextUpdateLeft; - wxStaticText* m_staticTextCreateLeft; - wxStaticText* m_staticTextData; - wxStaticText* m_staticTextCreateRight; - wxStaticText* m_staticTextUpdateRight; - wxStaticText* m_staticTextDeleteRight; - wxStaticLine* m_staticline381; - wxStaticLine* m_staticline12; - wxCheckBox* m_checkBoxDontShowAgain; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonStartSync; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnStartSync( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - - public: - - SyncConfirmationDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("FreeFileSync"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); - ~SyncConfirmationDlgGenerated(); - +private: + +protected: + wxStaticBitmap* m_bitmapSync; + wxStaticText* m_staticTextHeader; + wxStaticLine* m_staticline371; + wxPanel* m_panelStatistics; + wxStaticLine* m_staticline38; + wxStaticText* m_staticText84; + wxStaticText* m_staticTextVariant; + wxStaticLine* m_staticline14; + wxStaticText* m_staticText83; + wxStaticBitmap* m_bitmapDeleteLeft; + wxStaticBitmap* m_bitmapUpdateLeft; + wxStaticBitmap* m_bitmapCreateLeft; + wxStaticBitmap* m_bitmapData; + wxStaticBitmap* m_bitmapCreateRight; + wxStaticBitmap* m_bitmapUpdateRight; + wxStaticBitmap* m_bitmapDeleteRight; + wxStaticText* m_staticTextDeleteLeft; + wxStaticText* m_staticTextUpdateLeft; + wxStaticText* m_staticTextCreateLeft; + wxStaticText* m_staticTextData; + wxStaticText* m_staticTextCreateRight; + wxStaticText* m_staticTextUpdateRight; + wxStaticText* m_staticTextDeleteRight; + wxStaticLine* m_staticline381; + wxStaticLine* m_staticline12; + wxCheckBox* m_checkBoxDontShowAgain; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonStartSync; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnStartSync( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + +public: + + SyncConfirmationDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("FreeFileSync"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~SyncConfirmationDlgGenerated(); + }; /////////////////////////////////////////////////////////////////////////////// /// Class FolderPairPanelGenerated /////////////////////////////////////////////////////////////////////////////// -class FolderPairPanelGenerated : public wxPanel +class FolderPairPanelGenerated : public wxPanel { - private: - - protected: - wxButton* m_buttonSelectDirLeft; - wxButton* m_buttonSelectDirRight; - - public: - wxPanel* m_panelLeft; - wxBitmapButton* m_bpButtonFolderPairOptions; - wxBitmapButton* m_bpButtonRemovePair; - FolderHistoryBox* m_directoryLeft; - wxPanel* m_panel20; - wxBitmapButton* m_bpButtonAltCompCfg; - wxBitmapButton* m_bpButtonLocalFilter; - wxBitmapButton* m_bpButtonAltSyncCfg; - wxPanel* m_panelRight; - FolderHistoryBox* m_directoryRight; - - FolderPairPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0 ); - ~FolderPairPanelGenerated(); - +private: + +protected: + wxButton* m_buttonSelectDirLeft; + wxButton* m_buttonSelectDirRight; + +public: + wxPanel* m_panelLeft; + wxBitmapButton* m_bpButtonFolderPairOptions; + wxBitmapButton* m_bpButtonRemovePair; + FolderHistoryBox* m_directoryLeft; + wxPanel* m_panel20; + wxBitmapButton* m_bpButtonAltCompCfg; + wxBitmapButton* m_bpButtonLocalFilter; + wxBitmapButton* m_bpButtonAltSyncCfg; + wxPanel* m_panelRight; + FolderHistoryBox* m_directoryRight; + + FolderPairPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0 ); + ~FolderPairPanelGenerated(); + }; /////////////////////////////////////////////////////////////////////////////// /// Class CompareProgressDlgGenerated /////////////////////////////////////////////////////////////////////////////// -class CompareProgressDlgGenerated : public wxPanel +class CompareProgressDlgGenerated : public wxPanel { - private: - - protected: - wxPanel* m_panelStatistics; - wxStaticText* m_staticTextItemsFoundLabel; - wxStaticText* m_staticTextItemsFound; - wxStaticText* m_staticTextItemsRemainingLabel; - wxBoxSizer* bSizerItemsRemaining; - wxStaticText* m_staticTextItemsRemaining; - wxStaticText* m_staticTextDataRemaining; - wxStaticText* m_staticTextTimeRemainingLabel; - wxStaticText* m_staticTextTimeRemaining; - wxStaticText* m_staticTextTimeElapsed; - wxStaticText* m_staticTextStatus; - wxGauge* m_gauge2; - wxStaticText* m_staticTextSpeed; - - public: - - CompareProgressDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxRAISED_BORDER ); - ~CompareProgressDlgGenerated(); - +private: + +protected: + wxPanel* m_panelStatistics; + wxStaticText* m_staticTextItemsFoundLabel; + wxStaticText* m_staticTextItemsFound; + wxStaticText* m_staticTextItemsRemainingLabel; + wxBoxSizer* bSizerItemsRemaining; + wxStaticText* m_staticTextItemsRemaining; + wxStaticText* m_staticTextDataRemaining; + wxStaticText* m_staticTextTimeRemainingLabel; + wxStaticText* m_staticTextTimeRemaining; + wxStaticText* m_staticTextTimeElapsed; + wxStaticText* m_staticTextStatus; + wxGauge* m_gauge2; + wxStaticText* m_staticTextSpeed; + +public: + + CompareProgressDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxRAISED_BORDER ); + ~CompareProgressDlgGenerated(); + }; /////////////////////////////////////////////////////////////////////////////// /// Class SyncProgressPanelGenerated /////////////////////////////////////////////////////////////////////////////// -class SyncProgressPanelGenerated : public wxPanel +class SyncProgressPanelGenerated : public wxPanel { - private: - - protected: - wxBoxSizer* bSizer42; - wxBoxSizer* bSizer171; - wxStaticText* m_staticText87; - - public: - wxBoxSizer* bSizerRoot; - wxStaticBitmap* m_bitmapStatus; - wxStaticText* m_staticTextPhase; - wxAnimationCtrl* m_animCtrlSyncing; - wxBitmapButton* m_bpButtonMinimizeToTray; - wxBoxSizer* bSizerStatusText; - wxStaticText* m_staticTextStatus; - wxPanel* m_panelProgress; - wxPanel* m_panelItemsProcessed; - wxStaticText* m_staticTextProcessedObj; - wxStaticText* m_staticTextDataProcessed; - wxPanel* m_panelItemsRemaining; - wxStaticText* m_staticTextRemainingObj; - wxStaticText* m_staticTextDataRemaining; - wxPanel* m_panelTimeRemaining; - wxStaticText* m_staticTextRemTime; - wxStaticText* m_staticTextTimeElapsed; - wxStaticBitmap* m_bitmapGraphKeyBytes; - zen::Graph2D* m_panelGraphBytes; - wxStaticBitmap* m_bitmapGraphKeyItems; - zen::Graph2D* m_panelGraphItems; - wxNotebook* m_notebookResult; - wxStaticLine* m_staticlineFooter; - wxBoxSizer* bSizerStdButtons; - wxBoxSizer* bSizerOnCompletion; - OnCompletionBox* m_comboBoxOnCompletion; - wxButton* m_buttonClose; - wxButton* m_buttonPause; - wxButton* m_buttonStop; - - SyncProgressPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL ); - ~SyncProgressPanelGenerated(); - +private: + +protected: + wxBoxSizer* bSizer42; + wxBoxSizer* bSizer171; + wxStaticText* m_staticText87; + +public: + wxBoxSizer* bSizerRoot; + wxStaticBitmap* m_bitmapStatus; + wxStaticText* m_staticTextPhase; + wxAnimationCtrl* m_animCtrlSyncing; + wxBitmapButton* m_bpButtonMinimizeToTray; + wxBoxSizer* bSizerStatusText; + wxStaticText* m_staticTextStatus; + wxPanel* m_panelProgress; + wxPanel* m_panelItemsProcessed; + wxStaticText* m_staticTextProcessedObj; + wxStaticText* m_staticTextDataProcessed; + wxPanel* m_panelItemsRemaining; + wxStaticText* m_staticTextRemainingObj; + wxStaticText* m_staticTextDataRemaining; + wxPanel* m_panelTimeRemaining; + wxStaticText* m_staticTextRemTime; + wxStaticText* m_staticTextTimeElapsed; + wxStaticBitmap* m_bitmapGraphKeyBytes; + zen::Graph2D* m_panelGraphBytes; + wxStaticBitmap* m_bitmapGraphKeyItems; + zen::Graph2D* m_panelGraphItems; + wxNotebook* m_notebookResult; + wxStaticLine* m_staticlineFooter; + wxBoxSizer* bSizerStdButtons; + wxBoxSizer* bSizerOnCompletion; + OnCompletionBox* m_comboBoxOnCompletion; + wxButton* m_buttonClose; + wxButton* m_buttonPause; + wxButton* m_buttonStop; + + SyncProgressPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL ); + ~SyncProgressPanelGenerated(); + }; /////////////////////////////////////////////////////////////////////////////// /// Class LogPanelGenerated /////////////////////////////////////////////////////////////////////////////// -class LogPanelGenerated : public wxPanel +class LogPanelGenerated : public wxPanel { - private: - - protected: - ToggleButton* m_bpButtonErrors; - ToggleButton* m_bpButtonWarnings; - ToggleButton* m_bpButtonInfo; - wxStaticLine* m_staticline13; - zen::Grid* m_gridMessages; - - // Virtual event handlers, overide them in your derived class - virtual void OnErrors( wxCommandEvent& event ) { event.Skip(); } - virtual void OnWarnings( wxCommandEvent& event ) { event.Skip(); } - virtual void OnInfo( wxCommandEvent& event ) { event.Skip(); } - - - public: - - LogPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL ); - ~LogPanelGenerated(); - +private: + +protected: + ToggleButton* m_bpButtonErrors; + ToggleButton* m_bpButtonWarnings; + ToggleButton* m_bpButtonInfo; + wxStaticLine* m_staticline13; + zen::Grid* m_gridMessages; + + // Virtual event handlers, overide them in your derived class + virtual void OnErrors( wxCommandEvent& event ) { event.Skip(); } + virtual void OnWarnings( wxCommandEvent& event ) { event.Skip(); } + virtual void OnInfo( wxCommandEvent& event ) { event.Skip(); } + + +public: + + LogPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL ); + ~LogPanelGenerated(); + }; /////////////////////////////////////////////////////////////////////////////// /// Class BatchDlgGenerated /////////////////////////////////////////////////////////////////////////////// -class BatchDlgGenerated : public wxDialog +class BatchDlgGenerated : public wxDialog { - private: - - protected: - wxStaticBitmap* m_bitmapBatchJob; - wxStaticText* m_staticTextDescr; - wxStaticLine* m_staticline18; - wxPanel* m_panel35; - wxStaticText* m_staticText82; - wxToggleButton* m_toggleBtnErrorIgnore; - wxToggleButton* m_toggleBtnErrorPopup; - wxToggleButton* m_toggleBtnErrorStop; - wxStaticLine* m_staticline26; - wxCheckBox* m_checkBoxRunMinimized; - wxStaticText* m_staticText81; - OnCompletionBox* m_comboBoxOnCompletion; - wxStaticLine* m_staticline25; - wxCheckBox* m_checkBoxGenerateLogfile; - wxPanel* m_panelLogfile; - wxButton* m_buttonSelectLogfileDir; - wxCheckBox* m_checkBoxLogfilesLimit; - wxSpinCtrl* m_spinCtrlLogfileLimit; - wxHyperlinkCtrl* m_hyperlink17; - wxStaticLine* m_staticline13; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonSaveAs; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnErrorIgnore( wxCommandEvent& event ) { event.Skip(); } - virtual void OnErrorPopup( wxCommandEvent& event ) { event.Skip(); } - virtual void OnErrorStop( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToggleGenerateLogfile( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToggleLogfilesLimit( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHelpScheduleBatch( wxHyperlinkEvent& event ) { event.Skip(); } - virtual void OnSaveBatchJob( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - - public: - FolderHistoryBox* m_logfileDir; - - BatchDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Save as Batch Job"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~BatchDlgGenerated(); - +private: + +protected: + wxStaticBitmap* m_bitmapBatchJob; + wxStaticText* m_staticTextDescr; + wxStaticLine* m_staticline18; + wxPanel* m_panel35; + wxStaticText* m_staticText82; + wxToggleButton* m_toggleBtnErrorIgnore; + wxToggleButton* m_toggleBtnErrorPopup; + wxToggleButton* m_toggleBtnErrorStop; + wxStaticLine* m_staticline26; + wxCheckBox* m_checkBoxRunMinimized; + wxStaticText* m_staticText81; + OnCompletionBox* m_comboBoxOnCompletion; + wxStaticLine* m_staticline25; + wxCheckBox* m_checkBoxGenerateLogfile; + wxPanel* m_panelLogfile; + wxButton* m_buttonSelectLogfileDir; + wxCheckBox* m_checkBoxLogfilesLimit; + wxSpinCtrl* m_spinCtrlLogfileLimit; + wxHyperlinkCtrl* m_hyperlink17; + wxStaticLine* m_staticline13; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonSaveAs; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnErrorIgnore( wxCommandEvent& event ) { event.Skip(); } + virtual void OnErrorPopup( wxCommandEvent& event ) { event.Skip(); } + virtual void OnErrorStop( wxCommandEvent& event ) { event.Skip(); } + virtual void OnToggleGenerateLogfile( wxCommandEvent& event ) { event.Skip(); } + virtual void OnToggleLogfilesLimit( wxCommandEvent& event ) { event.Skip(); } + virtual void OnHelpScheduleBatch( wxHyperlinkEvent& event ) { event.Skip(); } + virtual void OnSaveBatchJob( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + +public: + FolderHistoryBox* m_logfileDir; + + BatchDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Save as Batch Job"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~BatchDlgGenerated(); + }; /////////////////////////////////////////////////////////////////////////////// /// Class DeleteDlgGenerated /////////////////////////////////////////////////////////////////////////////// -class DeleteDlgGenerated : public wxDialog +class DeleteDlgGenerated : public wxDialog { - private: - - protected: - wxStaticBitmap* m_bitmapDeleteType; - wxStaticText* m_staticTextHeader; - wxStaticLine* m_staticline91; - wxPanel* m_panel31; - wxStaticLine* m_staticline42; - wxTextCtrl* m_textCtrlFileList; - wxStaticLine* m_staticline9; - wxBoxSizer* bSizerStdButtons; - wxCheckBox* m_checkBoxUseRecycler; - wxButton* m_buttonOK; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnUseRecycler( wxCommandEvent& event ) { event.Skip(); } - virtual void OnOK( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - - public: - - DeleteDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Delete Items"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); - ~DeleteDlgGenerated(); - +private: + +protected: + wxStaticBitmap* m_bitmapDeleteType; + wxStaticText* m_staticTextHeader; + wxStaticLine* m_staticline91; + wxPanel* m_panel31; + wxStaticLine* m_staticline42; + wxTextCtrl* m_textCtrlFileList; + wxStaticLine* m_staticline9; + wxBoxSizer* bSizerStdButtons; + wxCheckBox* m_checkBoxUseRecycler; + wxButton* m_buttonOK; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnUseRecycler( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOK( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + +public: + + DeleteDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Delete Items"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); + ~DeleteDlgGenerated(); + }; /////////////////////////////////////////////////////////////////////////////// /// Class OptionsDlgGenerated /////////////////////////////////////////////////////////////////////////////// -class OptionsDlgGenerated : public wxDialog +class OptionsDlgGenerated : public wxDialog { - private: - - protected: - wxStaticBitmap* m_bitmapSettings; - wxStaticText* m_staticText44; - wxStaticLine* m_staticline20; - wxPanel* m_panel39; - wxCheckBox* m_checkBoxFailSafe; - wxStaticText* m_staticText91; - wxBoxSizer* bSizerLockedFiles; - wxCheckBox* m_checkBoxCopyLocked; - wxStaticText* m_staticText92; - wxCheckBox* m_checkBoxCopyPermissions; - wxStaticText* m_staticText93; - wxStaticLine* m_staticline39; - wxStaticText* m_staticText95; - wxStaticText* m_staticText96; - wxSpinCtrl* m_spinCtrlAutoRetryCount; - wxStaticText* m_staticTextAutoRetryDelay; - wxSpinCtrl* m_spinCtrlAutoRetryDelay; - wxStaticLine* m_staticline191; - wxStaticText* m_staticText85; - wxGrid* m_gridCustomCommand; - wxBitmapButton* m_bpButtonAddRow; - wxBitmapButton* m_bpButtonRemoveRow; - wxHyperlinkCtrl* m_hyperlink17; - wxStaticLine* m_staticline192; - zen::BitmapTextButton* m_buttonResetDialogs; - wxStaticLine* m_staticline40; - wxStaticLine* m_staticline36; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonDefault; - wxButton* m_buttonOkay; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnToggleAutoRetryCount( wxCommandEvent& event ) { event.Skip(); } - virtual void OnAddRow( wxCommandEvent& event ) { event.Skip(); } - virtual void OnRemoveRow( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHelpShowExamples( wxHyperlinkEvent& event ) { event.Skip(); } - virtual void OnResetDialogs( wxCommandEvent& event ) { event.Skip(); } - virtual void OnDefault( wxCommandEvent& event ) { event.Skip(); } - virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - - public: - - OptionsDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Options"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~OptionsDlgGenerated(); - +private: + +protected: + wxStaticBitmap* m_bitmapSettings; + wxStaticText* m_staticText44; + wxStaticLine* m_staticline20; + wxPanel* m_panel39; + wxCheckBox* m_checkBoxFailSafe; + wxStaticText* m_staticText91; + wxBoxSizer* bSizerLockedFiles; + wxCheckBox* m_checkBoxCopyLocked; + wxStaticText* m_staticText92; + wxCheckBox* m_checkBoxCopyPermissions; + wxStaticText* m_staticText93; + wxStaticLine* m_staticline39; + wxStaticText* m_staticText95; + wxStaticText* m_staticText96; + wxSpinCtrl* m_spinCtrlAutoRetryCount; + wxStaticText* m_staticTextAutoRetryDelay; + wxSpinCtrl* m_spinCtrlAutoRetryDelay; + wxStaticLine* m_staticline191; + wxStaticText* m_staticText85; + wxGrid* m_gridCustomCommand; + wxBitmapButton* m_bpButtonAddRow; + wxBitmapButton* m_bpButtonRemoveRow; + wxHyperlinkCtrl* m_hyperlink17; + wxStaticLine* m_staticline192; + zen::BitmapTextButton* m_buttonResetDialogs; + wxStaticLine* m_staticline40; + wxStaticLine* m_staticline36; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonDefault; + wxButton* m_buttonOkay; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnToggleAutoRetryCount( wxCommandEvent& event ) { event.Skip(); } + virtual void OnAddRow( wxCommandEvent& event ) { event.Skip(); } + virtual void OnRemoveRow( wxCommandEvent& event ) { event.Skip(); } + virtual void OnHelpShowExamples( wxHyperlinkEvent& event ) { event.Skip(); } + virtual void OnResetDialogs( wxCommandEvent& event ) { event.Skip(); } + virtual void OnDefault( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + +public: + + OptionsDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Options"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~OptionsDlgGenerated(); + }; /////////////////////////////////////////////////////////////////////////////// /// Class TooltipDialogGenerated /////////////////////////////////////////////////////////////////////////////// -class TooltipDialogGenerated : public wxDialog +class TooltipDialogGenerated : public wxDialog { - private: - - protected: - - public: - wxStaticBitmap* m_bitmapLeft; - wxStaticText* m_staticTextMain; - - TooltipDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); - ~TooltipDialogGenerated(); - +private: + +protected: + +public: + wxStaticBitmap* m_bitmapLeft; + wxStaticText* m_staticTextMain; + + TooltipDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~TooltipDialogGenerated(); + }; /////////////////////////////////////////////////////////////////////////////// /// Class SelectTimespanDlgGenerated /////////////////////////////////////////////////////////////////////////////// -class SelectTimespanDlgGenerated : public wxDialog +class SelectTimespanDlgGenerated : public wxDialog { - private: - - protected: - wxPanel* m_panel35; - wxCalendarCtrl* m_calendarFrom; - wxCalendarCtrl* m_calendarTo; - wxStaticLine* m_staticline21; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonOkay; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnChangeSelectionFrom( wxCalendarEvent& event ) { event.Skip(); } - virtual void OnChangeSelectionTo( wxCalendarEvent& event ) { event.Skip(); } - virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - - public: - - SelectTimespanDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Select Time Span"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); - ~SelectTimespanDlgGenerated(); - +private: + +protected: + wxPanel* m_panel35; + wxCalendarCtrl* m_calendarFrom; + wxCalendarCtrl* m_calendarTo; + wxStaticLine* m_staticline21; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonOkay; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnChangeSelectionFrom( wxCalendarEvent& event ) { event.Skip(); } + virtual void OnChangeSelectionTo( wxCalendarEvent& event ) { event.Skip(); } + virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + +public: + + SelectTimespanDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Select Time Span"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~SelectTimespanDlgGenerated(); + }; /////////////////////////////////////////////////////////////////////////////// /// Class AboutDlgGenerated /////////////////////////////////////////////////////////////////////////////// -class AboutDlgGenerated : public wxDialog +class AboutDlgGenerated : public wxDialog { - private: - - protected: - wxPanel* m_panel41; - wxStaticBitmap* m_bitmapLogo; - wxStaticLine* m_staticline341; - wxStaticText* m_staticText96; - wxHyperlinkCtrl* m_hyperlink11; - wxHyperlinkCtrl* m_hyperlink9; - wxHyperlinkCtrl* m_hyperlink10; - wxHyperlinkCtrl* m_hyperlink7; - wxHyperlinkCtrl* m_hyperlink14; - wxHyperlinkCtrl* m_hyperlink15; - wxHyperlinkCtrl* m_hyperlink13; - wxHyperlinkCtrl* m_hyperlink16; - wxHyperlinkCtrl* m_hyperlink12; - wxHyperlinkCtrl* m_hyperlink18; - wxPanel* m_panelDonate; - wxPanel* m_panel39; - wxAnimationCtrl* m_animCtrlWink; - wxStaticText* m_staticText83; - wxButton* m_buttonDonate; - wxStaticText* m_staticText94; - wxStaticBitmap* m_bitmap9; - wxHyperlinkCtrl* m_hyperlink1; - wxStaticBitmap* m_bitmap10; - wxHyperlinkCtrl* m_hyperlink2; - wxStaticLine* m_staticline34; - wxStaticText* m_staticText93; - wxStaticBitmap* m_bitmap13; - wxHyperlinkCtrl* m_hyperlink5; - wxStaticLine* m_staticline37; - wxStaticText* m_staticText54; - wxScrolledWindow* m_scrolledWindowTranslators; - wxFlexGridSizer* fgSizerTranslators; - wxStaticLine* m_staticline36; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonClose; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnDonate( wxCommandEvent& event ) { event.Skip(); } - virtual void OnOK( wxCommandEvent& event ) { event.Skip(); } - - - public: - - AboutDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("About"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); - ~AboutDlgGenerated(); - +private: + +protected: + wxPanel* m_panel41; + wxStaticBitmap* m_bitmapLogo; + wxStaticLine* m_staticline341; + wxStaticText* m_staticText96; + wxHyperlinkCtrl* m_hyperlink11; + wxHyperlinkCtrl* m_hyperlink9; + wxHyperlinkCtrl* m_hyperlink10; + wxHyperlinkCtrl* m_hyperlink7; + wxHyperlinkCtrl* m_hyperlink14; + wxHyperlinkCtrl* m_hyperlink15; + wxHyperlinkCtrl* m_hyperlink13; + wxHyperlinkCtrl* m_hyperlink16; + wxHyperlinkCtrl* m_hyperlink12; + wxHyperlinkCtrl* m_hyperlink18; + wxPanel* m_panelDonate; + wxPanel* m_panel39; + wxAnimationCtrl* m_animCtrlWink; + wxStaticText* m_staticText83; + wxButton* m_buttonDonate; + wxStaticText* m_staticText94; + wxStaticBitmap* m_bitmap9; + wxHyperlinkCtrl* m_hyperlink1; + wxStaticBitmap* m_bitmap10; + wxHyperlinkCtrl* m_hyperlink2; + wxStaticLine* m_staticline34; + wxStaticText* m_staticText93; + wxStaticBitmap* m_bitmap13; + wxHyperlinkCtrl* m_hyperlink5; + wxStaticLine* m_staticline37; + wxStaticText* m_staticText54; + wxScrolledWindow* m_scrolledWindowTranslators; + wxFlexGridSizer* fgSizerTranslators; + wxStaticLine* m_staticline36; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonClose; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnDonate( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOK( wxCommandEvent& event ) { event.Skip(); } + + +public: + + AboutDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("About"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~AboutDlgGenerated(); + }; #endif //__GUI_GENERATED_H__ diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp index b075b738..ce0d4cec 100644 --- a/FreeFileSync/Source/ui/main_dlg.cpp +++ b/FreeFileSync/Source/ui/main_dlg.cpp @@ -37,6 +37,7 @@ #include "../comparison.h" #include "../synchronization.h" #include "../algorithm.h" +#include "../fs/concrete.h" #include "../lib/resolve_path.h" #include "../lib/ffs_paths.h" #include "../lib/help_provider.h" @@ -91,16 +92,16 @@ bool isComponentOf(const wxWindow* child, const wxWindow* top) } -class DirectoryNameMainImpl : public DirectoryName<FolderHistoryBox> +class FolderSelectorMainImpl : public FolderSelector { public: - DirectoryNameMainImpl(MainDialog& mainDlg, - wxWindow& dropWindow1, - Grid& dropGrid, - wxButton& dirSelectButton, - FolderHistoryBox& dirpath, - wxStaticText& staticText) : - DirectoryName(dropWindow1, dirSelectButton, dirpath, &staticText, &dropGrid.getMainWin()), + FolderSelectorMainImpl(MainDialog& mainDlg, + wxWindow& dropWindow1, + Grid& dropGrid, + wxButton& dirSelectButton, + FolderHistoryBox& dirpath, + wxStaticText& staticText) : + FolderSelector(dropWindow1, dirSelectButton, dirpath, &staticText, &dropGrid.getMainWin()), mainDlg_(mainDlg) {} bool acceptDrop(const std::vector<wxString>& droppedFiles, const wxPoint& clientPos, const wxWindow& wnd) override @@ -134,8 +135,8 @@ public: } private: - DirectoryNameMainImpl (const DirectoryNameMainImpl&) = delete; - DirectoryNameMainImpl& operator=(const DirectoryNameMainImpl&) = delete; + FolderSelectorMainImpl (const FolderSelectorMainImpl&) = delete; + FolderSelectorMainImpl& operator=(const FolderSelectorMainImpl&) = delete; MainDialog& mainDlg_; }; @@ -199,19 +200,19 @@ public: void setValues(const FolderPairEnh& fp) { setConfig(fp.altCmpConfig, fp.altSyncConfig, fp.localFilter); - dirpathLeft .setPath(toWx(fp.dirpathPhraseLeft)); - dirpathRight.setPath(toWx(fp.dirpathPhraseRight)); + dirpathLeft .setPath(fp.dirpathPhraseLeft); + dirpathRight.setPath(fp.dirpathPhraseRight); } FolderPairEnh getValues() const { return FolderPairEnh(getLeftDir(), getRightDir(), getAltCompConfig(), getAltSyncConfig(), getAltFilterConfig()); } - Zstring getLeftDir () const { return toZ(dirpathLeft .getPath()); } - Zstring getRightDir() const { return toZ(dirpathRight.getPath()); } + Zstring getLeftDir () const { return dirpathLeft .getPath(); } + Zstring getRightDir() const { return dirpathRight.getPath(); } private: //support for drag and drop - DirectoryName<FolderHistoryBox> dirpathLeft; - DirectoryName<FolderHistoryBox> dirpathRight; + FolderSelector dirpathLeft; + FolderSelector dirpathRight; }; @@ -249,19 +250,19 @@ public: void setValues(const FolderPairEnh& fp) { setConfig(fp.altCmpConfig, fp.altSyncConfig, fp.localFilter); - dirpathLeft .setPath(toWx(fp.dirpathPhraseLeft)); - dirpathRight.setPath(toWx(fp.dirpathPhraseRight)); + dirpathLeft .setPath(fp.dirpathPhraseLeft); + dirpathRight.setPath(fp.dirpathPhraseRight); } FolderPairEnh getValues() const { return FolderPairEnh(getLeftDir(), getRightDir(), getAltCompConfig(), getAltSyncConfig(), getAltFilterConfig()); } - Zstring getLeftDir () const { return toZ(dirpathLeft .getPath()); } - Zstring getRightDir() const { return toZ(dirpathRight.getPath()); } + Zstring getLeftDir () const { return dirpathLeft .getPath(); } + Zstring getRightDir() const { return dirpathRight.getPath(); } private: //support for drag and drop - DirectoryNameMainImpl dirpathLeft; - DirectoryNameMainImpl dirpathRight; + FolderSelectorMainImpl dirpathLeft; + FolderSelectorMainImpl dirpathRight; }; @@ -797,8 +798,8 @@ MainDialog::MainDialog(const Zstring& globalConfigFile, auto addDirCheck = [&](const FolderPairEnh& fp) { - const Zstring dirLeft = getFormattedDirectoryPath(fp.dirpathPhraseLeft ); //should not block!? - const Zstring dirRight = getFormattedDirectoryPath(fp.dirpathPhraseRight); // + const Zstring dirLeft = getResolvedDisplayPath(fp.dirpathPhraseLeft ); //should not block!? + const Zstring dirRight = getResolvedDisplayPath(fp.dirpathPhraseRight); // if (dirLeft.empty() != dirRight.empty()) //only skip check if both sides are empty! havePartialPair = true; @@ -1356,11 +1357,11 @@ Zstring getExistingParentFolder(const FileSystemObject& fsObj) while (dirObj) { if (!dirObj->isEmpty<side>()) - return dirObj->getFullPath<side>(); + return ABF::getDisplayPath(dirObj->getAbstractPath<side>()); dirObj = dynamic_cast<const DirPair*>(&dirObj->parent()); } - return fsObj.getBaseDirPf<side>(); + return ABF::getDisplayPath(fsObj.getABF<side>().getAbstractPath()); } } @@ -1371,7 +1372,7 @@ void MainDialog::openExternalApplication(const wxString& commandline, const std: auto selectionTmp = selection; - const bool openFileBrowserRequested = [&]() -> bool + const bool openFileBrowserRequested = [&] { xmlAccess::XmlGlobalSettings::Gui dummy; return !dummy.externelApplications.empty() && dummy.externelApplications[0].second == commandline; @@ -1390,9 +1391,8 @@ void MainDialog::openExternalApplication(const wxString& commandline, const std: Zstring fallbackDir; if (selectionTmp.empty()) fallbackDir = leftSide ? - getFormattedDirectoryPath(firstFolderPair->getLeftDir()) : - getFormattedDirectoryPath(firstFolderPair->getRightDir()); - + getResolvedDisplayPath(firstFolderPair->getLeftDir()) : + getResolvedDisplayPath(firstFolderPair->getRightDir()); else fallbackDir = leftSide ? getExistingParentFolder<LEFT_SIDE >(*selectionTmp[0]) : @@ -1438,11 +1438,12 @@ void MainDialog::openExternalApplication(const wxString& commandline, const std: //regular command evaluation for (const FileSystemObject* fsObj : selectionTmp) //context menu calls this function only if selection is not empty! { - Zstring path1 = fsObj->getBaseDirPf<LEFT_SIDE>() + fsObj->getPairRelativePath(); //full path, even if item is not existing! - Zstring dir1 = beforeLast(path1, FILE_NAME_SEPARATOR); //Win: wrong for root paths like "C:\file.txt" + const Zstring& relPath = fsObj->getPairRelativePath(); + Zstring path1 = ABF::getDisplayPath(fsObj->getABF<LEFT_SIDE>().getAbstractPath(relPath)); //full path, even if item is not existing! + Zstring dir1 = ABF::getDisplayPath(fsObj->getABF<LEFT_SIDE>().getAbstractPath(beforeLast(relPath, FILE_NAME_SEPARATOR))); //returns empty string if term not found - Zstring path2 = fsObj->getBaseDirPf<RIGHT_SIDE>() + fsObj->getPairRelativePath(); - Zstring dir2 = beforeLast(path2, FILE_NAME_SEPARATOR); + Zstring path2 = ABF::getDisplayPath(fsObj->getABF<RIGHT_SIDE>().getAbstractPath(relPath)); + Zstring dir2 = ABF::getDisplayPath(fsObj->getABF<RIGHT_SIDE>().getAbstractPath(beforeLast(relPath, FILE_NAME_SEPARATOR))); if (!leftSide) { @@ -1501,7 +1502,7 @@ void MainDialog::setStatusBarFileStatistics(size_t filesOnLeftView, if (gridDataView->rowsTotal() > 0) { statusMiddleNew = _P("Showing %y of 1 row", "Showing %y of %x rows", gridDataView->rowsTotal()); - replace(statusMiddleNew, L"%y", toGuiString(gridDataView->rowsOnView()), false); //%x is already used as plural form placeholder! + replace(statusMiddleNew, L"%y", toGuiString(gridDataView->rowsOnView())); //%x is already used as plural form placeholder! } //fill middle text (considering flashStatusInformation()) @@ -2569,7 +2570,7 @@ void MainDialog::onDirManualCorrection(wxCommandEvent& event) wxString getFormattedHistoryElement(const Zstring& filepath) { Zstring output = afterLast(filepath, FILE_NAME_SEPARATOR); - if (endsWith(output, Zstr(".ffs_gui"))) + if (pathEndsWith(output, Zstr(".ffs_gui"))) output = beforeLast(output, Zstr('.')); return utfCvrtTo<wxString>(output); } @@ -2596,7 +2597,7 @@ void MainDialog::addFileToCfgHistory(const std::vector<Zstring>& filepaths) for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i) if (auto histData = dynamic_cast<wxClientHistoryData*>(m_listBoxHistory->GetClientObject(i))) { - if (EqualFilename()(filepath, histData->cfgFile_)) + if (EqualFilePath()(filepath, histData->cfgFile_)) return std::make_pair(histData, i); } else @@ -2616,7 +2617,7 @@ void MainDialog::addFileToCfgHistory(const std::vector<Zstring>& filepaths) wxString label; unsigned int newPos = 0; - if (EqualFilename()(filepath, lastRunConfigName())) + if (EqualFilePath()(filepath, lastRunConfigName())) label = lastSessionLabel; else { @@ -2688,7 +2689,7 @@ void MainDialog::removeCfgHistoryItems(const std::vector<Zstring>& filepaths) const int histSize = m_listBoxHistory->GetCount(); for (int i = 0; i < histSize; ++i) if (auto histData = dynamic_cast<wxClientHistoryData*>(m_listBoxHistory->GetClientObject(i))) - if (EqualFilename()(filepath, histData->cfgFile_)) + if (EqualFilePath()(filepath, histData->cfgFile_)) { m_listBoxHistory->Delete(i); break; @@ -2699,7 +2700,7 @@ void MainDialog::removeCfgHistoryItems(const std::vector<Zstring>& filepaths) void MainDialog::updateUnsavedCfgStatus() { - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilename()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilePath()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); const bool haveUnsavedCfg = lastConfigurationSaved != getConfig(); @@ -2740,7 +2741,7 @@ void MainDialog::updateUnsavedCfgStatus() void MainDialog::OnConfigSave(wxCommandEvent& event) { - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilename()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilePath()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); using namespace xmlAccess; @@ -2792,14 +2793,14 @@ bool MainDialog::trySaveConfig(const Zstring* guiFilename) //return true if save if (guiFilename) { targetFilename = *guiFilename; - assert(endsWith(targetFilename, Zstr(".ffs_gui"))); + assert(pathEndsWith(targetFilename, Zstr(".ffs_gui"))); } else { - Zstring defaultFileName = activeConfigFiles.size() == 1 && !EqualFilename()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstr("SyncSettings.ffs_gui"); + Zstring defaultFileName = activeConfigFiles.size() == 1 && !EqualFilePath()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstr("SyncSettings.ffs_gui"); //attention: activeConfigFiles may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config! - if (endsWith(defaultFileName, Zstr(".ffs_batch"))) - replace(defaultFileName, Zstr(".ffs_batch"), Zstr(".ffs_gui"), false); + if (pathEndsWith(defaultFileName, Zstr(".ffs_batch"))) + defaultFileName = beforeLast(defaultFileName, Zstr(".")) + Zstr(".ffs_gui"); wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak! wxEmptyString, @@ -2837,7 +2838,7 @@ bool MainDialog::trySaveBatchConfig(const Zstring* batchFileToUpdate) //essentially behave like trySaveConfig(): the collateral damage of not saving GUI-only settings "m_bpButtonViewTypeSyncAction" is negliable - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilename()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilePath()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); const XmlGuiConfig guiCfg = getConfig(); //prepare batch config: reuse existing batch-specific settings from file if available @@ -2874,7 +2875,7 @@ bool MainDialog::trySaveBatchConfig(const Zstring* batchFileToUpdate) if (batchFileToUpdate) { targetFilename = *batchFileToUpdate; - assert(endsWith(targetFilename, Zstr(".ffs_batch"))); + assert(pathEndsWith(targetFilename, Zstr(".ffs_batch"))); } else { @@ -2887,8 +2888,8 @@ bool MainDialog::trySaveBatchConfig(const Zstring* batchFileToUpdate) Zstring defaultFileName = !activeCfgFilename.empty() ? activeCfgFilename : Zstr("BatchRun.ffs_batch"); //attention: activeConfigFiles may be a *.ffs_gui file! We don't want to overwrite it with a BATCH config! - if (endsWith(defaultFileName, Zstr(".ffs_gui"))) - replace(defaultFileName, Zstr(".ffs_gui"), Zstr(".ffs_batch")); + if (pathEndsWith(defaultFileName, Zstr(".ffs_gui"))) + defaultFileName = beforeLast(defaultFileName, Zstr(".")) + Zstr(".ffs_batch"); wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak! wxEmptyString, @@ -2922,7 +2923,7 @@ bool MainDialog::saveOldConfig() //return false on user abort { if (lastConfigurationSaved != getConfig()) { - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilename()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilePath()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); //notify user about changed settings if (globalCfg.optDialogs.popupOnConfigChange) @@ -2981,7 +2982,7 @@ bool MainDialog::saveOldConfig() //return false on user abort void MainDialog::OnConfigLoad(wxCommandEvent& event) { - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilename()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilePath()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); wxFileDialog filePicker(this, wxEmptyString, @@ -3713,7 +3714,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event) try { //PERF_START; - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilename()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilePath()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); const auto& guiCfg = getConfig(); @@ -3736,15 +3737,18 @@ void MainDialog::OnStartSync(wxCommandEvent& event) std::unique_ptr<LockHolder> dirLocks; if (globalCfg.createLockFile) { - std::set<Zstring, LessFilename> dirpathsExisting; + std::set<Zstring, LessFilePath> dirPathsExisting; for (auto it = begin(folderCmp); it != end(folderCmp); ++it) { if (it->isExisting<LEFT_SIDE>()) //do NOT check directory existence again! - dirpathsExisting.insert(it->getBaseDirPf<LEFT_SIDE >()); + if (Opt<Zstring> nativeFolderPath = ABF::getNativeItemPath(it->getABF<LEFT_SIDE >().getAbstractPath())) //restrict directory locking to native paths until further + dirPathsExisting.insert(*nativeFolderPath); + if (it->isExisting<RIGHT_SIDE>()) - dirpathsExisting.insert(it->getBaseDirPf<RIGHT_SIDE>()); + if (Opt<Zstring> nativeFolderPath = ABF::getNativeItemPath(it->getABF<RIGHT_SIDE >().getAbstractPath())) + dirPathsExisting.insert(*nativeFolderPath); } - dirLocks = zen::make_unique<LockHolder>(dirpathsExisting, globalCfg.optDialogs.warningDirectoryLockFailed, statusHandler); + dirLocks = zen::make_unique<LockHolder>(dirPathsExisting, globalCfg.optDialogs.warningDirectoryLockFailed, statusHandler); } //START SYNCHRONIZATION @@ -4053,7 +4057,9 @@ void MainDialog::applySyncConfig() showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(warning).setCheckBox(dontWarnAgain, _("&Don't show this warning again"))); warningActive = !dontWarnAgain; } - }); + }, + nullptr //[&](std::int64_t bytesDelta){ } -> status update while loading db file + ); updateGui(); } @@ -4123,9 +4129,7 @@ void MainDialog::hideFindPanel() void MainDialog::startFindNext() //F3 or ENTER in m_textCtrlSearchTxt { - wxString searchString = m_textCtrlSearchTxt->GetValue(); - trim(searchString); - + const wxString searchString = trimCpy(m_textCtrlSearchTxt->GetValue()); if (searchString.empty()) showFindPanel(); else @@ -4159,7 +4163,7 @@ void MainDialog::startFindNext() //F3 or ENTER in m_textCtrlSearchTxt showFindPanel(); showNotificationDialog(this, DialogInfoType::INFO, PopupDialogCfg(). setTitle(_("Find")). - setMainInstructions(replaceCpy(_("Cannot find %x"), L"%x", L"\"" + searchString + L"\"", false))); + setMainInstructions(replaceCpy(_("Cannot find %x"), L"%x", L"\"" + searchString + L"\""))); } } } @@ -4506,8 +4510,8 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirPair& baseDirObj) { - header += utfCvrtTo<Utf8String>(baseDirObj.getBaseDirPf<LEFT_SIDE >()) + CSV_SEP; - header += utfCvrtTo<Utf8String>(baseDirObj.getBaseDirPf<RIGHT_SIDE>()) + '\n'; + header += utfCvrtTo<Utf8String>(ABF::getDisplayPath(baseDirObj.getABF<LEFT_SIDE >().getAbstractPath())) + CSV_SEP; + header += utfCvrtTo<Utf8String>(ABF::getDisplayPath(baseDirObj.getABF<RIGHT_SIDE>().getAbstractPath())) + '\n'; }); header += '\n'; diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h index 55f6f6c0..2e3b1652 100644 --- a/FreeFileSync/Source/ui/main_dlg.h +++ b/FreeFileSync/Source/ui/main_dlg.h @@ -59,7 +59,7 @@ private: friend class ManualDeletionHandler; friend class FolderPairFirst; friend class FolderPairPanel; - friend class DirectoryNameMainImpl; + friend class FolderSelectorMainImpl; template <class GuiPanel> friend class FolderPairCallback; friend class PanelMoveWindow; diff --git a/FreeFileSync/Source/ui/on_completion_box.cpp b/FreeFileSync/Source/ui/on_completion_box.cpp index b86727bc..27be3904 100644 --- a/FreeFileSync/Source/ui/on_completion_box.cpp +++ b/FreeFileSync/Source/ui/on_completion_box.cpp @@ -65,9 +65,7 @@ const wxEventType wxEVT_VALIDATE_USER_SELECTION = wxNewEventType(); bool isCloseProgressDlgCommand(const Zstring& value) { - auto tmp = utfCvrtTo<std::wstring>(value); - trim(tmp); - return tmp == cmdTxtCloseProgressDlg; + return trimCpy(utfCvrtTo<std::wstring>(value)) == cmdTxtCloseProgressDlg; } @@ -103,8 +101,7 @@ void OnCompletionBox::addItemHistory() { if (history_) { - Zstring command = getValue(); - trim(command); + const Zstring command = trimCpy(getValue()); if (command == utfCvrtTo<Zstring>(separationLine) || //do not add sep. line command == utfCvrtTo<Zstring>(cmdTxtCloseProgressDlg) || //do not add special command @@ -114,10 +111,10 @@ void OnCompletionBox::addItemHistory() //do not add built-in commands to history for (const auto& item : defaultCommands) if (command == utfCvrtTo<Zstring>(item.first) || - ::EqualFilename()(command, item.second)) + ::EqualFilePath()(command, item.second)) return; - vector_remove_if(*history_, [&](const Zstring& item) { return ::EqualFilename()(command, item); }); + vector_remove_if(*history_, [&](const Zstring& item) { return ::EqualFilePath()(command, item); }); history_->insert(history_->begin(), command); @@ -129,8 +126,7 @@ void OnCompletionBox::addItemHistory() Zstring OnCompletionBox::getValue() const { - auto value = copyStringTo<std::wstring>(GetValue()); - trim(value); + auto value = trimCpy(copyStringTo<std::wstring>(GetValue())); if (value == implementation::translate(cmdTxtCloseProgressDlg)) //undo translation for config file storage value = cmdTxtCloseProgressDlg; @@ -141,8 +137,7 @@ Zstring OnCompletionBox::getValue() const void OnCompletionBox::setValue(const Zstring& value) { - auto tmp = utfCvrtTo<std::wstring>(value); - trim(tmp); + auto tmp = trimCpy(utfCvrtTo<std::wstring>(value)); if (tmp == cmdTxtCloseProgressDlg) tmp = implementation::translate(cmdTxtCloseProgressDlg); //have this symbolic constant translated properly diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp index 1d461050..e05179ee 100644 --- a/FreeFileSync/Source/ui/progress_indicator.cpp +++ b/FreeFileSync/Source/ui/progress_indicator.cpp @@ -59,20 +59,12 @@ const int WINDOW_BYTES_PER_SEC = 5000; // class StopWatch { public: - StopWatch() : startTime(wxGetUTCTimeMillis().GetValue()), paused(false), elapsedUntilPause(0) {} //start running - - void restart() - { - startTime = wxGetUTCTimeMillis().GetValue(); //uses ::GetSystemTimeAsFileTime() - paused = false; - } - void pause() { if (!paused) { paused = true; - elapsedUntilPause = wxGetUTCTimeMillis().GetValue() - startTime; + elapsedUntilPause += numeric::dist(startTime, wxGetUTCTimeMillis().GetValue()); } } @@ -81,16 +73,29 @@ public: if (paused) { paused = false; - startTime = wxGetUTCTimeMillis().GetValue() - elapsedUntilPause; + startTime = wxGetUTCTimeMillis().GetValue(); } } - int64_t timeMs() const { return paused ? elapsedUntilPause : wxGetUTCTimeMillis().GetValue() - startTime; } + void restart() + { + startTime = wxGetUTCTimeMillis().GetValue(); //uses ::GetSystemTimeAsFileTime() + paused = false; + elapsedUntilPause = 0; + } + + int64_t timeMs() const + { + int64_t msTotal = elapsedUntilPause; + if (!paused) + msTotal += numeric::dist(startTime, wxGetUTCTimeMillis().GetValue()); + return msTotal; + } private: - int64_t startTime; - bool paused; - int64_t elapsedUntilPause; + long long startTime = wxGetUTCTimeMillis().GetValue(); + bool paused = false; + int64_t elapsedUntilPause = 0; }; } diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp index 9693e1e6..2b34af2f 100644 --- a/FreeFileSync/Source/ui/small_dlgs.cpp +++ b/FreeFileSync/Source/ui/small_dlgs.cpp @@ -98,7 +98,7 @@ AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent) //generate logo: put *after* first Fit() Layout(); //make sure m_panelLogo has final width (required by wxGTK) - wxImage appnameImg = createImageFromText(wxString(L"FreeFileSync ") + zen::currentVersion, + wxImage appnameImg = createImageFromText(wxString(L"FreeFileSync ") + zen::ffsVersion, wxFont(wxNORMAL_FONT->GetPointSize() * 1.8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, L"Tahoma"), *wxBLACK); //accessibility: align foreground/background colors! wxImage buildImg = createImageFromText(replaceCpy(_("Build: %x"), L"%x", build), diff --git a/FreeFileSync/Source/ui/sorting.h b/FreeFileSync/Source/ui/sorting.h index 7fb3065f..a14aeea0 100644 --- a/FreeFileSync/Source/ui/sorting.h +++ b/FreeFileSync/Source/ui/sorting.h @@ -50,7 +50,7 @@ bool lessShortFileName(const FileSystemObject& a, const FileSystemObject& b) return true; //sort directories and files/symlinks by short name - return makeSortDirection(LessFilename(), Int2Type<ascending>())(a.getItemName<side>(), b.getItemName<side>()); + return makeSortDirection(LessFilePath(), Int2Type<ascending>())(a.getItemName<side>(), b.getItemName<side>()); } template <bool ascending, SelectedSide side> inline @@ -62,7 +62,8 @@ bool lessFullPath(const FileSystemObject& a, const FileSystemObject& b) else if (b.isEmpty<side>()) return true; - return makeSortDirection(LessFilename(), Int2Type<ascending>())(a.getFullPath<side>(), b.getFullPath<side>()); + return makeSortDirection(LessFilePath(), Int2Type<ascending>())(ABF::getDisplayPath(a.getAbstractPath<side>()), + ABF::getDisplayPath(b.getAbstractPath<side>())); } @@ -90,7 +91,7 @@ bool lessRelativeFolder(const FileSystemObject& a, const FileSystemObject& b) else if (isDirectoryA) return true; - return LessFilename()(a.getPairShortName(), b.getPairShortName()); + return LessFilePath()(a.getPairShortName(), b.getPairShortName()); } @@ -170,7 +171,7 @@ bool lessExtension(const FileSystemObject& a, const FileSystemObject& b) return pos == Zstring::npos ? Zstring() : Zstring(shortName.begin() + pos + 1, shortName.end()); }; - return makeSortDirection(LessFilename(), Int2Type<ascending>())(getExtension(a), getExtension(b)); + return makeSortDirection(LessFilePath(), Int2Type<ascending>())(getExtension(a), getExtension(b)); } diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp index a1d030f7..68571294 100644 --- a/FreeFileSync/Source/ui/sync_cfg.cpp +++ b/FreeFileSync/Source/ui/sync_cfg.cpp @@ -17,7 +17,7 @@ #include <wx+/image_resources.h> #include "gui_generated.h" #include "on_completion_box.h" -#include "dir_name.h" +#include "folder_selector.h" #include "../file_hierarchy.h" #include "../lib/help_provider.h" #include "../lib/norm_filter.h" @@ -149,7 +149,7 @@ private: OnGuiError onGuiError; EnumDescrList<VersioningStyle> enumVersioningStyle; - DirectoryName<FolderHistoryBox> versioningFolder; + FolderSelector versioningFolder; }; //################################################################################################################# @@ -721,7 +721,7 @@ void ConfigDialog::setSyncOptions(const SyncOptions& so) directionCfg = so.syncCfg.directionCfg; //make working copy; ownership *not* on GUI handleDeletion = so.syncCfg.handleDeletion; - versioningFolder.setPath(utfCvrtTo<wxString>(so.syncCfg.versioningDirectory)); + versioningFolder.setPath(so.syncCfg.versioningFolderPhrase); setEnumVal(enumVersioningStyle, *m_choiceVersioningStyle, so.syncCfg.versioningStyle); //misc config @@ -736,10 +736,10 @@ ConfigDialog::SyncOptions ConfigDialog::getSyncOptions() const { SyncOptions output; - output.syncCfg.directionCfg = directionCfg; - output.syncCfg.handleDeletion = handleDeletion; - output.syncCfg.versioningDirectory = utfCvrtTo<Zstring>(versioningFolder.getPath()); - output.syncCfg.versioningStyle = getEnumVal(enumVersioningStyle, *m_choiceVersioningStyle); + output.syncCfg.directionCfg = directionCfg; + output.syncCfg.handleDeletion = handleDeletion; + output.syncCfg.versioningFolderPhrase = utfCvrtTo<Zstring>(versioningFolder.getPath()); + output.syncCfg.versioningStyle = getEnumVal(enumVersioningStyle, *m_choiceVersioningStyle); output.onGuiError = onGuiError; output.onCompletion = m_comboBoxOnCompletion->getValue(); @@ -908,9 +908,7 @@ void ConfigDialog::OnOkay(wxCommandEvent& event) if (m_checkBoxUseLocalSyncOptions->GetValue() && so.syncCfg.handleDeletion == zen::DELETE_TO_VERSIONING) { - Zstring versioningDir = so.syncCfg.versioningDirectory; - trim(versioningDir); - if (versioningDir.empty()) + if (trimCpy(so.syncCfg.versioningFolderPhrase).empty()) { m_notebook->ChangeSelection(static_cast<size_t>(SyncConfigPanel::SYNC)); showNotificationDialog(this, DialogInfoType::INFO, PopupDialogCfg().setMainInstructions(_("Please enter a target folder for versioning."))); @@ -932,12 +930,9 @@ void ConfigDialog::OnOkay(wxCommandEvent& event) FilterConfig filterCfg = getFilter(); //parameter correction: include filter must not be empty! - { - Zstring tmp = filterCfg.includeFilter; - trim(tmp); - if (tmp.empty()) - filterCfg.includeFilter = FilterConfig().includeFilter; //no need to show error message, just correct user input - } + if (trimCpy(filterCfg.includeFilter).empty()) + filterCfg.includeFilter = FilterConfig().includeFilter; //no need to show error message, just correct user input + filterCfgOut = filterCfg; //------------- synchronization panel ----------------- diff --git a/FreeFileSync/Source/ui/tree_view.cpp b/FreeFileSync/Source/ui/tree_view.cpp index 548e736f..a1831a3c 100644 --- a/FreeFileSync/Source/ui/tree_view.cpp +++ b/FreeFileSync/Source/ui/tree_view.cpp @@ -145,30 +145,28 @@ void calcPercentage(std::vector<std::pair<std::uint64_t, int*>>& workList) std::for_each(workList.begin(), workList.begin() + remainingPercent, [&](std::pair<std::uint64_t, int*>& pair) { ++*pair.second; }); } } +} -Zstring getShortDisplayNameForFolderPair(const Zstring& dirLeftPf, const Zstring& dirRightPf) //post-fixed with separator +Zstring impl::getShortDisplayNameForFolderPair(const Zstring& displayPathLeft, const Zstring& displayrPathRight) { - assert(endsWith(dirLeftPf, FILE_NAME_SEPARATOR) || dirLeftPf .empty()); - assert(endsWith(dirRightPf, FILE_NAME_SEPARATOR) || dirRightPf.empty()); - - auto itL = dirLeftPf .end(); - auto itR = dirRightPf.end(); + auto itL = displayPathLeft .end(); + auto itR = displayrPathRight.end(); for (;;) { - auto itLPrev = find_last(dirLeftPf .begin(), itL, FILE_NAME_SEPARATOR); - auto itRPrev = find_last(dirRightPf.begin(), itR, FILE_NAME_SEPARATOR); + auto itLPrev = find_last(displayPathLeft .begin(), itL, FILE_NAME_SEPARATOR); //c:\file, d:\1file have no common postfix! + auto itRPrev = find_last(displayrPathRight.begin(), itR, FILE_NAME_SEPARATOR); if (itLPrev == itL || itRPrev == itR) { if (itLPrev == itL) - itLPrev = dirLeftPf.begin(); + itLPrev = displayPathLeft.begin(); else ++itLPrev; //skip separator if (itRPrev == itR) - itRPrev = dirRightPf.begin(); + itRPrev = displayrPathRight.begin(); else ++itRPrev; @@ -186,26 +184,28 @@ Zstring getShortDisplayNameForFolderPair(const Zstring& dirLeftPf, const Zstring itR = itRPrev; } - Zstring commonPostfix(itL, dirLeftPf.end()); + Zstring commonPostfix(itL, displayPathLeft.end()); if (startsWith(commonPostfix, FILE_NAME_SEPARATOR)) commonPostfix = afterFirst(commonPostfix, FILE_NAME_SEPARATOR); - if (endsWith(commonPostfix, FILE_NAME_SEPARATOR)) - commonPostfix.resize(commonPostfix.size() - 1); if (commonPostfix.empty()) { - auto getLastComponent = [](const Zstring& dirPf) { return afterLast(beforeLast(dirPf, FILE_NAME_SEPARATOR), FILE_NAME_SEPARATOR); }; //returns the whole string if term not found - if (dirLeftPf.empty()) - return getLastComponent(dirRightPf); - else if (dirRightPf.empty()) - return getLastComponent(dirLeftPf); + auto getLastComponent = [](const Zstring& dirPath) -> Zstring + { + if (endsWith(dirPath, FILE_NAME_SEPARATOR)) //preserve trailing separator, support "C:\" + return afterLast(Zstring(dirPath.c_str(), dirPath.size() - 1), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR; + return afterLast(dirPath, FILE_NAME_SEPARATOR); //returns the whole string if term not found + }; + if (displayPathLeft.empty()) + return getLastComponent(displayrPathRight); + else if (displayrPathRight.empty()) + return getLastComponent(displayPathLeft); else - return getLastComponent(dirLeftPf) + utfCvrtTo<Zstring>(L" \u2212 ") + //= unicode minus - getLastComponent(dirRightPf); + return getLastComponent(displayPathLeft) + utfCvrtTo<Zstring>(L" \u2212 ") + //= unicode minus + getLastComponent(displayrPathRight); } return commonPostfix; } -} template <bool ascending> @@ -225,7 +225,7 @@ struct TreeView::LessShortName switch (lhs.type_) { case TreeView::TYPE_ROOT: - return makeSortDirection(LessFilename(), Int2Type<ascending>())(static_cast<const RootNodeImpl*>(lhs.node_)->displayName, + return makeSortDirection(LessFilePath(), Int2Type<ascending>())(static_cast<const RootNodeImpl*>(lhs.node_)->displayName, static_cast<const RootNodeImpl*>(rhs.node_)->displayName); case TreeView::TYPE_DIRECTORY: @@ -238,7 +238,7 @@ struct TreeView::LessShortName else if (!dirObjR) return true; - return makeSortDirection(LessFilename(), Int2Type<ascending>())(dirObjL->getPairShortName(), dirObjR->getPairShortName()); + return makeSortDirection(LessFilePath(), Int2Type<ascending>())(dirObjL->getPairShortName(), dirObjR->getPairShortName()); } case TreeView::TYPE_FILES: @@ -429,8 +429,8 @@ void TreeView::updateView(Predicate pred) else { root.baseDirObj = baseObj; - root.displayName = getShortDisplayNameForFolderPair(baseObj->getBaseDirPf<LEFT_SIDE >(), - baseObj->getBaseDirPf<RIGHT_SIDE>()); + root.displayName = impl::getShortDisplayNameForFolderPair(ABF::getDisplayPath(baseObj->getABF<LEFT_SIDE >().getAbstractPath()), + ABF::getDisplayPath(baseObj->getABF<RIGHT_SIDE>().getAbstractPath())); this->compressNode(root); //"this->" required by two-pass lookup as enforced by GCC 4.7 } @@ -665,8 +665,8 @@ void TreeView::setData(FolderComparison& newData) //remove truly empty folder pairs as early as this: we want to distinguish single/multiple folder pair cases by looking at "folderCmp" vector_remove_if(folderCmp, [](const std::shared_ptr<BaseDirPair>& baseObj) { - return baseObj->getBaseDirPf<LEFT_SIDE >().empty() && - baseObj->getBaseDirPf<RIGHT_SIDE>().empty(); + return baseObj->getABF<LEFT_SIDE >().emptyBaseFolderPath() && + baseObj->getABF<RIGHT_SIDE>().emptyBaseFolderPath(); }); } @@ -790,8 +790,8 @@ private: if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row)) if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get())) { - const wxString& dirLeft = utfCvrtTo<wxString>(root->baseDirObj_.getBaseDirPf<LEFT_SIDE >()); - const wxString& dirRight = utfCvrtTo<wxString>(root->baseDirObj_.getBaseDirPf<RIGHT_SIDE>()); + const wxString& dirLeft = utfCvrtTo<wxString>(ABF::getDisplayPath(root->baseDirObj_.getABF<LEFT_SIDE >().getAbstractPath())); + const wxString& dirRight = utfCvrtTo<wxString>(ABF::getDisplayPath(root->baseDirObj_.getABF<RIGHT_SIDE>().getAbstractPath())); if (dirLeft.empty()) return dirRight; else if (dirRight.empty()) diff --git a/FreeFileSync/Source/ui/tree_view.h b/FreeFileSync/Source/ui/tree_view.h index ea8e926d..2d057d33 100644 --- a/FreeFileSync/Source/ui/tree_view.h +++ b/FreeFileSync/Source/ui/tree_view.h @@ -183,6 +183,12 @@ private: }; +namespace impl //declare for unit tests: +{ +Zstring getShortDisplayNameForFolderPair(const Zstring& dirPathLeft, const Zstring& dirPathRight); +} + + namespace treeview { void init(Grid& grid, const std::shared_ptr<TreeView>& treeDataView); diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h index 628a367d..b02118f7 100644 --- a/FreeFileSync/Source/version/version.h +++ b/FreeFileSync/Source/version/version.h @@ -3,7 +3,8 @@ namespace zen { -const wchar_t currentVersion[] = L"6.15"; //internal linkage! +const wchar_t ffsVersion[] = L"7.0"; //internal linkage! +const wchar_t FFS_VERSION_SEPERATOR = L'.'; } #endif diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 183fc3c3..7573fcb9 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -1323,10 +1323,10 @@ void Grid::updateWindowSizes(bool updateScrollbar) auto setScrollbars2 = [&](int logWidth, int logHeight) //replace SetScrollbars, which loses precision of pixelsPerUnitX for some brain-dead reason { - mainWin_->SetVirtualSize(logWidth, logHeight); //set before calling SetScrollRate(): - //else SetScrollRate() would fail to preserve scroll position when "new virtual pixel-pos > old virtual height" + mainWin_->SetVirtualSize(logWidth, logHeight); //set before calling SetScrollRate(): + //else SetScrollRate() would fail to preserve scroll position when "new virtual pixel-pos > old virtual height" - int ppsuX = 0; //pixel per scroll unit + int ppsuX = 0; //pixel per scroll unit int ppsuY = 0; GetScrollPixelsPerUnit(&ppsuX, &ppsuY); diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp index 089acf4e..3a7ba3ee 100644 --- a/wx+/image_resources.cpp +++ b/wx+/image_resources.cpp @@ -89,8 +89,8 @@ void GlobalResources::init(const Zstring& filepath) wxImage img(streamIn, wxBITMAP_TYPE_PNG); //end this alpha/no-alpha/mask/wxDC::DrawBitmap/RTL/high-contrast-scheme interoperability nightmare here and now!!!! - //=> there's only one type of png image: with alpha channel, no mask!!! - convertToVanillaImage(img); + //=> there's only one type of png image: with alpha channel, no mask!!! + convertToVanillaImage(img); bitmaps.emplace(name, img); } diff --git a/wx+/image_tools.cpp b/wx+/image_tools.cpp index 859c9e5a..4b76fac0 100644 --- a/wx+/image_tools.cpp +++ b/wx+/image_tools.cpp @@ -190,17 +190,21 @@ void zen::convertToVanillaImage(wxImage& img) { if (!img.HasAlpha()) { + const int width = img.GetWidth (); + const int height = img.GetHeight(); + if (width <= 0 || height <= 0) return; + unsigned char mask_r = 0; unsigned char mask_g = 0; unsigned char mask_b = 0; const bool haveMask = img.HasMask() && img.GetOrFindMaskColour(&mask_r, &mask_g, &mask_b); - //check for mask before calling wxImage::GetOrFindMaskColour() to skip needlessly searching for new mask color + //check for mask before calling wxImage::GetOrFindMaskColour() to skip needlessly searching for new mask color img.SetAlpha(); - ::memset(img.GetAlpha(), wxIMAGE_ALPHA_OPAQUE, img.GetWidth() * img.GetHeight()); + ::memset(img.GetAlpha(), wxIMAGE_ALPHA_OPAQUE, width * height); - //wxWidgets, as always, tries to be more clever than it really is and fucks up wxStaticBitmap if wxBitmap is fully opaque: - img.GetAlpha()[img.GetWidth() * img.GetHeight() - 1] = 254; + //wxWidgets, as always, tries to be more clever than it really is and fucks up wxStaticBitmap if wxBitmap is fully opaque: + img.GetAlpha()[width * height - 1] = 254; if (haveMask) { @@ -208,7 +212,7 @@ void zen::convertToVanillaImage(wxImage& img) unsigned char* alphaPtr = img.GetAlpha(); const unsigned char* dataPtr = img.GetData(); - const int pixelCount = img.GetWidth() * img.GetHeight(); + const int pixelCount = width * height; for (int i = 0; i < pixelCount; ++ i) { const unsigned char r = *dataPtr++; @@ -222,8 +226,8 @@ void zen::convertToVanillaImage(wxImage& img) } } } - else - { - assert(!img.HasMask()); - } + else + { + assert(!img.HasMask()); + } }
\ No newline at end of file diff --git a/wx+/image_tools.h b/wx+/image_tools.h index b53d42ea..5e21bd40 100644 --- a/wx+/image_tools.h +++ b/wx+/image_tools.h @@ -181,10 +181,10 @@ bool isEqual(const wxBitmap& lhs, const wxBitmap& rhs) if (imLhs.HasAlpha() != imRhs.HasAlpha()) return false; - if (!std::equal(imLhs.GetData(), imLhs.GetData() + pixelCount * 3, imRhs.GetData())) - return false; + if (!std::equal(imLhs.GetData(), imLhs.GetData() + pixelCount * 3, imRhs.GetData())) + return false; - if (imLhs.HasAlpha()) + if (imLhs.HasAlpha()) if (!std::equal(imLhs.GetAlpha(), imLhs.GetAlpha() + pixelCount, imRhs.GetAlpha())) return false; diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 26645157..a948a5e8 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -11,7 +11,7 @@ #include "scope_guard.h" #ifdef ZEN_WIN - #include "notify_removal.h" + #include "device_notify.h" #include "win.h" //includes "windows.h" #include "long_path_prefix.h" @@ -161,12 +161,11 @@ public: //end of constructor, no need to start managing "hDir" } - ReadChangesAsync(ReadChangesAsync&& other) : - hDir(INVALID_HANDLE_VALUE) + ReadChangesAsync(ReadChangesAsync&& other) : shared_(std::move(other.shared_)), + dirpathPf(std::move(other.dirpathPf)), + hDir(other.hDir) { - shared_ = std::move(other.shared_); - dirpathPf = std::move(other.dirpathPf); - std::swap(hDir, other.hDir); + other.hDir = INVALID_HANDLE_VALUE; } ~ReadChangesAsync() @@ -271,15 +270,24 @@ private: }; -class HandleVolumeRemoval : public NotifyRequestDeviceRemoval +class HandleVolumeRemoval { public: HandleVolumeRemoval(HANDLE hDir, + const Zstring& displayPath, boost::thread& worker) : - NotifyRequestDeviceRemoval(hDir), //throw FileError - worker_(worker), - removalRequested(false), - operationComplete(false) {} + notificationHandle(registerFolderRemovalNotification(hDir, //throw FileError + displayPath, + [this] { this->onRequestRemoval (); }, //noexcept! + [this](bool successful) { this->onRemovalFinished(); })), // + worker_(worker), + removalRequested(false), + operationComplete(false) {} + + ~HandleVolumeRemoval() + { + unregisterDeviceNotification(notificationHandle); + } //all functions are called by main thread! @@ -287,7 +295,7 @@ public: bool finished() const { return operationComplete; } private: - void onRequestRemoval(HANDLE hnd) override + void onRequestRemoval() //noexcept! { //must release hDir immediately => stop monitoring! if (worker_.joinable()) //= join() precondition: play safe; can't trust Windows to only call-back once @@ -300,8 +308,9 @@ private: removalRequested = true; } //don't throw! - void onRemovalFinished(HANDLE hnd, bool successful) override { operationComplete = true; } //throw()! + void onRemovalFinished() { operationComplete = true; } //noexcept! + DeviceNotificationHandle* notificationHandle; boost::thread& worker_; bool removalRequested; bool operationComplete; @@ -313,20 +322,18 @@ struct DirWatcher::Pimpl { boost::thread worker; std::shared_ptr<SharedData> shared; - - Zstring dirpath; std::unique_ptr<HandleVolumeRemoval> volRemoval; }; -DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError +DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError + baseDirPath(dirPath), pimpl_(zen::make_unique<Pimpl>()) { pimpl_->shared = std::make_shared<SharedData>(); - pimpl_->dirpath = directory; - ReadChangesAsync reader(directory, pimpl_->shared); //throw FileError - pimpl_->volRemoval = zen::make_unique<HandleVolumeRemoval>(reader.getDirHandle(), pimpl_->worker); //throw FileError + ReadChangesAsync reader(dirPath, pimpl_->shared); //throw FileError + pimpl_->volRemoval = zen::make_unique<HandleVolumeRemoval>(reader.getDirHandle(), dirPath, pimpl_->worker); //throw FileError pimpl_->worker = boost::thread(std::move(reader)); } @@ -347,6 +354,7 @@ DirWatcher::~DirWatcher() std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()>& processGuiMessages) //throw FileError { std::vector<Entry> output; + pimpl_->shared->fetchChanges(output); //throw FileError //wait until device removal is confirmed, to prevent locking hDir again by some new watch! if (pimpl_->volRemoval->requestReceived()) @@ -360,10 +368,9 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void() boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(50)); } - output.emplace_back(ACTION_DELETE, pimpl_->dirpath); //report removal as change to main directory + output.emplace_back(ACTION_DELETE, baseDirPath); //report removal as change to main directory } - else //the normal case... - pimpl_->shared->fetchChanges(output); //throw FileError + return output; } @@ -373,32 +380,35 @@ struct DirWatcher::Pimpl { Pimpl() : notifDescr() {} - Zstring basedirpath; int notifDescr; std::map<int, Zstring> watchDescrs; //watch descriptor and (sub-)directory name (postfixed with separator) -> owned by "notifDescr" }; -DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError +DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError + baseDirPath(dirPath), pimpl_(zen::make_unique<Pimpl>()) { //get all subdirectories - Zstring dirpathFmt = directory; - if (endsWith(dirpathFmt, FILE_NAME_SEPARATOR)) - dirpathFmt.resize(dirpathFmt.size() - 1); + std::vector<Zstring> fullDirList { baseDirPath }; + { + std::function<void (const Zstring& path)> traverse; - std::vector<Zstring> fullDirList { dirpathFmt }; + traverse = [&traverse, &fullDirList](const Zstring& path) + { + traverseFolder(path, nullptr, + [&](const DirInfo& di ) { fullDirList.push_back(di.fullPath); traverse(di.fullPath); }, + nullptr, //don't traverse into symlinks (analog to windows build) + [&](const std::wstring& errorMsg) { throw FileError(errorMsg); }); + }; -traverseFolder(dirpathFmt, nullptr, - [&](const DirInfo& di ){ fullDirList.push_back(di.fullPath); }, - nullptr, //don't traverse into symlinks (analog to windows build) -[&](const std::wstring& errorMsg){ throw FileError(errorMsg); }); + traverse(baseDirPath); + } //init - pimpl_->basedirpath = directory; - pimpl_->notifDescr = ::inotify_init(); + pimpl_->notifDescr = ::inotify_init(); if (pimpl_->notifDescr == -1) - throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(directory)), L"inotify_init", getLastError()); + throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(baseDirPath)), L"inotify_init", getLastError()); zen::ScopeGuard guardDescr = zen::makeGuard([&] { ::close(pimpl_->notifDescr); }); @@ -410,12 +420,12 @@ traverseFolder(dirpathFmt, nullptr, initSuccess = ::fcntl(pimpl_->notifDescr, F_SETFL, flags | O_NONBLOCK) != -1; } if (!initSuccess) - throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(directory)), L"fcntl", getLastError()); + throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(baseDirPath)), L"fcntl", getLastError()); //add watches - for (const Zstring& subdir : fullDirList) + for (const Zstring& subDirPath : fullDirList) { - int wd = ::inotify_add_watch(pimpl_->notifDescr, subdir.c_str(), + int wd = ::inotify_add_watch(pimpl_->notifDescr, subDirPath.c_str(), IN_ONLYDIR | //"Only watch pathname if it is a directory." IN_DONT_FOLLOW | //don't follow symbolic links IN_CREATE | @@ -430,12 +440,13 @@ traverseFolder(dirpathFmt, nullptr, { const auto ec = getLastError(); if (ec == ENOSPC) //fix misleading system message "No space left on device" - throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)), formatSystemError(L"inotify_add_watch", ec, L"The user limit on the total number of inotify watches was reached or the kernel failed to allocate a needed resource.")); + throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subDirPath)), + formatSystemError(L"inotify_add_watch", ec, L"The user limit on the total number of inotify watches was reached or the kernel failed to allocate a needed resource.")); - throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)), formatSystemError(L"inotify_add_watch", ec)); + throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subDirPath)), formatSystemError(L"inotify_add_watch", ec)); } - pimpl_->watchDescrs.emplace(wd, appendSeparator(subdir)); + pimpl_->watchDescrs.emplace(wd, appendSeparator(subDirPath)); } guardDescr.dismiss(); @@ -465,7 +476,7 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void() if (errno == EAGAIN) //this error is ignored in all inotify wrappers I found return std::vector<Entry>(); - throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(pimpl_->basedirpath)), L"read", getLastError()); + throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(baseDirPath)), L"read", getLastError()); } std::vector<Entry> output; @@ -523,7 +534,7 @@ void eventCallback(ConstFSEventStreamRef streamRef, //events are aggregated => it's possible to see a single event with flags //kFSEventStreamEventFlagItemCreated | kFSEventStreamEventFlagItemModified | kFSEventStreamEventFlagItemRemoved - //https://developer.apple.com/library/mac/documentation/Darwin/Reference/FSEvents_Ref/index.html#//apple_ref/doc/constant_group/FSEventStreamEventFlags + //https://developer.apple.com/library/mac/documentation/Darwin/Reference/FSEvents_Ref/index.html#//apple_ref/doc/constant_group/FSEventStreamEventFlags if (eventFlags[i] & kFSEventStreamEventFlagItemCreated || eventFlags[i] & kFSEventStreamEventFlagMount) changedFiles.emplace_back(DirWatcher::ACTION_CREATE, paths[i]); @@ -555,12 +566,13 @@ struct DirWatcher::Pimpl }; -DirWatcher::DirWatcher(const Zstring& directory) : +DirWatcher::DirWatcher(const Zstring& dirPath) : + baseDirPath(dirPath), pimpl_(zen::make_unique<Pimpl>()) { - CFStringRef dirpathCf = osx::createCFString(directory.c_str()); //returns nullptr on error + CFStringRef dirpathCf = osx::createCFString(baseDirPath.c_str()); //returns nullptr on error if (!dirpathCf) - throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(directory)), L"Function call failed: createCFString"); //no error code documented! + throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(baseDirPath)), L"Function call failed: createCFString"); //no error code documented! ZEN_ON_SCOPE_EXIT(::CFRelease(dirpathCf)); CFArrayRef dirpathCfArray = ::CFArrayCreate(nullptr, //CFAllocatorRef allocator, @@ -568,7 +580,7 @@ DirWatcher::DirWatcher(const Zstring& directory) : 1, //CFIndex numValues, nullptr); //const CFArrayCallBacks* callBacks if (!dirpathCfArray) - throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(directory)), L"Function call failed: CFArrayCreate"); //no error code documented! + throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(baseDirPath)), L"Function call failed: CFArrayCreate"); //no error code documented! ZEN_ON_SCOPE_EXIT(::CFRelease(dirpathCfArray)); FSEventStreamContext context = {}; @@ -594,7 +606,7 @@ DirWatcher::DirWatcher(const Zstring& directory) : zen::ScopeGuard guardRunloop = zen::makeGuard([&] { ::FSEventStreamInvalidate(pimpl_->eventStream); }); if (!::FSEventStreamStart(pimpl_->eventStream)) - throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(directory)), L"Function call failed: FSEventStreamStart"); //no error code documented! + throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(baseDirPath)), L"Function call failed: FSEventStreamStart"); //no error code documented! guardCreate .dismiss(); guardRunloop.dismiss(); diff --git a/zen/dir_watcher.h b/zen/dir_watcher.h index b5255898..cdc80165 100644 --- a/zen/dir_watcher.h +++ b/zen/dir_watcher.h @@ -21,7 +21,8 @@ namespace zen //watch directory including subdirectories /* !Note handling of directories!: - Windows: removal of top watched directory is NOT notified (e.g. brute force usb stick removal) + Windows: removal of top watched directory is NOT notified when watching the dir handle, e.g. brute force usb stick removal, + (watchting for GUID_DEVINTERFACE_WPD OTOH works fine!) however manual unmount IS notified (e.g. usb stick removal, then re-insert), but watching is stopped! Renaming of top watched directory handled incorrectly: Not notified(!) + additional changes in subfolders now do report FILE_ACTION_MODIFIED for directory (check that should prevent this fails!) @@ -36,7 +37,7 @@ namespace zen class DirWatcher { public: - DirWatcher(const Zstring& directory); //throw FileError + DirWatcher(const Zstring& dirPath); //throw FileError ~DirWatcher(); enum ActionType @@ -62,6 +63,8 @@ private: DirWatcher (const DirWatcher&) = delete; DirWatcher& operator=(const DirWatcher&) = delete; + const Zstring baseDirPath; + struct Pimpl; std::unique_ptr<Pimpl> pimpl_; }; diff --git a/zen/file_access.cpp b/zen/file_access.cpp index ffbdc813..b1b781ee 100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -12,8 +12,8 @@ #include "file_traverser.h" #include "scope_guard.h" #include "symlink_target.h" -#include "file_io.h" #include "file_id_def.h" +#include "serialize.h" #ifdef ZEN_WIN #include <Aclapi.h> @@ -133,67 +133,38 @@ bool zen::somethingExists(const Zstring& objname) namespace { #ifdef ZEN_WIN -//fast ::GetVolumePathName() clone: let's hope it's not too simple (doesn't honor mount points) -Zstring getVolumeNameFast(const Zstring& filepath) +bool isFatDrive(const Zstring& filePath) //throw() { - //this call is expensive: ~1.5 ms! - // if (!::GetVolumePathName(filepath.c_str(), //__in LPCTSTR lpszFileName, - // fsName, //__out LPTSTR lpszVolumePathName, - // BUFFER_SIZE)) //__in DWORD cchBufferLength - // ... - // Zstring volumePath = appendSeparator(fsName); - - const Zstring nameFmt = appendSeparator(removeLongPathPrefix(filepath)); //throw() + const DWORD bufferSize = MAX_PATH + 1; + std::vector<wchar_t> buffer(bufferSize); - if (startsWith(nameFmt, Zstr("\\\\"))) //UNC path: "\\ComputerName\SharedFolder\" - { - size_t nameSize = nameFmt.size(); - const size_t posFirstSlash = nameFmt.find(Zstr("\\"), 2); - if (posFirstSlash != Zstring::npos) - { - nameSize = posFirstSlash + 1; - const size_t posSecondSlash = nameFmt.find(Zstr("\\"), posFirstSlash + 1); - if (posSecondSlash != Zstring::npos) - nameSize = posSecondSlash + 1; - } - return Zstring(nameFmt.c_str(), nameSize); //include trailing backslash! - } - else //local path: "C:\Folder\" + //this call is expensive: ~1.5 ms! + if (!::GetVolumePathName(filePath.c_str(), //__in LPCTSTR lpszFileName, + &buffer[0], //__out LPTSTR lpszVolumePathName, + bufferSize)) //__in DWORD cchBufferLength { - const size_t pos = nameFmt.find(Zstr(":\\")); - if (pos == 1) //expect single letter volume - return Zstring(nameFmt.c_str(), 3); - } - - return Zstring(); -} - - -bool isFatDrive(const Zstring& filepath) //throw() -{ - const Zstring volumePath = getVolumeNameFast(filepath); - if (volumePath.empty()) + assert(false); return false; + } - const DWORD bufferSize = MAX_PATH + 1; - wchar_t fsName[bufferSize] = {}; + const Zstring volumePath = appendSeparator(&buffer[0]); //suprisingly fast: ca. 0.03 ms per call! - if (!::GetVolumeInformation(appendSeparator(volumePath).c_str(), //__in_opt LPCTSTR lpRootPathName, + if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName, nullptr, //__out LPTSTR lpVolumeNameBuffer, 0, //__in DWORD nVolumeNameSize, nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, nullptr, //__out_opt LPDWORD lpMaximumComponentLength, nullptr, //__out_opt LPDWORD lpFileSystemFlags, - fsName, //__out LPTSTR lpFileSystemNameBuffer, + &buffer[0], //__out LPTSTR lpFileSystemNameBuffer, bufferSize)) //__in DWORD nFileSystemNameSize { - assert(false); //shouldn't happen + assert(false); return false; } - //DST hack seems to be working equally well for FAT and FAT32 (in particular creation time has 10^-2 s precision as advertised) - return fsName == Zstring(L"FAT") || - fsName == Zstring(L"FAT32"); + + return &buffer[0] == Zstring(L"FAT") || + &buffer[0] == Zstring(L"FAT32"); } @@ -267,7 +238,7 @@ std::uint64_t zen::getFilesize(const Zstring& filepath) //throw FileError } -std::uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError +std::uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, returns 0 if not available { #ifdef ZEN_WIN ULARGE_INTEGER bytesFree = {}; @@ -275,14 +246,15 @@ std::uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError &bytesFree, //__out_opt PULARGE_INTEGER lpFreeBytesAvailable, nullptr, //__out_opt PULARGE_INTEGER lpTotalNumberOfBytes, nullptr)) //__out_opt PULARGE_INTEGER lpTotalNumberOfFreeBytes - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)), L"GetDiskFreeSpaceEx", getLastError()); + throwFileError(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtFileName(path)), L"GetDiskFreeSpaceEx", getLastError()); + //return 0 if info is not available: "The GetDiskFreeSpaceEx function returns zero for lpFreeBytesAvailable for all CD requests" return get64BitUInt(bytesFree.LowPart, bytesFree.HighPart); #elif defined ZEN_LINUX || defined ZEN_MAC struct ::statfs info = {}; if (::statfs(path.c_str(), &info) != 0) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)), L"statfs", getLastError()); + throwFileError(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtFileName(path)), L"statfs", getLastError()); return static_cast<std::uint64_t>(info.f_bsize) * info.f_bavail; #endif @@ -293,8 +265,8 @@ bool zen::removeFile(const Zstring& filepath) //throw FileError { #ifdef ZEN_WIN const wchar_t functionName[] = L"DeleteFile"; - const Zstring& filepathFmt = applyLongPathPrefix(filepath); - if (!::DeleteFile(filepathFmt.c_str())) + + if (!::DeleteFile(applyLongPathPrefix(filepath).c_str())) #elif defined ZEN_LINUX || defined ZEN_MAC const wchar_t functionName[] = L"unlink"; if (::unlink(filepath.c_str()) != 0) @@ -304,9 +276,9 @@ bool zen::removeFile(const Zstring& filepath) //throw FileError #ifdef ZEN_WIN if (lastError == ERROR_ACCESS_DENIED) //function fails if file is read-only { - ::SetFileAttributes(filepathFmt.c_str(), FILE_ATTRIBUTE_NORMAL); //(try to) normalize file attributes + ::SetFileAttributes(applyLongPathPrefix(filepath).c_str(), FILE_ATTRIBUTE_NORMAL); //(try to) normalize file attributes - if (::DeleteFile(filepathFmt.c_str())) //now try again... + if (::DeleteFile(applyLongPathPrefix(filepath).c_str())) //now try again... return true; lastError = ::GetLastError(); } @@ -380,7 +352,7 @@ void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw File } } } - + //begin of "regular" error reporting const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName)); std::wstring errorDescr = formatSystemError(L"MoveFileEx", lastError); @@ -480,8 +452,8 @@ bool have8dot3NameClash(const Zstring& filepath) if (!shortName.empty() && !longName .empty() && - EqualFilename()(origName, shortName) && - !EqualFilename()(shortName, longName)) + EqualFilePath()(origName, shortName) && + !EqualFilePath()(shortName, longName)) { //for filepath short and long file name are equal and another unrelated file happens to have the same short name //e.g. filepath == "TESTWE~1", but another file is existing named "TestWeb" with short name ""TESTWE~1" @@ -526,21 +498,21 @@ private: //rename file: no copying!!! -void zen::renameFile(const Zstring& oldName, const Zstring& newName) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting +void zen::renameFile(const Zstring& itemPathOld, const Zstring& itemPathNew) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting { try { - renameFile_sub(oldName, newName); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting + renameFile_sub(itemPathOld, itemPathNew); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting } catch (const ErrorTargetExisting&) { #ifdef ZEN_WIN //try to handle issues with already existing short 8.3 file names on Windows - if (have8dot3NameClash(newName)) + if (have8dot3NameClash(itemPathNew)) { - Fix8Dot3NameClash dummy(newName); //throw FileError; move clashing filepath to the side + Fix8Dot3NameClash dummy(itemPathNew); //throw FileError; move clashing filepath to the side //now try again... - renameFile_sub(oldName, newName); //throw FileError + renameFile_sub(itemPathOld, itemPathNew); //throw FileError return; } #endif @@ -964,8 +936,8 @@ void zen::setFileTime(const Zstring& filepath, std::int64_t modTime, ProcSymlink // - utimensat() is supposed to obsolete utime/utimes and is also used by "touch" //=> let's give utimensat another chance: struct ::timespec newTimes[2] = {}; - newTimes[0].tv_nsec = UTIME_OMIT; //omit access time - newTimes[1].tv_sec = modTime; //modification time (seconds) + newTimes[0].tv_sec = modTime; //access time: using UTIME_OMIT for tv_nsec would trigger even more bugs!! https://sourceforge.net/p/freefilesync/discussion/open-discussion/thread/218564cf/ + newTimes[1].tv_sec = modTime; //modification time (seconds) if (procSl == ProcSymlink::FOLLOW) { @@ -1016,13 +988,16 @@ bool zen::supportsPermissions(const Zstring& dirpath) //throw FileError #ifdef ZEN_WIN const DWORD bufferSize = MAX_PATH + 1; std::vector<wchar_t> buffer(bufferSize); + if (!::GetVolumePathName(dirpath.c_str(), //__in LPCTSTR lpszFileName, &buffer[0], //__out LPTSTR lpszVolumePathName, bufferSize)) //__in DWORD cchBufferLength throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirpath)), L"GetVolumePathName", getLastError()); + const Zstring volumePath = appendSeparator(&buffer[0]); + DWORD fsFlags = 0; - if (!::GetVolumeInformation(&buffer[0], //__in_opt LPCTSTR lpRootPathName, + if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName, nullptr, //__out LPTSTR lpVolumeNameBuffer, 0, //__in DWORD nVolumeNameSize, nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, @@ -1309,7 +1284,7 @@ void makeDirectoryRecursively(const Zstring& directory) //FileError, ErrorTarget try { - makeDirectoryPlain(directory, Zstring(), false); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + copyNewDirectory(Zstring(), directory, false /*copyFilePermissions*/); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing } catch (const ErrorTargetPathMissing&) { @@ -1322,10 +1297,10 @@ void makeDirectoryRecursively(const Zstring& directory) //FileError, ErrorTarget { makeDirectoryRecursively(dirParent); //throw FileError, (ErrorTargetExisting) } - catch (const ErrorTargetExisting&) { /*parent directory created externally in the meantime? => NOT AN ERROR*/ } + catch (const ErrorTargetExisting&) {} //parent directory created externally in the meantime? => NOT AN ERROR; not a directory? fail in next step! //now try again... - makeDirectoryPlain(directory, Zstring(), false); //throw FileError, (ErrorTargetExisting), (ErrorTargetPathMissing) + copyNewDirectory(Zstring(), directory, false /*copyFilePermissions*/); //throw FileError, (ErrorTargetExisting), (ErrorTargetPathMissing) return; } throw; @@ -1334,7 +1309,7 @@ void makeDirectoryRecursively(const Zstring& directory) //FileError, ErrorTarget } -void zen::makeDirectory(const Zstring& directory, bool failIfExists) //throw FileError, ErrorTargetExisting +void zen::makeNewDirectory(const Zstring& directory) //throw FileError, ErrorTargetExisting { //remove trailing separator (even for C:\ root directories) const Zstring dirFormatted = endsWith(directory, FILE_NAME_SEPARATOR) ? @@ -1345,40 +1320,22 @@ void zen::makeDirectory(const Zstring& directory, bool failIfExists) //throw Fil { makeDirectoryRecursively(dirFormatted); //FileError, ErrorTargetExisting } - catch (const ErrorTargetExisting&) - { - //avoid any file system race-condition by *not* checking directory existence again here!!! - if (failIfExists) - throw; - } - catch (const FileError&) + catch (const ErrorTargetExisting&) //*something* existing: folder or FILE! { - /* - could there be situations where a directory/network path exists, - but creation fails with error different than "ErrorTargetExisting"?? - - creation of C:\ fails with ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS - */ - if (somethingExists(directory)) //a file system race-condition! don't use dirExists() => harmonize with ErrorTargetExisting! - { - assert(false); - if (failIfExists) - throw; //do NOT convert to ErrorTargetExisting: if "failIfExists", not getting a ErrorTargetExisting *atomically* is unexpected! - } - else + //avoid any file system race-condition by *not* checking existence again here!!! throw; } } -void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing - const Zstring& templateDir, +void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing bool copyFilePermissions) { #ifdef ZEN_WIN //special handling for volume root: trying to create existing root directory results in ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS! - Zstring dirTmp = removeLongPathPrefix(endsWith(directory, FILE_NAME_SEPARATOR) ? - beforeLast(directory, FILE_NAME_SEPARATOR) : - directory); + Zstring dirTmp = removeLongPathPrefix(endsWith(targetPath, FILE_NAME_SEPARATOR) ? + beforeLast(targetPath, FILE_NAME_SEPARATOR) : + targetPath); if (dirTmp.size() == 2 && std::iswalpha(dirTmp[0]) && dirTmp[1] == L':') { @@ -1398,19 +1355,19 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT //- it may fail with "wrong parameter (error code 87)" when source is on mapped online storage //- automatically copies symbolic links if encountered: unfortunately it doesn't copy symlinks over network shares but silently creates empty folders instead (on XP)! //- it isn't able to copy most junctions because of missing permissions (although target path can be retrieved alternatively!) - if (!::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), //__in LPCTSTR lpPathName, + if (!::CreateDirectory(applyLongPathPrefixCreateDir(targetPath).c_str(), //__in LPCTSTR lpPathName, nullptr)) //__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes { DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! //handle issues with already existing short 8.3 file names on Windows if (lastError == ERROR_ALREADY_EXISTS) - if (have8dot3NameClash(directory)) + if (have8dot3NameClash(targetPath)) { - Fix8Dot3NameClash dummy(directory); //throw FileError; move clashing object to the side + Fix8Dot3NameClash dummy(targetPath); //throw FileError; move clashing object to the side //now try again... - if (::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), nullptr)) + if (::CreateDirectory(applyLongPathPrefixCreateDir(targetPath).c_str(), nullptr)) lastError = ERROR_SUCCESS; else lastError = ::GetLastError(); @@ -1418,7 +1375,7 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT if (lastError != ERROR_SUCCESS) { - const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)); + const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(targetPath)); const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError); if (lastError == ERROR_ALREADY_EXISTS) @@ -1433,18 +1390,18 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO; //= default for newly created directory struct ::stat dirInfo = {}; - if (!templateDir.empty()) - if (::stat(templateDir.c_str(), &dirInfo) == 0) + if (!sourcePath.empty()) + if (::stat(sourcePath.c_str(), &dirInfo) == 0) { mode = dirInfo.st_mode; //analog to "cp" which copies "mode" (considering umask) by default mode |= S_IRWXU; //FFS only: we need full access to copy child items! "cp" seems to apply permissions *after* copying child items } //=> need copyItemPermissions() only for "chown" and umask-agnostic permissions - if (::mkdir(directory.c_str(), mode) != 0) + if (::mkdir(targetPath.c_str(), mode) != 0) { const int lastError = errno; //copy before directly or indirectly making other system calls! - const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)); + const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(targetPath)); const std::wstring errorDescr = formatSystemError(L"mkdir", lastError); if (lastError == EEXIST) @@ -1455,11 +1412,11 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT } #endif - if (!templateDir.empty()) + if (!sourcePath.empty()) { #ifdef ZEN_WIN //optional: try to copy file attributes (dereference symlinks and junctions) - const HANDLE hDirSrc = ::CreateFile(zen::applyLongPathPrefix(templateDir).c_str(), //_In_ LPCTSTR lpFileName, + const HANDLE hDirSrc = ::CreateFile(zen::applyLongPathPrefix(sourcePath).c_str(), //_In_ LPCTSTR lpFileName, 0, //_In_ DWORD dwDesiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, @@ -1474,16 +1431,16 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT BY_HANDLE_FILE_INFORMATION dirInfo = {}; if (::GetFileInformationByHandle(hDirSrc, &dirInfo)) { - ::SetFileAttributes(applyLongPathPrefix(directory).c_str(), dirInfo.dwFileAttributes); + ::SetFileAttributes(applyLongPathPrefix(targetPath).c_str(), dirInfo.dwFileAttributes); //copy "read-only and system attributes": http://blogs.msdn.com/b/oldnewthing/archive/2003/09/30/55100.aspx const bool isEncrypted = (dirInfo.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0; const bool isCompressed = (dirInfo.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; if (isEncrypted) - ::EncryptFile(directory.c_str()); //seems no long path is required (check passed!) + ::EncryptFile(targetPath.c_str()); //seems no long path is required (check passed!) - HANDLE hDirTrg = ::CreateFile(applyLongPathPrefix(directory).c_str(), //_In_ LPCTSTR lpFileName, + HANDLE hDirTrg = ::CreateFile(applyLongPathPrefix(targetPath).c_str(), //_In_ LPCTSTR lpFileName, GENERIC_READ | GENERIC_WRITE, //_In_ DWORD dwDesiredAccess, /*read access required for FSCTL_SET_COMPRESSION*/ FILE_SHARE_READ | @@ -1521,14 +1478,14 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT } #elif defined ZEN_MAC - /*int rv =*/ ::copyfile(templateDir.c_str(), directory.c_str(), 0, COPYFILE_XATTR); + /*int rv =*/ ::copyfile(sourcePath.c_str(), targetPath.c_str(), 0, COPYFILE_XATTR); #endif - zen::ScopeGuard guardNewDir = zen::makeGuard([&] { try { removeDirectory(directory); } catch (FileError&) {} }); //ensure cleanup: + zen::ScopeGuard guardNewDir = zen::makeGuard([&] { try { removeDirectory(targetPath); } catch (FileError&) {} }); //ensure cleanup: //enforce copying file permissions: it's advertized on GUI... if (copyFilePermissions) - copyItemPermissions(templateDir, directory, ProcSymlink::FOLLOW); //throw FileError + copyItemPermissions(sourcePath, targetPath, ProcSymlink::FOLLOW); //throw FileError guardNewDir.dismiss(); //target has been created successfully! } @@ -1663,7 +1620,7 @@ bool canCopyAsSparse(DWORD fileAttrSource, const Zstring& targetFile) //throw () return false; //small perf optimization: don't check "targetFile" if not needed //------------------------------------------------------------------------------------ - const DWORD bufferSize = 10000; + const DWORD bufferSize = MAX_PATH + 1; std::vector<wchar_t> buffer(bufferSize); //full pathName need not yet exist! @@ -1672,8 +1629,10 @@ bool canCopyAsSparse(DWORD fileAttrSource, const Zstring& targetFile) //throw () bufferSize)) //__in DWORD cchBufferLength return false; + const Zstring volumePath = appendSeparator(&buffer[0]); + DWORD fsFlagsTarget = 0; - if (!::GetVolumeInformation(&buffer[0], //__in_opt LPCTSTR lpRootPathName + if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName nullptr, //__out_opt LPTSTR lpVolumeNameBuffer, 0, //__in DWORD nVolumeNameSize, nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, @@ -1715,10 +1674,9 @@ bool canCopyAsSparse(const Zstring& sourceFile, const Zstring& targetFile) //thr //precondition: canCopyAsSparse() must return "true"! -void copyFileWindowsSparse(const Zstring& sourceFile, - const Zstring& targetFile, - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus, - InSyncAttributes* newAttrib) //throw FileError, ErrorTargetExisting, ErrorFileLocked +InSyncAttributes copyFileWindowsSparse(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting, ErrorFileLocked + const Zstring& targetFile, + const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus) { assert(canCopyAsSparse(sourceFile, targetFile)); @@ -1810,13 +1768,11 @@ void copyFileWindowsSparse(const Zstring& sourceFile, throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)), L"GetFileInformationByHandle", getLastError()); //return up-to-date file attributes - if (newAttrib) - { - newAttrib->fileSize = get64BitUInt(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh); - newAttrib->modificationTime = filetimeToTimeT(fileInfoSource.ftLastWriteTime); //no DST hack (yet) - newAttrib->sourceFileId = extractFileId(fileInfoSource); - newAttrib->targetFileId = extractFileId(fileInfoTarget); - } + InSyncAttributes newAttrib = {}; + newAttrib.fileSize = get64BitUInt(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh); + newAttrib.modificationTime = filetimeToTimeT(fileInfoSource.ftLastWriteTime); //no DST hack (yet) + newAttrib.sourceFileId = extractFileId(fileInfoSource); + newAttrib.targetFileId = extractFileId(fileInfoTarget); //#################### copy NTFS compressed attribute ######################### const bool sourceIsCompressed = (fileInfoSource.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; @@ -1860,7 +1816,7 @@ void copyFileWindowsSparse(const Zstring& sourceFile, } //---------------------------------------------------------------------- - const DWORD BUFFER_SIZE = 128 * 1024; //must be greater than sizeof(WIN32_STREAM_ID) + const DWORD BUFFER_SIZE = std::max(128 * 1024, static_cast<int>(sizeof(WIN32_STREAM_ID))); //must be greater than sizeof(WIN32_STREAM_ID)! std::vector<BYTE> buffer(BUFFER_SIZE); LPVOID contextRead = nullptr; //manage context for BackupRead()/BackupWrite() @@ -1886,7 +1842,7 @@ void copyFileWindowsSparse(const Zstring& sourceFile, throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"BackupRead", getLastError()); //better use fine-granular error messages "reading/writing"! if (bytesRead > BUFFER_SIZE) - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"buffer overflow"); //user should never see this + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"BackupRead: buffer overflow."); //user should never see this if (bytesRead < BUFFER_SIZE) eof = true; @@ -1902,7 +1858,7 @@ void copyFileWindowsSparse(const Zstring& sourceFile, throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)), L"BackupWrite", getLastError()); if (bytesWritten != bytesRead) - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)), L"incomplete write"); //user should never see this + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)), L"BackupWrite: incomplete write."); //user should never see this //total bytes transferred may be larger than file size! context information + ADS or smaller (sparse, compressed)! @@ -1918,7 +1874,7 @@ void copyFileWindowsSparse(const Zstring& sourceFile, //::BackupRead() silently fails reading encrypted files -> double check! if (!someBytesWritten && get64BitUInt(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh) != 0U) //note: there is no guaranteed ordering relation beween bytes transferred and file size! Consider ADS (>) and compressed/sparse files (<)! - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"unknown error"); //user should never see this -> this method is called only if "canCopyAsSparse()" + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"BackupRead: unknown error"); //user should never see this -> this method is called only if "canCopyAsSparse()" //time needs to be set at the end: BackupWrite() changes modification time if (!::SetFileTime(hFileTarget, @@ -1928,6 +1884,7 @@ void copyFileWindowsSparse(const Zstring& sourceFile, throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(targetFile)), L"SetFileTime", getLastError()); guardTarget.dismiss(); + return newAttrib; } @@ -2049,13 +2006,12 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize, const bool supportNonEncryptedDestination = winXpOrLater(); //encrypted destination is not supported with Windows 2000 -//caveat: function scope static initialization is not thread-safe in VS 2010! +//caveat: function scope static initialization is not thread-safe in VS 2010! -> still not sufficient if multiple threads access during static init!!! -void copyFileWindowsDefault(const Zstring& sourceFile, - const Zstring& targetFile, - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus, - InSyncAttributes* newAttrib) //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse +InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse + const Zstring& targetFile, + const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus) { //try to get backup read and write privileges: who knows, maybe this helps solve some obscure "access denied" errors try { activatePrivilege(SE_BACKUP_NAME); } @@ -2129,7 +2085,7 @@ void copyFileWindowsDefault(const Zstring& sourceFile, if (lastError == ERROR_INVALID_PARAMETER && isFatDrive(targetFile) && getFilesize(sourceFile) >= 4U * std::uint64_t(1024U * 1024 * 1024)) //throw FileError - errorDescr += L"\nFAT volumes cannot store files larger than 4 gigabyte."; + errorDescr += L"\nFAT volumes cannot store files larger than 4 gigabytes."; //see "Limitations of the FAT32 File System": http://support.microsoft.com/kb/314463/en-us //note: ERROR_INVALID_PARAMETER can also occur when copying to a SharePoint server or MS SkyDrive and the target filepath is of a restricted type. @@ -2139,47 +2095,45 @@ void copyFileWindowsDefault(const Zstring& sourceFile, throw FileError(errorMsg, errorDescr); } - if (newAttrib) - { - newAttrib->fileSize = get64BitUInt(cbd.fileInfoSrc.nFileSizeLow, cbd.fileInfoSrc.nFileSizeHigh); - newAttrib->modificationTime = filetimeToTimeT(cbd.fileInfoSrc.ftLastWriteTime); - newAttrib->sourceFileId = extractFileId(cbd.fileInfoSrc); - newAttrib->targetFileId = extractFileId(cbd.fileInfoTrg); - } - //caveat: - ::CopyFileEx() silently *ignores* failure to set modification time!!! => we always need to set it again but with proper error checking! // - perf: recent measurements show no slow down at all for buffered USB sticks! setFileTimeRaw(targetFile, &cbd.fileInfoSrc.ftCreationTime, cbd.fileInfoSrc.ftLastWriteTime, ProcSymlink::FOLLOW); //throw FileError guardTarget.dismiss(); //target has been created successfully! + + InSyncAttributes newAttrib = {}; + newAttrib.fileSize = get64BitUInt(cbd.fileInfoSrc.nFileSizeLow, cbd.fileInfoSrc.nFileSizeHigh); + newAttrib.modificationTime = filetimeToTimeT(cbd.fileInfoSrc.ftLastWriteTime); + newAttrib.sourceFileId = extractFileId(cbd.fileInfoSrc); + newAttrib.targetFileId = extractFileId(cbd.fileInfoTrg); + return newAttrib; } //another layer to support copying sparse files inline -void copyFileWindowsSelectRoutine(const Zstring& sourceFile, const Zstring& targetFile, const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus, InSyncAttributes* sourceAttr) +InSyncAttributes copyFileWindowsSelectRoutine(const Zstring& sourceFile, const Zstring& targetFile, const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus) { try { - copyFileWindowsDefault(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse + return copyFileWindowsDefault(sourceFile, targetFile, onUpdateCopyStatus); //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse } catch (ErrorShouldCopyAsSparse&) //we quickly check for this condition within callback of ::CopyFileEx()! { - copyFileWindowsSparse(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked + return copyFileWindowsSparse(sourceFile, targetFile, onUpdateCopyStatus); //throw FileError, ErrorTargetExisting, ErrorFileLocked } } //another layer of indirection solving 8.3 name clashes inline -void copyFileOsSpecific(const Zstring& sourceFile, - const Zstring& targetFile, - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus, - InSyncAttributes* sourceAttr) +InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, + const Zstring& targetFile, + const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus) { try { - copyFileWindowsSelectRoutine(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked + return copyFileWindowsSelectRoutine(sourceFile, targetFile, onUpdateCopyStatus); //throw FileError, ErrorTargetExisting, ErrorFileLocked } catch (const ErrorTargetExisting&) { @@ -2187,8 +2141,7 @@ void copyFileOsSpecific(const Zstring& sourceFile, if (have8dot3NameClash(targetFile)) { Fix8Dot3NameClash dummy(targetFile); //throw FileError; move clashing filepath to the side - copyFileWindowsSelectRoutine(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError; the short filepath name clash is solved, this should work now - return; + return copyFileWindowsSelectRoutine(sourceFile, targetFile, onUpdateCopyStatus); //throw FileError; the short filepath name clash is solved, this should work now } throw; } @@ -2196,10 +2149,9 @@ void copyFileOsSpecific(const Zstring& sourceFile, #elif defined ZEN_LINUX || defined ZEN_MAC -void copyFileOsSpecific(const Zstring& sourceFile, - const Zstring& targetFile, - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus, - InSyncAttributes* newAttrib) //throw FileError, ErrorTargetExisting +InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting + const Zstring& targetFile, + const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus) { FileInput fileIn(sourceFile); //throw FileError @@ -2221,40 +2173,31 @@ void copyFileOsSpecific(const Zstring& sourceFile, throw FileError(errorMsg, errorDescr); } + if (onUpdateCopyStatus) onUpdateCopyStatus(0); //throw X! + InSyncAttributes newAttrib = {}; zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); //transactional behavior: place guard after ::open() and before lifetime of FileOutput: //=> don't delete file that existed previously!!! { FileOutput fileOut(fdTarget, targetFile); //pass ownership + if (onUpdateCopyStatus) onUpdateCopyStatus(0); //throw X! - std::vector<char> buffer(128 * 1024); - do - { - const size_t bytesRead = fileIn.read(&buffer[0], buffer.size()); //throw FileError - - fileOut.write(&buffer[0], bytesRead); //throw FileError - - if (onUpdateCopyStatus) - onUpdateCopyStatus(bytesRead); //throw X! - } - while (!fileIn.eof()); + copyStream(fileIn, fileOut, std::min(fileIn .optimalBlockSize(), + fileOut.optimalBlockSize()), onUpdateCopyStatus); //throw FileError, X struct ::stat targetInfo = {}; if (::fstat(fileOut.getHandle(), &targetInfo) != 0) throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)), L"fstat", getLastError()); - if (newAttrib) //return file statistics - { - newAttrib->fileSize = sourceInfo.st_size; + newAttrib.fileSize = sourceInfo.st_size; #ifdef ZEN_MAC - newAttrib->modificationTime = sourceInfo.st_mtimespec.tv_sec; //use same time variable like setFileTimeRaw() for consistency + newAttrib.modificationTime = sourceInfo.st_mtimespec.tv_sec; //use same time variable like setFileTimeRaw() for consistency #else - newAttrib->modificationTime = sourceInfo.st_mtime; + newAttrib.modificationTime = sourceInfo.st_mtime; #endif - newAttrib->sourceFileId = extractFileId(sourceInfo); - newAttrib->targetFileId = extractFileId(targetInfo); - } + newAttrib.sourceFileId = extractFileId(sourceInfo); + newAttrib.targetFileId = extractFileId(targetInfo); #ifdef ZEN_MAC //using ::copyfile with COPYFILE_DATA seems to trigger bugs unlike our stream-based copying! @@ -2265,6 +2208,8 @@ void copyFileOsSpecific(const Zstring& sourceFile, if (::fcopyfile(fileIn.getHandle(), fileOut.getHandle(), 0, COPYFILE_XATTR) != 0) throwFileError(replaceCpy(replaceCpy(_("Cannot copy attributes from %x to %y."), L"%x", L"\n" + fmtFileName(sourceFile)), L"%y", L"\n" + fmtFileName(targetFile)), L"copyfile", getLastError()); #endif + +fileOut.close(); //throw FileError -> optional, but good place to catch errors when closing stream! } //close output file handle before setting file time //we cannot set the target file times (::futimes) while the file descriptor is still open after a write operation: @@ -2272,7 +2217,7 @@ void copyFileOsSpecific(const Zstring& sourceFile, //Linux: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=340236 // http://comments.gmane.org/gmane.linux.file-systems.cifs/2854 //OS X: https://sourceforge.net/p/freefilesync/discussion/help/thread/881357c0/ -#ifdef ZEN_MAC +#ifdef ZEN_MAC setFileTimeRaw(targetFile, &sourceInfo.st_birthtimespec, sourceInfo.st_mtimespec, ProcSymlink::FOLLOW); //throw FileError //sourceInfo.st_birthtime; -> only seconds-precision //sourceInfo.st_mtime; -> @@ -2281,6 +2226,7 @@ void copyFileOsSpecific(const Zstring& sourceFile, #endif guardTarget.dismiss(); //target has been created successfully! + return newAttrib; } #endif @@ -2288,25 +2234,21 @@ void copyFileOsSpecific(const Zstring& sourceFile, ------------------ |File Copy Layers| ------------------ - copyFile (setup transactional behavior) + copyNewFile | - copyFileWithPermissions - | - copyFileOsSpecific (solve 8.3 issue) + copyFileOsSpecific (solve 8.3 issue on Windows) | copyFileWindowsSelectRoutine / \ copyFileWindowsDefault(::CopyFileEx) copyFileWindowsSparse(::BackupRead/::BackupWrite) */ +} -inline -void copyFileWithPermissions(const Zstring& sourceFile, - const Zstring& targetFile, - bool copyFilePermissions, - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus, - InSyncAttributes* sourceAttr) + +InSyncAttributes zen::copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked + const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus) { - copyFileOsSpecific(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked + const InSyncAttributes attr = copyFileOsSpecific(sourceFile, targetFile, onUpdateCopyStatus); //throw FileError, ErrorTargetExisting, ErrorFileLocked if (copyFilePermissions) { @@ -2317,67 +2259,6 @@ void copyFileWithPermissions(const Zstring& sourceFile, guardTargetFile.dismiss(); //target has been created successfully! } -} -} - - -void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorFileLocked - const Zstring& targetFile, - bool copyFilePermissions, - bool transactionalCopy, - const std::function<void()>& onDeleteTargetFile, - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus, - InSyncAttributes* sourceAttr) -{ - if (transactionalCopy) - { - Zstring tmpTarget = targetFile + TEMP_FILE_ENDING; - - for (int i = 0;; ++i) - try - { - copyFileWithPermissions(sourceFile, tmpTarget, copyFilePermissions, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked - break; - } - catch (const ErrorTargetExisting&) //optimistic strategy: assume everything goes well, but recover on error -> minimize file accesses - { - if (i == 10) throw; //avoid endless recursion in pathological cases, e.g. https://sourceforge.net/p/freefilesync/discussion/open-discussion/thread/36adac33 - tmpTarget = targetFile + Zchar('_') + numberTo<Zstring>(i) + TEMP_FILE_ENDING; - } - - //transactional behavior: ensure cleanup; not needed before copyFileWithPermissions() which is already transactional - zen::ScopeGuard guardTempFile = zen::makeGuard([&] { try { removeFile(tmpTarget); } catch (FileError&) {} }); - - //have target file deleted (after read access on source and target has been confirmed) => allow for almost transactional overwrite - if (onDeleteTargetFile) - onDeleteTargetFile(); //throw X - - //perf: this call is REALLY expensive on unbuffered volumes! ~40% performance decrease on FAT USB stick! - renameFile(tmpTarget, targetFile); //throw FileError - - /* - CAVEAT on FAT/FAT32: the sequence of deleting the target file and renaming "file.txt.ffs_tmp" to "file.txt" does - NOT PRESERVE the creation time of the .ffs_tmp file, but SILENTLY "reuses" whatever creation time the old "file.txt" had! - This "feature" is called "File System Tunneling": - http://blogs.msdn.com/b/oldnewthing/archive/2005/07/15/439261.aspx - http://support.microsoft.com/kb/172190/en-us - */ - - guardTempFile.dismiss(); - } - else - { - /* - Note: non-transactional file copy solves at least four problems: - -> skydrive - doesn't allow for .ffs_tmp extension and returns ERROR_INVALID_PARAMETER - -> network renaming issues - -> allow for true delete before copy to handle low disk space problems - -> higher performance on non-buffered drives (e.g. usb sticks) - */ - if (onDeleteTargetFile) - onDeleteTargetFile(); - - copyFileWithPermissions(sourceFile, targetFile, copyFilePermissions, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked - } + return attr; } diff --git a/zen/file_access.h b/zen/file_access.h index bd1b0168..0dfb650e 100644 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -29,7 +29,7 @@ void setFileTime(const Zstring& filepath, std::int64_t modificationTime, ProcSym //symlink handling: always evaluate target std::uint64_t getFilesize(const Zstring& filepath); //throw FileError -std::uint64_t getFreeDiskSpace(const Zstring& path); //throw FileError +std::uint64_t getFreeDiskSpace(const Zstring& path); //throw FileError, returns 0 if not available bool removeFile(const Zstring& filepath); //throw FileError; return "false" if file is not existing void removeDirectory(const Zstring& directory, //throw FileError @@ -37,17 +37,18 @@ void removeDirectory(const Zstring& directory, //throw FileError const std::function<void (const Zstring& dirpath )>& onBeforeDirDeletion = nullptr); //one call for each *existing* object! //rename file or directory: no copying!!! -void renameFile(const Zstring& oldName, const Zstring& newName); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting +void renameFile(const Zstring& itemPathOld, const Zstring& itemPathNew); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting bool supportsPermissions(const Zstring& dirpath); //throw FileError, dereferences symlinks //if parent directory not existing: create recursively: -void makeDirectory(const Zstring& directory, bool failIfExists = false); //throw FileError, ErrorTargetExisting +void makeNewDirectory(const Zstring& directory); //throw FileError, ErrorTargetExisting //fail if already existing or parent directory not existing: //directory should not end with path separator -//templateDir may be empty -void makeDirectoryPlain(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing +void copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, bool copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + +void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions); //throw FileError struct InSyncAttributes { @@ -57,24 +58,9 @@ struct InSyncAttributes FileId targetFileId; }; -void copyFile(const Zstring& sourceFile, //throw FileError, ErrorFileLocked (Windows-only) - const Zstring& targetFile, //symlink handling: dereference source - bool copyFilePermissions, - bool transactionalCopy, - //if target is existing user needs to implement deletion: copyFile() NEVER overwrites target if already existing! - //if transactionalCopy == true, full read access on source had been proven at this point, so it's safe to delete it. - const std::function<void()>& onDeleteTargetFile, //may be nullptr; may throw! - //accummulated delta != file size! consider ADS, sparse, compressed files - const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus, //may be nullptr; may throw! - - InSyncAttributes* newAttrib = nullptr); //return current attributes at the time of copy - -//Note: it MAY happen that copyFile() leaves temp files behind, e.g. temporary network drop. -// => clean them up at an appropriate time (automatically set sync directions to delete them). They have the following ending: - -const Zchar TEMP_FILE_ENDING[] = Zstr(".ffs_tmp"); //don't use Zstring as global constant: avoid static initialization order problem in global namespace! - -void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions); //throw FileError +InSyncAttributes copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked + //accummulated delta != file size! consider ADS, sparse, compressed files + const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus); //may be nullptr; throw X! } #endif //FILE_ACCESS_H_8017341345614857 diff --git a/zen/file_id_def.h b/zen/file_id_def.h index a96e978b..7a2059a6 100644 --- a/zen/file_id_def.h +++ b/zen/file_id_def.h @@ -23,7 +23,8 @@ namespace zen typedef DWORD DeviceId; typedef ULONGLONG FileIndex; -typedef std::pair<DeviceId, FileIndex> FileId; +typedef std::pair<DeviceId, FileIndex> FileId; //optional! (however, always set on Linux, and *generally* available on Windows) + inline FileId extractFileId(const BY_HANDLE_FILE_INFORMATION& fileInfo) diff --git a/zen/file_io.cpp b/zen/file_io.cpp index d4bfdd9b..a5412f19 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -5,6 +5,7 @@ // ************************************************************************** #include "file_io.h" +#include "file_access.h" #ifdef ZEN_WIN #include "long_path_prefix.h" @@ -75,10 +76,10 @@ void checkForUnsupportedType(const Zstring& filepath) //throw FileError } -FileInput::FileInput(FileHandle handle, const Zstring& filepath) : FileInputBase(filepath), fileHandle(handle) {} +FileInput::FileInput(FileHandle handle, const Zstring& filepath) : FileBase(filepath), fileHandle(handle) {} -FileInput::FileInput(const Zstring& filepath) : FileInputBase(filepath) //throw FileError +FileInput::FileInput(const Zstring& filepath) : FileBase(filepath) //throw FileError, ErrorFileLocked { #ifdef ZEN_WIN auto createHandle = [&](DWORD dwShareMode) @@ -135,6 +136,7 @@ FileInput::FileInput(const Zstring& filepath) : FileInputBase(filepath) //throw const Zstring procList = getLockingProcessNames(filepath); //throw() if (!procList.empty()) errorDescr = _("The file is locked by another process:") + L"\n" + procList; + throw ErrorFileLocked(errorMsg, errorDescr); } throw FileError(errorMsg, errorDescr); } @@ -169,62 +171,50 @@ FileInput::~FileInput() size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError; returns actual number of bytes read { - assert(!eof() || bytesToRead == 0); -#ifdef ZEN_WIN - if (bytesToRead == 0) return 0; - - DWORD bytesRead = 0; - if (!::ReadFile(fileHandle, //__in HANDLE hFile, - buffer, //__out LPVOID lpBuffer, - static_cast<DWORD>(bytesToRead), //__in DWORD nNumberOfBytesToRead, - &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead, - nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped - throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())), L"ReadFile", getLastError()); - - if (bytesRead < bytesToRead) //verify only! - setEof(); // - - if (bytesRead > bytesToRead) - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())), L"buffer overflow"); //user should never see this - - return bytesRead; - -#elif defined ZEN_LINUX || defined ZEN_MAC - //Compare copy_reg() in copy.c: ftp://ftp.gnu.org/gnu/coreutils/coreutils-8.23.tar.xz size_t bytesReadTotal = 0; - while (bytesToRead > 0 && !eof()) //"read() with a count of 0 returns zero" => indistinguishable from eof! => check! + while (bytesToRead > 0) //"read() with a count of 0 returns zero" => indistinguishable from end of file! => check! { +#ifdef ZEN_WIN + //test for end of file: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365690%28v=vs.85%29.aspx + DWORD bytesRead = 0; + if (!::ReadFile(fileHandle, //__in HANDLE hFile, + buffer, //__out LPVOID lpBuffer, + static_cast<DWORD>(bytesToRead), //__in DWORD nNumberOfBytesToRead, + &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead, + nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped + throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilePath())), L"ReadFile", getLastError()); + +#elif defined ZEN_LINUX || defined ZEN_MAC ssize_t bytesRead = 0; do { bytesRead = ::read(fileHandle, buffer, bytesToRead); } - while (bytesRead < 0 && errno == EINTR); + while (bytesRead < 0 && errno == EINTR); //Compare copy_reg() in copy.c: ftp://ftp.gnu.org/gnu/coreutils/coreutils-8.23.tar.xz if (bytesRead < 0) - throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())), L"read", getLastError()); - else if (bytesRead == 0) //"zero indicates end of file" - setEof(); - else if (bytesRead > static_cast<ssize_t>(bytesToRead)) //better safe than sorry - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())), L"buffer overflow"); //user should never see this + throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilePath())), L"read", getLastError()); +#endif + if (bytesRead == 0) //"zero indicates end of file" + return bytesReadTotal; + else if (static_cast<size_t>(bytesRead) > bytesToRead) //better safe than sorry + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilePath())), L"ReadFile: buffer overflow."); //user should never see this //if ::read is interrupted (EINTR) right in the middle, it will return successfully with "bytesRead < bytesToRead" => loop! buffer = static_cast<char*>(buffer) + bytesRead; //suppress warning about pointer arithmetics on void* bytesToRead -= bytesRead; bytesReadTotal += bytesRead; } - return bytesReadTotal; -#endif } //---------------------------------------------------------------------------------------------------- -FileOutput::FileOutput(FileHandle handle, const Zstring& filepath) : FileOutputBase(filepath), fileHandle(handle) {} +FileOutput::FileOutput(FileHandle handle, const Zstring& filepath) : FileBase(filepath), fileHandle(handle) {} -FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : FileOutputBase(filepath) //throw FileError, ErrorTargetExisting +FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : FileBase(filepath) //throw FileError, ErrorTargetExisting { #ifdef ZEN_WIN const DWORD dwCreationDisposition = access == FileOutput::ACC_OVERWRITE ? CREATE_ALWAYS : CREATE_NEW; @@ -307,12 +297,45 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : FileOutputB } +namespace +{ +inline +FileHandle getInvalidHandle() +{ +#ifdef ZEN_WIN + return INVALID_HANDLE_VALUE; +#elif defined ZEN_LINUX || defined ZEN_MAC + return -1; +#endif +} +} + + FileOutput::~FileOutput() { + if (fileHandle != getInvalidHandle()) + try + { + close(); //throw FileError + } + catch (FileError&) { assert(false); } +} + + +void FileOutput::close() //throw FileError +{ + if (fileHandle == getInvalidHandle()) + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilePath())), L"Contract error: close() called more than once."); + ZEN_ON_SCOPE_EXIT(fileHandle = getInvalidHandle()); + + //no need to clean-up on failure here (just like there is no clean on FileOutput::write failure!) => FileOutput is not transactional! + #ifdef ZEN_WIN - ::CloseHandle(fileHandle); + if (!::CloseHandle(fileHandle)) + throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilePath())), L"CloseHandle", getLastError()); #elif defined ZEN_LINUX || defined ZEN_MAC - ::close(fileHandle); + if (::close(fileHandle) != 0) + throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilePath())), L"close", getLastError()); #endif } @@ -326,10 +349,10 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro static_cast<DWORD>(bytesToWrite), //__in DWORD nNumberOfBytesToWrite, &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten, nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped - throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())), L"WriteFile", getLastError()); + throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilePath())), L"WriteFile", getLastError()); if (bytesWritten != bytesToWrite) //must be fulfilled for synchronous writes! - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())), L"Incomplete write."); //user should never see this + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilePath())), L"WriteFile: incomplete write."); //user should never see this #elif defined ZEN_LINUX || defined ZEN_MAC while (bytesToWrite > 0) @@ -346,10 +369,10 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro if (bytesWritten == 0) //comment in safe-read.c suggests to treat this as an error due to buggy drivers errno = ENOSPC; - throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())), L"write", getLastError()); + throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilePath())), L"write", getLastError()); } if (bytesWritten > static_cast<ssize_t>(bytesToWrite)) //better safe than sorry - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())), L"buffer overflow"); //user should never see this + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilePath())), L"write: buffer overflow."); //user should never see this //if ::write() is interrupted (EINTR) right in the middle, it will return successfully with "bytesWritten < bytesToWrite"! buffer = static_cast<const char*>(buffer) + bytesWritten; //suppress warning about pointer arithmetics on void* diff --git a/zen/file_io.h b/zen/file_io.h index ee7841ca..648bafe8 100644 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -7,7 +7,6 @@ #ifndef FILEIO_89578342758342572345 #define FILEIO_89578342758342572345 -#include "file_io_base.h" #include "file_error.h" #ifdef ZEN_WIN @@ -31,30 +30,56 @@ namespace zen typedef int FileHandle; #endif -class FileInput : public FileInputBase +class FileBase { public: - FileInput(const Zstring& filepath); //throw FileError + const Zstring& getFilePath() const { return filename_; } + +protected: + FileBase(const Zstring& filename) : filename_(filename) {} + +private: + FileBase (const FileBase&) = delete; + FileBase& operator=(const FileBase&) = delete; + + const Zstring filename_; +}; + +//----------------------------------------------------------------------------------------------- + +class FileInput : public FileBase +{ +public: + FileInput(const Zstring& filepath); //throw FileError, ErrorFileLocked FileInput(FileHandle handle, const Zstring& filepath); //takes ownership! ~FileInput(); - size_t read(void* buffer, size_t bytesToRead) override; //throw FileError; returns actual number of bytes read + size_t read(void* buffer, size_t bytesToRead); //throw FileError; returns "bytesToRead", unless end of file! FileHandle getHandle() { return fileHandle; } + size_t optimalBlockSize() const { return 128 * 1024; } private: FileHandle fileHandle; }; -class FileOutput : public FileOutputBase +class FileOutput : public FileBase { public: + enum AccessFlag + { + ACC_OVERWRITE, + ACC_CREATE_NEW + }; + FileOutput(const Zstring& filepath, AccessFlag access); //throw FileError, ErrorTargetExisting FileOutput(FileHandle handle, const Zstring& filepath); //takes ownership! ~FileOutput(); + void close(); //throw FileError -> optional, but good place to catch errors when closing stream! - void write(const void* buffer, size_t bytesToWrite) override; //throw FileError + void write(const void* buffer, size_t bytesToWrite); //throw FileError FileHandle getHandle() { return fileHandle; } + size_t optimalBlockSize() const { return 128 * 1024; } private: FileHandle fileHandle; diff --git a/zen/file_io_base.h b/zen/file_io_base.h deleted file mode 100644 index 9b6b27ca..00000000 --- a/zen/file_io_base.h +++ /dev/null @@ -1,64 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef FILEIO_BASE_H_INCLUDED_23432431789615314 -#define FILEIO_BASE_H_INCLUDED_23432431789615314 - -#include "zstring.h" - -namespace zen -{ -class FileBase -{ -public: - const Zstring& getFilename() const { return filename_; } - -protected: - FileBase(const Zstring& filename) : filename_(filename) {} - ~FileBase() {} - -private: - FileBase (const FileBase&) = delete; - FileBase& operator=(const FileBase&) = delete; - - const Zstring filename_; -}; - - -class FileInputBase : public FileBase -{ -public: - virtual size_t read(void* buffer, size_t bytesToRead) = 0; //throw FileError; returns actual number of bytes read - bool eof() const { return eofReached; } //end of file reached - -protected: - FileInputBase(const Zstring& filename) : FileBase(filename), eofReached(false) {} - ~FileInputBase() {} - void setEof() { eofReached = true; } - -private: - bool eofReached; -}; - - -class FileOutputBase : public FileBase -{ -public: - enum AccessFlag - { - ACC_OVERWRITE, - ACC_CREATE_NEW - }; - virtual void write(const void* buffer, size_t bytesToWrite) = 0; //throw FileError - -protected: - FileOutputBase(const Zstring& filename) : FileBase(filename) {} - ~FileOutputBase() {} -}; - -} - -#endif //FILEIO_BASE_H_INCLUDED_23432431789615314 diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index baf40e9e..66dcd198 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -13,12 +13,12 @@ #include "file_access.h" #include "symlink_target.h" #elif defined ZEN_MAC - #include "osx_string.h" + #include "osx_string.h" #endif #if defined ZEN_LINUX || defined ZEN_MAC #include <cstddef> //offsetof - #include <unistd.h> //::pathconf() + #include <unistd.h> //::pathconf() #include <sys/stat.h> #include <dirent.h> #endif @@ -27,9 +27,9 @@ using namespace zen; void zen::traverseFolder(const Zstring& dirPath, - const std::function<void (const FileInfo& fi)>& onFile, - const std::function<void (const DirInfo& di)>& onDir, - const std::function<void (const SymlinkInfo& si)>& onLink, + const std::function<void (const FileInfo& fi)>& onFile, + const std::function<void (const DirInfo& di)>& onDir, + const std::function<void (const SymlinkInfo& si)>& onLink, const std::function<void (const std::wstring& errorMsg)>& onError) { try @@ -51,13 +51,12 @@ void zen::traverseFolder(const Zstring& dirPath, } ZEN_ON_SCOPE_EXIT(::FindClose(hDir)); - bool firstIteration = true; + bool firstIteration = true; for (;;) - { - if (firstIteration) //keep ::FindNextFile at the start of the for-loop to support "continue"! - firstIteration = false; - else - if (!::FindNextFile(hDir, &findData)) + { + if (firstIteration) //keep ::FindNextFile at the start of the for-loop to support "continue"! + firstIteration = false; + else if (!::FindNextFile(hDir, &findData)) { const DWORD lastError = ::GetLastError(); if (lastError == ERROR_NO_MORE_FILES) //not an error situation @@ -69,7 +68,7 @@ void zen::traverseFolder(const Zstring& dirPath, //skip "." and ".." const Zchar* const shortName = findData.cFileName; - if (shortName[0] == 0) throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"Data corruption: Found item without name."); + if (shortName[0] == 0) throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"FindNextFile: Data corruption, found item without name."); if (shortName[0] == L'.' && (shortName[1] == 0 || (shortName[1] == L'.' && shortName[2] == 0))) continue; @@ -94,32 +93,27 @@ void zen::traverseFolder(const Zstring& dirPath, } #elif defined ZEN_LINUX || defined ZEN_MAC - const Zstring dirPathFmt = //remove trailing slash - dirPath.size() > 1 && endsWith(dirPath, FILE_NAME_SEPARATOR) ? //exception: allow '/' - beforeLast(dirPath, FILE_NAME_SEPARATOR) : - dirPath; - /* quote: "Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede that field within the dirent structure, portable applications that use readdir_r() should allocate the buffer whose address is passed in entry as follows: len = offsetof(struct dirent, d_name) + pathconf(dirPath, _PC_NAME_MAX) + 1 entryp = malloc(len); */ - const size_t nameMax = std::max<long>(::pathconf(dirPathFmt.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1) - std::vector<char> buffer(offsetof(struct ::dirent, d_name) + nameMax + 1); + const size_t nameMax = std::max<long>(::pathconf(dirPath.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1) + std::vector<char> buffer(offsetof(struct ::dirent, d_name) + nameMax + 1); #ifdef ZEN_MAC - std::vector<char> bufferUtfDecomposed; + std::vector<char> bufferUtfDecomposed; #endif - DIR* dirObj = ::opendir(dirPathFmt.c_str()); //directory must NOT end with path separator, except "/" + DIR* dirObj = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/" if (!dirObj) - throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirPathFmt)), L"opendir", getLastError()); + throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirPath)), L"opendir", getLastError()); ZEN_ON_SCOPE_EXIT(::closedir(dirObj)); //never close nullptr handles! -> crash for (;;) { struct ::dirent* dirEntry = nullptr; if (::readdir_r(dirObj, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0) - throwFileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPathFmt)), L"readdir_r", getLastError()); + throwFileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"readdir_r", getLastError()); //don't retry but restart dir traversal on error! http://blogs.msdn.com/b/oldnewthing/archive/2014/06/12/10533529.aspx if (!dirEntry) //no more items @@ -128,7 +122,7 @@ void zen::traverseFolder(const Zstring& dirPath, //don't return "." and ".." const char* shortName = dirEntry->d_name; - if (shortName[0] == 0) throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"Data corruption: Found item without name."); + if (shortName[0] == 0) throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirPath)), L"readdir_r: Data corruption, found item without name."); if (shortName[0] == '.' && (shortName[1] == 0 || (shortName[1] == '.' && shortName[2] == 0))) continue; @@ -152,20 +146,20 @@ void zen::traverseFolder(const Zstring& dirPath, //const char* sampleDecomposed = "\x6f\xcc\x81.txt"; //const char* samplePrecomposed = "\xc3\xb3.txt"; #endif - const Zstring& itempath = appendSeparator(dirPathFmt) + shortName; + const Zstring& itempath = appendSeparator(dirPath) + shortName; struct ::stat statData = {}; - try - { - if (::lstat(itempath.c_str(), &statData) != 0) //lstat() does not resolve symlinks - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(itempath)), L"lstat", getLastError()); - } - catch (const FileError& e) - { - if (onError) - onError(e.toString()); - continue; //ignore error: skip file - } + try + { + if (::lstat(itempath.c_str(), &statData) != 0) //lstat() does not resolve symlinks + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(itempath)), L"lstat", getLastError()); + } + catch (const FileError& e) + { + if (onError) + onError(e.toString()); + continue; //ignore error: skip file + } if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks! { diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h index 19b838ef..dfdf60ba 100644 --- a/zen/long_path_prefix.h +++ b/zen/long_path_prefix.h @@ -120,8 +120,8 @@ Zstring zen::ntPathToWin32Path(const Zstring& path) //noexcept { std::vector<wchar_t> buf(bufSize); const DWORD charsWritten = ::GetEnvironmentVariable(L"SystemRoot", //_In_opt_ LPCTSTR lpName, - &buf[0], //_Out_opt_ LPTSTR lpBuffer, - bufSize); //_In_ DWORD nSize + &buf[0], //_Out_opt_ LPTSTR lpBuffer, + bufSize); //_In_ DWORD nSize if (0 < charsWritten && charsWritten < bufSize) return replaceCpy(path, L"\\SystemRoot\\", appendSeparator(Zstring(&buf[0], charsWritten)), false); diff --git a/zen/notify_removal.cpp b/zen/notify_removal.cpp deleted file mode 100644 index f528e81b..00000000 --- a/zen/notify_removal.cpp +++ /dev/null @@ -1,212 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "notify_removal.h" -#include <set> -#include <algorithm> -#include <dbt.h> -#include "scope_guard.h" - -using namespace zen; - - -class MessageProvider //administrates a single dummy window to receive messages -{ -public: - static MessageProvider& instance() //throw FileError - { - static MessageProvider inst; - return inst; - } - - struct Listener - { - virtual ~Listener() {} - virtual void onMessage(UINT message, WPARAM wParam, LPARAM lParam) = 0; //throw()! - }; - void registerListener(Listener& l) { listener.insert(&l); } - void unregisterListener(Listener& l) { listener.erase(&l); } //don't unregister objects with static lifetime - - HWND getWnd() const { return windowHandle; } //get handle in order to register additional notifications - -private: - MessageProvider(); - ~MessageProvider(); - MessageProvider (const MessageProvider&) = delete; - MessageProvider& operator=(const MessageProvider&) = delete; - - static const wchar_t dummyClassName[]; - - static LRESULT CALLBACK topWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); - - void processMessage(UINT message, WPARAM wParam, LPARAM lParam); - - const HINSTANCE hMainModule; - HWND windowHandle; - - std::set<Listener*> listener; -}; - - -const wchar_t MessageProvider::dummyClassName[] = L"E6AD5EB1-527B-4EEF-AC75-27883B233380"; //random name - - -LRESULT CALLBACK MessageProvider::topWndProc(HWND hwnd, //handle to window - UINT uMsg, //message identifier - WPARAM wParam, //first message parameter - LPARAM lParam) //second message parameter -{ - if (auto pThis = reinterpret_cast<MessageProvider*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA))) - try - { - pThis->processMessage(uMsg, wParam, lParam); //not supposed to throw! - } - catch (...) { assert(false); } - - return ::DefWindowProc(hwnd, uMsg, wParam, lParam); -} - - -MessageProvider::MessageProvider() : - hMainModule(::GetModuleHandle(nullptr)), //get program's module handle - windowHandle(nullptr) -{ - if (!hMainModule) - throwFileError(_("Unable to register to receive system messages."), L"GetModuleHandle", getLastError()); - - //register the main window class - WNDCLASS wc = {}; - wc.lpfnWndProc = topWndProc; - wc.hInstance = hMainModule; - wc.lpszClassName = dummyClassName; - - if (::RegisterClass(&wc) == 0) - throwFileError(_("Unable to register to receive system messages."), L"RegisterClass", getLastError()); - - ScopeGuard guardConstructor = zen::makeGuard([&] { this->~MessageProvider(); }); - - //create dummy-window - windowHandle = ::CreateWindow(dummyClassName, //_In_opt_ LPCTSTR lpClassName, - nullptr, //_In_opt_ LPCTSTR lpWindowName, - 0, //_In_ DWORD dwStyle, - 0, 0, 0, 0, //_In_ int x, y, nWidth, nHeight, - nullptr, //_In_opt_ HWND hWndParent; we need a toplevel window to receive device arrival events, not a message-window (HWND_MESSAGE)! - nullptr, //_In_opt_ HMENU hMenu, - hMainModule, //_In_opt_ HINSTANCE hInstance, - nullptr); //_In_opt_ LPVOID lpParam - if (!windowHandle) - throwFileError(_("Unable to register to receive system messages."), L"CreateWindow", getLastError()); - - //store this-pointer for topWndProc() to use: do this AFTER CreateWindow() to avoid processing messages while this constructor is running!!! - //unlike: http://blogs.msdn.com/b/oldnewthing/archive/2014/02/03/10496248.aspx - ::SetLastError(ERROR_SUCCESS); //[!] required for proper error handling, see MSDN, SetWindowLongPtr - if (::SetWindowLongPtr(windowHandle, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this)) == 0 && ::GetLastError() != ERROR_SUCCESS) - throwFileError(_("Unable to register to receive system messages."), L"SetWindowLongPtr", ::GetLastError()); - - guardConstructor.dismiss(); -} - - -MessageProvider::~MessageProvider() -{ - //clean-up in reverse order - if (windowHandle) - ::DestroyWindow(windowHandle); - ::UnregisterClass(dummyClassName, //LPCTSTR lpClassName OR ATOM in low-order word! - hMainModule); //HINSTANCE hInstance -} - - -void MessageProvider::processMessage(UINT message, WPARAM wParam, LPARAM lParam) -{ - for (Listener* ls : listener) - ls->onMessage(message, wParam, lParam); -} - -//#################################################################################################### - -class NotifyRequestDeviceRemoval::Pimpl : private MessageProvider::Listener -{ -public: - Pimpl(NotifyRequestDeviceRemoval& parent, HANDLE hDir) : //throw FileError - parent_(parent) - { - MessageProvider::instance().registerListener(*this); //throw FileError - - ScopeGuard guardProvider = makeGuard([&] { MessageProvider::instance().unregisterListener(*this); }); - - //register handles to receive notifications - DEV_BROADCAST_HANDLE filter = {}; - filter.dbch_size = sizeof(filter); - filter.dbch_devicetype = DBT_DEVTYP_HANDLE; - filter.dbch_handle = hDir; - - hNotification = ::RegisterDeviceNotification(MessageProvider::instance().getWnd(), //__in HANDLE hRecipient, - &filter, //__in LPVOID NotificationFilter, - DEVICE_NOTIFY_WINDOW_HANDLE); //__in DWORD Flags - if (!hNotification) - { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - if (lastError != ERROR_CALL_NOT_IMPLEMENTED && //fail on SAMBA share: this shouldn't be a showstopper! - lastError != ERROR_SERVICE_SPECIFIC_ERROR && //neither should be fail for "Pogoplug" mapped network drives - lastError != ERROR_INVALID_DATA) //this seems to happen for a NetDrive-mapped FTP server - throwFileError(_("Unable to register to receive system messages."), L"RegisterDeviceNotification", lastError); - } - - guardProvider.dismiss(); - } - - ~Pimpl() - { - if (hNotification) - ::UnregisterDeviceNotification(hNotification); - MessageProvider::instance().unregisterListener(*this); - } - -private: - Pimpl (const Pimpl&) = delete; - Pimpl& operator=(const Pimpl&) = delete; - - void onMessage(UINT message, WPARAM wParam, LPARAM lParam) override //throw()! - { - //DBT_DEVICEQUERYREMOVE example: http://msdn.microsoft.com/en-us/library/aa363427(v=VS.85).aspx - if (message == WM_DEVICECHANGE) - if (wParam == DBT_DEVICEQUERYREMOVE || - wParam == DBT_DEVICEQUERYREMOVEFAILED || - wParam == DBT_DEVICEREMOVECOMPLETE) - { - PDEV_BROADCAST_HDR header = reinterpret_cast<PDEV_BROADCAST_HDR>(lParam); - if (header->dbch_devicetype == DBT_DEVTYP_HANDLE) - { - PDEV_BROADCAST_HANDLE body = reinterpret_cast<PDEV_BROADCAST_HANDLE>(lParam); - - if (body->dbch_hdevnotify == hNotification) //is it for the notification we registered? - switch (wParam) - { - case DBT_DEVICEQUERYREMOVE: - parent_.onRequestRemoval(body->dbch_handle); - break; - case DBT_DEVICEQUERYREMOVEFAILED: - parent_.onRemovalFinished(body->dbch_handle, false); - break; - case DBT_DEVICEREMOVECOMPLETE: - parent_.onRemovalFinished(body->dbch_handle, true); - break; - } - } - } - } - - NotifyRequestDeviceRemoval& parent_; - HDEVNOTIFY hNotification; -}; - -//#################################################################################################### - -NotifyRequestDeviceRemoval::NotifyRequestDeviceRemoval(HANDLE hDir) : pimpl(zen::make_unique<Pimpl>(*this, hDir)) {} - - -NotifyRequestDeviceRemoval::~NotifyRequestDeviceRemoval() {} //make sure ~unique_ptr() works with complete type diff --git a/zen/notify_removal.h b/zen/notify_removal.h deleted file mode 100644 index 08d75818..00000000 --- a/zen/notify_removal.h +++ /dev/null @@ -1,34 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef NOTIFY_H_INCLUDED_257804267562 -#define NOTIFY_H_INCLUDED_257804267562 - -#include <memory> -#include "win.h" //includes "windows.h" -#include "file_error.h" - -//handle (user-) request for device removal via template method pattern -//evaluate directly after processing window messages -class NotifyRequestDeviceRemoval -{ -public: - NotifyRequestDeviceRemoval(HANDLE hDir); //throw FileError - virtual ~NotifyRequestDeviceRemoval(); - -private: - virtual void onRequestRemoval(HANDLE hnd) = 0; //throw()! - //NOTE: onRemovalFinished is NOT guaranteed to execute after onRequestRemoval()! but most likely will - virtual void onRemovalFinished(HANDLE hnd, bool successful) = 0; //throw()! - - NotifyRequestDeviceRemoval (NotifyRequestDeviceRemoval&) = delete; - NotifyRequestDeviceRemoval& operator=(NotifyRequestDeviceRemoval&) = delete; - - class Pimpl; - std::unique_ptr<Pimpl> pimpl; -}; - -#endif //NOTIFY_H_INCLUDED_257804267562 @@ -30,41 +30,82 @@ public: ZEN_DEPRECATE PerfTimer() : //throw TimerError - ticksPerSec_(ticksPerSec()), startTime(getTicks()), resultShown(false) + ticksPerSec_(ticksPerSec()), + resultShown(false), + startTime(getTicksNow()), + paused(false), + elapsedUntilPause(0) { //std::clock() - "counts CPU time in Linux GCC and wall time in VC++" - WTF!??? - - if (ticksPerSec_ == 0 || !startTime.isValid()) + if (ticksPerSec_ == 0) throw TimerError(); } - ~PerfTimer() { if (!resultShown) showResult(); } + ~PerfTimer() { if (!resultShown) try { showResult(); } catch (TimerError&){} } + + void pause() + { + if (!paused) + { + paused = true; + elapsedUntilPause += dist(startTime, getTicksNow()); + } + } + + void resume() + { + if (paused) + { + paused = false; + startTime = getTicksNow(); + } + } + + void restart() + { + startTime = getTicksNow(); + paused = false; + elapsedUntilPause = 0; + } + + int64_t timeMs() const + { + int64_t ticksTotal = elapsedUntilPause; + if (!paused) + ticksTotal += dist(startTime, getTicksNow()); + return 1000 * ticksTotal / ticksPerSec_; + } void showResult() { - const TickVal now = getTicks(); - if (!now.isValid()) - throw TimerError(); + const bool wasRunning = !paused; + if (wasRunning) pause(); //don't include call to MessageBox()! + ZEN_ON_SCOPE_EXIT(if (wasRunning) resume()); - const std::int64_t delta = 1000 * dist(startTime, now) / ticksPerSec_; #ifdef ZEN_WIN std::wostringstream ss; - ss << delta << L" ms"; + ss << timeMs() << L" ms"; ::MessageBox(nullptr, ss.str().c_str(), L"Timer", MB_OK); #else - std::clog << "Perf: duration: " << delta << " ms\n"; + std::clog << "Perf: duration: " << timeMs() << " ms\n"; #endif resultShown = true; + } - startTime = getTicks(); //don't include call to MessageBox()! - if (!startTime.isValid()) +private: + TickVal getTicksNow() const + { + const TickVal now = getTicks(); + if (!now.isValid()) throw TimerError(); + return now; } -private: const std::int64_t ticksPerSec_; - TickVal startTime; bool resultShown; + TickVal startTime; + bool paused; + int64_t elapsedUntilPause; }; } diff --git a/zen/recycler.cpp b/zen/recycler.cpp index 3b5ac421..ed6669ef 100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -75,10 +75,9 @@ void zen::recycleOrDelete(const std::vector<Zstring>& itempaths, const std::func { if (itempaths.empty()) return; - //::SetFileAttributes(applyLongPathPrefix(itempath).c_str(), FILE_ATTRIBUTE_NORMAL); //warning: moving long file paths to recycler does not work! - //both ::SHFileOperation() and ::IFileOperation() cannot delete a folder named "System Volume Information" with normal attributes but shamelessly report success - //both ::SHFileOperation() and ::IFileOperation() can't handle \\?\-prefix! + //both ::SHFileOperation() and ::IFileOperation cannot delete a folder named "System Volume Information" with normal attributes but shamelessly report success + //both ::SHFileOperation() and ::IFileOperation can't handle \\?\-prefix! if (vistaOrLater()) //new recycle bin usage: available since Vista { @@ -95,20 +94,28 @@ void zen::recycleOrDelete(const std::vector<Zstring>& itempaths, const std::func for (auto it = itempaths.begin(); it != itempaths.end(); ++it) //CAUTION: do not create temporary strings here!! cNames.push_back(it->c_str()); + + CallbackData cbd(notifyDeletionStatus); if (!moveToRecycleBin(&cNames[0], cNames.size(), onRecyclerCallback, &cbd)) { if (cbd.exception) std::rethrow_exception(cbd.exception); - std::wstring itempathFmt = fmtFileName(itempaths[0]); //probably not the correct file name for file lists larger than 1! - if (itempaths.size() > 1) - itempathFmt += L", ..."; //give at least some hint that there are multiple files, and the error need not be related to the first one + if (cNames.size() == 1) + throw FileError(replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtFileName(itempaths[0])), getLastErrorMessage()); - throw FileError(replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", itempathFmt), getLastErrorMessage()); //already includes details about locking errors! + //batch recycling failed: retry one-by-one to get a better error message; see FileOperation.dll + for (size_t i = 0; i < cNames.size(); ++i) + { + if (notifyDeletionStatus) notifyDeletionStatus(itempaths[i]); + + if (!moveToRecycleBin(&cNames[i], 1, nullptr, nullptr)) + throw FileError(replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtFileName(itempaths[i])), getLastErrorMessage()); //already includes details about locking errors! + } } } - else //regular recycle bin usage: available since XP + else //regular recycle bin usage: available since XP: 1. bad error reporting 2. early failure { Zstring itempathsDoubleNull; for (const Zstring& itempath : itempaths) @@ -122,7 +129,7 @@ void zen::recycleOrDelete(const std::vector<Zstring>& itempaths, const std::func fileOp.wFunc = FO_DELETE; fileOp.pFrom = itempathsDoubleNull.c_str(); fileOp.pTo = nullptr; - fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI; + fileOp.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI; fileOp.fAnyOperationsAborted = false; fileOp.hNameMappings = nullptr; fileOp.lpszProgressTitle = nullptr; @@ -130,7 +137,10 @@ void zen::recycleOrDelete(const std::vector<Zstring>& itempaths, const std::func //"You should use fully-qualified path names with this function. Using it with relative path names is not thread safe." if (::SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted) { - throw FileError(replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtFileName(itempaths[0]))); //probably not the correct file name for file list larger than 1! + std::wstring itempathFmt = fmtFileName(itempaths[0]); //probably not the correct file name for file lists larger than 1! + if (itempaths.size() > 1) + itempathFmt += L", ..."; //give at least some hint that there are multiple files, and the error need not be related to the first one + throw FileError(replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", itempathFmt)); } } } @@ -143,7 +153,7 @@ bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError return false; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it! #ifdef ZEN_WIN - recycleOrDelete({ itempath }, nullptr); //throw FileError + recycleOrDelete({ itempath }, nullptr); //throw FileError #elif defined ZEN_LINUX GFile* file = ::g_file_new_for_path(itempath.c_str()); //never fails according to docu @@ -157,7 +167,7 @@ bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtFileName(itempath)); if (!error) - throw FileError(errorMsg, L"Unknown error."); //user should never see this + throw FileError(errorMsg, L"g_file_trash: unknown error."); //user should never see this //implement same behavior as in Windows: if recycler is not existing, delete permanently if (error->code == G_IO_ERROR_NOT_SUPPORTED) diff --git a/zen/serialize.h b/zen/serialize.h index 54baf75a..4af12af1 100644 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -25,12 +25,13 @@ binary container for data storage: must support "basic" std::vector interface (e //binary container reference implementations typedef Zbase<char> Utf8String; //ref-counted + COW text stream + guaranteed performance: exponential growth -class BinaryStream; //ref-counted byte stream + guaranteed performance: exponential growth -> no COW, but 12% faster than Utf8String (due to no null-termination?) +class ByteArray; //ref-counted byte stream + guaranteed performance: exponential growth -> no COW, but 12% faster than Utf8String (due to no null-termination?) -class BinaryStream //essentially a std::vector<char> with ref-counted semantics, but no COW! => *almost* value type semantics, but not quite + +class ByteArray //essentially a std::vector<char> with ref-counted semantics, but no COW! => *almost* value type semantics, but not quite { public: - BinaryStream() : buffer(std::make_shared<std::vector<char>>()) {} + ByteArray() : buffer(std::make_shared<std::vector<char>>()) {} typedef std::vector<char>::value_type value_type; typedef std::vector<char>::iterator iterator; @@ -46,7 +47,7 @@ public: size_t size() const { return buffer->size(); } bool empty() const { return buffer->empty(); } - inline friend bool operator==(const BinaryStream& lhs, const BinaryStream& rhs) { return *lhs.buffer == *rhs.buffer; } + inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return *lhs.buffer == *rhs.buffer; } private: std::shared_ptr<std::vector<char>> buffer; //always bound! @@ -65,7 +66,7 @@ template <class BinContainer> BinContainer loadBinStream(const Zstring& filepath ----------------------------- struct BinInputStream { - const void* requestRead(size_t len); //expect external read of len bytes + size_t read(void* data, size_t len); //return "len" bytes unless end of stream! }; ------------------------------ @@ -73,58 +74,62 @@ struct BinInputStream ------------------------------ struct BinOutputStream { - void* requestWrite(size_t len); //expect external write of len bytes + void write(const void* data, size_t len); }; */ -//binary input/output stream reference implementation -class UnexpectedEndOfStreamError {}; +//binary input/output stream reference implementations: -struct BinStreamIn //throw UnexpectedEndOfStreamError +template <class BinContainer> +struct MemoryStreamIn { - BinStreamIn(const BinaryStream& cont) : buffer(cont), pos(0) {} //this better be cheap! + MemoryStreamIn(const BinContainer& cont) : buffer(cont), pos(0) {} //this better be cheap! - const void* requestRead(size_t len) //throw UnexpectedEndOfStreamError + size_t read(void* data, size_t len) //return "len" bytes unless end of stream! { - if (len == 0) return nullptr; //don't allow for possibility to access empty buffer - if (pos + len > buffer.size()) - throw UnexpectedEndOfStreamError(); - size_t oldPos = pos; - pos += len; - return &*buffer.begin() + oldPos; + static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes + const size_t bytesRead = std::min(len, buffer.size() - pos); + auto itFirst = buffer.begin() + pos; + std::copy(itFirst, itFirst + bytesRead, static_cast<char*>(data)); + pos += bytesRead; + return bytesRead; } private: - BinaryStream buffer; + const BinContainer buffer; size_t pos; }; -struct BinStreamOut +template <class BinContainer> +struct MemoryStreamOut { - void* requestWrite(size_t len) + void write(const void* data, size_t len) { - if (len == 0) return nullptr; //don't allow for possibility to access empty buffer + static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes const size_t oldSize = buffer.size(); buffer.resize(oldSize + len); - return &*buffer.begin() + oldSize; + std::copy(static_cast<const char*>(data), static_cast<const char*>(data) + len, buffer.begin() + oldSize); } - BinaryStream get() { return buffer; } + const BinContainer& ref() const { return buffer; } private: - BinaryStream buffer; + BinContainer buffer; }; //---------------------------------------------------------------------- //functions based on binary stream abstraction +template <class BinInputStream, class BinOutputStream> +void copyStream(BinInputStream& streamIn, BinOutputStream& streamOut, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus); //optional + template <class N, class BinOutputStream> void writeNumber (BinOutputStream& stream, const N& num); // template <class C, class BinOutputStream> void writeContainer(BinOutputStream& stream, const C& str); //throw () template < class BinOutputStream> void writeArray (BinOutputStream& stream, const void* data, size_t len); // //---------------------------------------------------------------------- - -template <class N, class BinInputStream> N readNumber (BinInputStream& stream); // -template <class C, class BinInputStream> C readContainer(BinInputStream& stream); //throw UnexpectedEndOfStreamError (corrupted data) +class UnexpectedEndOfStreamError {}; +template <class N, class BinInputStream> N readNumber (BinInputStream& stream); //throw UnexpectedEndOfStreamError (corrupted data) +template <class C, class BinInputStream> C readContainer(BinInputStream& stream); // template < class BinInputStream> void readArray (BinInputStream& stream, void* data, size_t len); // @@ -135,66 +140,54 @@ template < class BinInputStream> void readArray (BinInputStream& stre //-----------------------implementation------------------------------- -template <class BinContainer> inline -void saveBinStream(const Zstring& filepath, //throw FileError - const BinContainer& cont, - const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //optional +template <class BinInputStream, class BinOutputStream> inline +void copyStream(BinInputStream& streamIn, BinOutputStream& streamOut, size_t blockSize, + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //optional { - static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes (until further) - - FileOutput fileOut(filepath, zen::FileOutput::ACC_OVERWRITE); //throw FileError - if (!cont.empty()) + assert(blockSize > 0); + std::vector<char> buffer(blockSize); + for (;;) { - const size_t blockSize = 128 * 1024; - auto bytePtr = &*cont.begin(); - size_t bytesLeft = cont.size(); + const size_t bytesRead = streamIn.read(&buffer[0], buffer.size()); + streamOut.write(&buffer[0], bytesRead); - while (bytesLeft > blockSize) - { - fileOut.write(bytePtr, blockSize); //throw FileError - bytePtr += blockSize; - bytesLeft -= blockSize; - if (onUpdateStatus) onUpdateStatus(blockSize); - } + if (onNotifyCopyStatus) + onNotifyCopyStatus(bytesRead); //throw X! - fileOut.write(bytePtr, bytesLeft); //throw FileError - if (onUpdateStatus) onUpdateStatus(bytesLeft); + if (bytesRead != buffer.size()) //end of file + break; } } template <class BinContainer> inline -BinContainer loadBinStream(const Zstring& filepath, //throw FileError - const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //optional +void saveBinStream(const Zstring& filepath, //throw FileError + const BinContainer& cont, + const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //optional { - static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes (until further) - - FileInput fileIn(filepath); //throw FileError - - BinContainer contOut; - const size_t blockSize = 128 * 1024; - do - { - contOut.resize(contOut.size() + blockSize); //container better implement exponential growth! - - const size_t bytesRead = fileIn.read(&*contOut.begin() + contOut.size() - blockSize, blockSize); //throw FileError - if (bytesRead < blockSize) - contOut.resize(contOut.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics + MemoryStreamIn<BinContainer> streamIn(cont); + FileOutput streamOut(filepath, zen::FileOutput::ACC_OVERWRITE); //throw FileError, (ErrorTargetExisting) + if (onUpdateStatus) onUpdateStatus(0); //throw X! + copyStream(streamIn, streamOut, streamOut.optimalBlockSize(), onUpdateStatus); //throw FileError +} - if (onUpdateStatus) onUpdateStatus(bytesRead); - } - while (!fileIn.eof()); - return contOut; +template <class BinContainer> inline +BinContainer loadBinStream(const Zstring& filepath, //throw FileError + const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //optional +{ + FileInput streamIn(filepath); //throw FileError, ErrorFileLocked + if (onUpdateStatus) onUpdateStatus(0); //throw X! + MemoryStreamOut<BinContainer> streamOut; + copyStream(streamIn, streamOut, streamIn.optimalBlockSize(), onUpdateStatus); //throw FileError + return streamOut.ref(); } template <class BinOutputStream> inline void writeArray(BinOutputStream& stream, const void* data, size_t len) { - std::copy(static_cast<const char*>(data), - static_cast<const char*>(data) + len, - static_cast< char*>(stream.requestWrite(len))); + stream.write(data, len); } @@ -219,9 +212,9 @@ void writeContainer(BinOutputStream& stream, const C& cont) //don't even conside template <class BinInputStream> inline void readArray(BinInputStream& stream, void* data, size_t len) //throw UnexpectedEndOfStreamError { - //expect external write of len bytes: - const char* const src = static_cast<const char*>(stream.requestRead(len)); //throw UnexpectedEndOfStreamError - std::copy(src, src + len, static_cast<char*>(data)); + const size_t bytesRead = stream.read(data, len); + if (bytesRead < len) + throw UnexpectedEndOfStreamError(); } diff --git a/zen/string_base.h b/zen/string_base.h index b1d7102e..be3b532e 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -287,8 +287,8 @@ template <class Char, template <class, class> class SP, class AP> inline Zbase<C template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, Char rhs) { return Zbase<Char, SP, AP>(lhs) += rhs; } //don't use unified first argument but save one move-construction in the r-value case instead! -template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP>&& lhs, const Zbase<Char, SP, AP>& rhs) { return std::move(lhs += rhs); } //is the move really needed? -template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP>&& lhs, const Char* rhs) { return std::move(lhs += rhs); } //lhs, is an l-vlaue in the function body... +template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP>&& lhs, const Zbase<Char, SP, AP>& rhs) { return std::move(lhs += rhs); } //the move *is* needed!!! +template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP>&& lhs, const Char* rhs) { return std::move(lhs += rhs); } //lhs, is an l-value parameter... template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP>&& lhs, Char rhs) { return std::move(lhs += rhs); } //and not a local variable => no copy elision template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+( Char lhs, const Zbase<Char, SP, AP>& rhs) { return Zbase<Char, SP, AP>(lhs) += rhs; } diff --git a/zen/string_tools.h b/zen/string_tools.h index 35a07b64..c8591522 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -35,7 +35,8 @@ template <class S, class T> S afterFirst (const S& str, const T& term); //return template <class S, class T> S beforeFirst(const S& str, const T& term); //returns the whole string if term not found template <class S, class T> std::vector<S> split(const S& str, const T& delimiter); -template <class S> void trim(S& str, bool fromLeft = true, bool fromRight = true); +template <class S> void trim ( S& str, bool fromLeft = true, bool fromRight = true); +template <class S> S trimCpy(const S& str, bool fromLeft = true, bool fromRight = true); template <class S, class T, class U> void replace ( S& str, const T& oldTerm, const U& newTerm, bool replaceAll = true); template <class S, class T, class U> S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll = true); @@ -331,9 +332,9 @@ void trim(S& str, bool fromLeft, bool fromRight) assert(fromLeft || fromRight); typedef typename GetCharType<S>::Type CharType; //don't use value_type! (wxString, Glib::ustring) - const CharType* const oldBegin = str.c_str(); - const CharType* newBegin = oldBegin; - const CharType* newEnd = oldBegin + str.length(); + const CharType* const oldBegin = strBegin(str); + const CharType* newBegin = oldBegin; + const CharType* newEnd = oldBegin + strLength(str); if (fromRight) while (newBegin != newEnd && isWhiteSpace(newEnd[-1])) @@ -350,6 +351,16 @@ void trim(S& str, bool fromLeft, bool fromRight) } +template <class S> inline +S trimCpy(const S& str, bool fromLeft, bool fromRight) +{ + //implementing trimCpy() in terms of trim(), instead of the other way round, avoids memory allocations when trimming from right! + S tmp = str; + trim(tmp, fromLeft, fromRight); + return tmp; +} + + namespace implementation { template <class S, class T> diff --git a/zen/string_traits.h b/zen/string_traits.h index 8bc55a6a..8c4775f4 100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -176,8 +176,8 @@ size_t cStringLength(const C* str) //naive implementation seems somewhat faster } -template <class S> inline -const typename GetCharType<S>::Type* strBegin(const S& str, typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type* = nullptr) //SFINAE: T must be a "string" +template <class S, typename = typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type> inline +const typename GetCharType<S>::Type* strBegin(const S& str) //SFINAE: T must be a "string" { return str.c_str(); } @@ -190,8 +190,8 @@ inline const char* strBegin(const StringRef<char >& ref) { return ref.data( inline const wchar_t* strBegin(const StringRef<wchar_t>& ref) { return ref.data(); } -template <class S> inline -size_t strLength(const S& str, typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type* = nullptr) //SFINAE: T must be a "string" +template <class S, typename = typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type> inline +size_t strLength(const S& str) //SFINAE: T must be a "string" { return str.length(); } diff --git a/zen/symlink_target.h b/zen/symlink_target.h index 9239385a..c895d9a0 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -165,13 +165,13 @@ Zstring getResolvedFilePath_impl(const Zstring& linkPath) //throw FileError const HANDLE hFile = ::CreateFile(applyLongPathPrefix(linkPath).c_str(), //_In_ LPCTSTR lpFileName, - 0, //_In_ DWORD dwDesiredAccess, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, - nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, - OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, - //needed to open a directory: - FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, - nullptr); //_In_opt_ HANDLE hTemplateFile + 0, //_In_ DWORD dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, + nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, + OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, + //needed to open a directory: + FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, + nullptr); //_In_opt_ HANDLE hTemplateFile if (hFile == INVALID_HANDLE_VALUE) throwFileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), L"CreateFile", getLastError()); ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); diff --git a/zen/thread.h b/zen/thread.h index d860abd0..c9b4c76f 100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -101,7 +101,7 @@ auto async(Function fun) -> boost::unique_future<decltype(fun())> #endif auto fut = pt.get_future(); boost::thread(std::move(pt)).detach(); //we have to explicitly detach since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!! - return std::move(fut); //compiler error without "move", why needed??? + return fut; } @@ -218,7 +218,7 @@ String formatTime(const String2& format, const TimeComp& comp, UserDefinedFormat std::mktime(&ctc); // unfortunately std::strftime() needs all elements of "struct tm" filled, e.g. tm_wday, tm_yday //note: although std::mktime() explicitly expects "local time", calculating weekday and day of year *should* be time-zone and DST independent - CharType buffer[256] = {}; + CharType buffer[256] = {}; const size_t charsWritten = strftimeWrap(buffer, 256, strBegin(format), &ctc); return String(buffer, charsWritten); } @@ -237,11 +237,11 @@ TimeComp localTime(time_t utc) { struct ::tm lt = {}; - //use thread-safe variants of localtime()! + //use thread-safe variants of localtime()! #ifdef ZEN_WIN if (::localtime_s(<, &utc) != 0) #else - if (::localtime_r(&utc, <) == nullptr) + if (::localtime_r(&utc, <) == nullptr) #endif return TimeComp(); diff --git a/zen/type_traits.h b/zen/type_traits.h index 082baea9..e03085d9 100644 --- a/zen/type_traits.h +++ b/zen/type_traits.h @@ -38,10 +38,10 @@ struct ResultType //Herb Sutter's signedness conversion helpers: http://herbsutter.com/2013/06/13/gotw-93-solution-auto-variables-part-2/ template<class T> inline -typename std::make_signed<T>::type makeSigned(T t) { return typename std::make_signed<T>::type(t); } +typename std::make_signed<T>::type makeSigned(T t) { return static_cast<typename std::make_signed<T>::type>(t); } template<class T> inline -typename std::make_unsigned<T>::type makeUnsigned(T t) { return typename std::make_unsigned<T>::type(t); } +typename std::make_unsigned<T>::type makeUnsigned(T t) { return static_cast<typename std::make_unsigned<T>::type>(t); } //################# Built-in Types ######################## //Example: "IsSignedInt<int>::value" evaluates to "true" diff --git a/zen/win_ver.h b/zen/win_ver.h index 611a27c0..2e8f1ee7 100644 --- a/zen/win_ver.h +++ b/zen/win_ver.h @@ -40,7 +40,7 @@ const OsVersion osVersionWin2000 (5, 0); /* 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 + VerifyVersionInfo -> always reports *real* Windows Version *) Win10 Technical preview caveat: VerifyVersionInfo returns 6.3 unless manifest entry is added!!! */ @@ -118,11 +118,17 @@ bool runningWOW64() //test if process is running under WOW64: http://msdn.micros } +template <bool is64BitBuild> inline +bool running64BitWindowsImpl() { return true; } + +template <> inline +bool running64BitWindowsImpl<false>() { return runningWOW64(); } + inline bool running64BitWindows() //http://blogs.msdn.com/b/oldnewthing/archive/2005/02/01/364563.aspx { static_assert(zen::is32BitBuild || zen::is64BitBuild, ""); - return is64BitBuild || runningWOW64(); //should we bother to give a compile-time result in the first case? + return running64BitWindowsImpl<is64BitBuild>(); } } diff --git a/zen/xml_io.cpp b/zen/xml_io.cpp index a070b526..a1bf05d0 100644 --- a/zen/xml_io.cpp +++ b/zen/xml_io.cpp @@ -16,36 +16,26 @@ XmlDoc zen::loadXmlDocument(const Zstring& filepath) //throw FileError { //can't simply use zen::loadBinStream() due to the short-circuit xml-validation below! - std::string stream; - - FileInput inputFile(filepath); //throw FileError + FileInput fileStreamIn(filepath); //throw FileError + MemoryStreamOut<std::string> memStreamOut; { //quick test whether input is an XML: avoid loading large binary files up front! const std::string xmlBegin = "<?xml version="; - stream.resize(strLength(BYTE_ORDER_MARK_UTF8) + xmlBegin.size()); + std::vector<char> buf(xmlBegin.size() + strLength(BYTE_ORDER_MARK_UTF8)); - const size_t bytesRead = inputFile.read(&stream[0], stream.size()); //throw FileError - stream.resize(bytesRead); + const size_t bytesRead = fileStreamIn.read(&buf[0], buf.size()); + memStreamOut.write(&buf[0], bytesRead); - if (!startsWith(stream, xmlBegin) && - !startsWith(stream, BYTE_ORDER_MARK_UTF8 + xmlBegin)) //allow BOM! + if (!startsWith(memStreamOut.ref(), xmlBegin) && + !startsWith(memStreamOut.ref(), BYTE_ORDER_MARK_UTF8 + xmlBegin)) //allow BOM! throw FileError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filepath))); } - const size_t blockSize = 128 * 1024; - do - { - stream.resize(stream.size() + blockSize); - - const size_t bytesRead = inputFile.read(&*stream.begin() + stream.size() - blockSize, blockSize); //throw FileError - if (bytesRead < blockSize) - stream.resize(stream.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics - } - while (!inputFile.eof()); + copyStream(fileStreamIn, memStreamOut, fileStreamIn.optimalBlockSize(), nullptr); //throw FileError try { - return parse(stream); //throw XmlParsingError + return parse(memStreamOut.ref()); //throw XmlParsingError } catch (const XmlParsingError& e) { diff --git a/zen/zstring.cpp b/zen/zstring.cpp index f803f160..73ef3ee9 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -17,7 +17,7 @@ #endif #ifndef NDEBUG - #include <mutex> + #include "thread.h" #include <iostream> #endif @@ -39,14 +39,14 @@ public: void insert(const void* ptr, size_t size) { - std::lock_guard<std::mutex> dummy(lockActStrings); + boost::lock_guard<boost::mutex> dummy(lockActStrings); if (!activeStrings.emplace(ptr, size).second) reportProblem("Serious Error: New memory points into occupied space: " + rawMemToString(ptr, size)); } void remove(const void* ptr) { - std::lock_guard<std::mutex> dummy(lockActStrings); + boost::lock_guard<boost::mutex> dummy(lockActStrings); if (activeStrings.erase(ptr) != 1) reportProblem("Serious Error: No memory available for deallocation at this location!"); } @@ -92,15 +92,15 @@ private: #else std::cerr << message; #endif - throw std::logic_error("Memory leak! " + message + "\n" + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); + throw std::logic_error("Memory leak! " + message + "\n" + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); } - std::mutex lockActStrings; + boost::mutex lockActStrings; std::unordered_map<const void*, size_t> activeStrings; }; //caveat: function scope static initialization is not thread-safe in VS 2010! -auto& dummy = LeakChecker::get(); +auto& dummy = LeakChecker::get(); //still not sufficient if multiple threads access during static init!!! } void z_impl::leakCheckerInsert(const void* ptr, size_t size) { LeakChecker::get().insert(ptr, size); } @@ -143,8 +143,8 @@ const LCID ZSTRING_INVARIANT_LOCALE = zen::winXpOrLater() ? typedef int (WINAPI* CompareStringOrdinalFunc)(LPCWSTR lpString1, int cchCount1, LPCWSTR lpString2, int cchCount2, BOOL bIgnoreCase); const SysDllFun<CompareStringOrdinalFunc> compareStringOrdinal = SysDllFun<CompareStringOrdinalFunc>(L"kernel32.dll", "CompareStringOrdinal"); +//watch for dependencies in global namespace!!! //caveat: function scope static initialization is not thread-safe in VS 2010! -//No global dependencies => no static initialization order problem in global namespace! } @@ -158,7 +158,7 @@ int cmpFileName(const Zstring& lhs, const Zstring& rhs) static_cast<int>(rhs.size()), //__in int cchCount2, true); //__in BOOL bIgnoreCase if (rv <= 0) - throw std::runtime_error("Error comparing strings (CompareStringOrdinal). " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); + throw std::runtime_error("Error comparing strings (CompareStringOrdinal). " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); else return rv - 2; //convert to C-style string compare result } @@ -184,7 +184,7 @@ int cmpFileName(const Zstring& lhs, const Zstring& rhs) static_cast<int>(minSize), //__in int cchSrc, strOut, //__out LPTSTR lpDestStr, static_cast<int>(minSize)) == 0) //__in int cchDest - throw std::runtime_error("Error comparing strings (LCMapString). " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); + throw std::runtime_error("Error comparing strings (LCMapString). " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); }; auto eval = [&](wchar_t* bufL, wchar_t* bufR) @@ -231,7 +231,7 @@ Zstring makeUpperCopy(const Zstring& str) len, //__in int cchSrc, &*output.begin(), //__out LPTSTR lpDestStr, len) == 0) //__in int cchDest - throw std::runtime_error("Error comparing strings (LCMapString). " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); + throw std::runtime_error("Error comparing strings (LCMapString). " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); return output; } @@ -240,7 +240,8 @@ Zstring makeUpperCopy(const Zstring& str) #elif defined ZEN_MAC int cmpFileName(const Zstring& lhs, const Zstring& rhs) { - return ::strcasecmp(lhs.c_str(), rhs.c_str()); //locale-dependent! + const int rv = ::strcasecmp(lhs.c_str(), rhs.c_str()); //locale-dependent! + return rv; } diff --git a/zen/zstring.h b/zen/zstring.h index 0610a27f..7dcfbb69 100644 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -68,12 +68,12 @@ typedef zen::Zbase<Zchar, zen::StorageRefCountThreadSafe, AllocatorFreeStoreChec //Compare filepaths: Windows does NOT distinguish between upper/lower-case, while Linux DOES int cmpFileName(const Zstring& lhs, const Zstring& rhs); -struct LessFilename //case-insensitive on Windows, case-sensitive on Linux +struct LessFilePath //case-insensitive on Windows, case-sensitive on Linux { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return cmpFileName(lhs, rhs) < 0; } }; -struct EqualFilename //case-insensitive on Windows, case-sensitive on Linux +struct EqualFilePath //case-insensitive on Windows, case-sensitive on Linux { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return cmpFileName(lhs, rhs) == 0; } }; @@ -85,10 +85,35 @@ struct EqualFilename //case-insensitive on Windows, case-sensitive on Linux inline Zstring appendSeparator(Zstring path) //support rvalue references! { - return zen::endsWith(path, FILE_NAME_SEPARATOR) ? path : (path += FILE_NAME_SEPARATOR); + return zen::endsWith(path, FILE_NAME_SEPARATOR) ? path : (path += FILE_NAME_SEPARATOR); //returning a by-value parameter implicitly converts to r-value! } +inline +Zstring getFileExtension(const Zstring& filePath) +{ + const Zstring shortName = afterLast(filePath, FILE_NAME_SEPARATOR); //returns the whole string if term not found + + return contains(shortName, Zchar('.')) ? + afterLast(filePath, Zchar('.')) : + Zstring(); +} + + +inline +bool pathStartsWith(const Zstring& str, const Zstring& prefix) +{ + return str.size() >= prefix.size() && + EqualFilePath()(Zstring(str.begin(), str.begin() + prefix.size()), prefix); +} + + +inline +bool pathEndsWith(const Zstring& str, const Zstring& postfix) +{ + return str.size() >= postfix.size() && + EqualFilePath()(Zstring(str.end() - postfix.size(), str.end()), postfix); +} |