From 37dab163d3ee934a56f7d4ef2423c973f20cd27a Mon Sep 17 00:00:00 2001
From: Daniel Wilhelm
Date: Mon, 25 Jul 2016 20:46:59 +0200
Subject: 8.3
---
FreeFileSync/Build/Changelog.txt | 18 +-
FreeFileSync/Build/Help/html/base.css | 1 +
.../Build/Help/html/external-applications.html | 36 +-
.../Build/Help/html/synchronization-settings.html | 4 +-
FreeFileSync/Build/Languages/german.lng | 39 +-
FreeFileSync/Build/Resources.zip | Bin 291954 -> 294636 bytes
FreeFileSync/Source/RealtimeSync/application.cpp | 3 +-
FreeFileSync/Source/RealtimeSync/main_dlg.cpp | 2 +
FreeFileSync/Source/algorithm.cpp | 253 +-
FreeFileSync/Source/algorithm.h | 35 +-
FreeFileSync/Source/application.cpp | 3 +-
FreeFileSync/Source/comparison.cpp | 12 +-
FreeFileSync/Source/file_hierarchy.cpp | 8 +-
FreeFileSync/Source/file_hierarchy.h | 35 +-
FreeFileSync/Source/fs/native.cpp | 3 +-
FreeFileSync/Source/lib/db_file.cpp | 12 +-
FreeFileSync/Source/lib/process_xml.cpp | 114 +-
FreeFileSync/Source/lib/process_xml.h | 22 +-
FreeFileSync/Source/synchronization.cpp | 30 +-
FreeFileSync/Source/ui/batch_config.cpp | 3 +-
FreeFileSync/Source/ui/custom_grid.cpp | 262 +-
FreeFileSync/Source/ui/grid_view.cpp | 55 +-
FreeFileSync/Source/ui/gui_generated.cpp | 7962 ++++++++++----------
FreeFileSync/Source/ui/gui_generated.h | 1704 ++---
FreeFileSync/Source/ui/gui_status_handler.cpp | 2 +-
FreeFileSync/Source/ui/main_dlg.cpp | 378 +-
FreeFileSync/Source/ui/main_dlg.h | 9 +-
FreeFileSync/Source/ui/progress_indicator.cpp | 3 +-
FreeFileSync/Source/ui/search.cpp | 65 +-
FreeFileSync/Source/ui/search.h | 2 +-
FreeFileSync/Source/ui/small_dlgs.cpp | 58 +-
FreeFileSync/Source/ui/small_dlgs.h | 8 +-
FreeFileSync/Source/ui/sync_cfg.cpp | 1 +
FreeFileSync/Source/ui/tree_view.cpp | 26 +-
FreeFileSync/Source/ui/version_check.cpp | 96 +-
FreeFileSync/Source/ui/version_check.h | 8 +-
FreeFileSync/Source/ui/version_check_impl.h | 55 +
FreeFileSync/Source/ui/version_id.h | 38 -
FreeFileSync/Source/version/version.h | 2 +-
wx+/popup_dlg.cpp | 17 +-
wx+/popup_dlg.h | 9 +-
wx+/popup_dlg_generated.cpp | 149 +-
wx+/popup_dlg_generated.h | 56 +-
zen/crc.h | 48 +
zen/file_access.cpp | 52 +-
zen/file_access.h | 6 +-
zen/i18n.h | 7 +-
zen/serialize.h | 2 +-
zen/shell_execute.h | 9 +-
zen/symlink_target.h | 2 +-
50 files changed, 6045 insertions(+), 5679 deletions(-)
create mode 100644 FreeFileSync/Source/ui/version_check_impl.h
delete mode 100644 FreeFileSync/Source/ui/version_id.h
create mode 100644 zen/crc.h
diff --git a/FreeFileSync/Build/Changelog.txt b/FreeFileSync/Build/Changelog.txt
index 9335fbec..8b4688ef 100644
--- a/FreeFileSync/Build/Changelog.txt
+++ b/FreeFileSync/Build/Changelog.txt
@@ -1,3 +1,19 @@
+FreeFileSync 8.3 [2016-07-08]
+-----------------------------
+Make temporary local copy for non-native file paths: %local_path%
+Support selections from both grid sides at a time for external applications
+New external application macros: %item_path%, %folder_path%, %item_path2%, %folder_path2%
+Migrate external application commands to new macro syntax
+Support reverse grid search (Shift + F3)
+Don't condense empty sub folders on overview panel
+Show changelog delta in update notification
+Center modal dialogs after layout redetermination
+Warn about portable installation into programs folder
+Calculate default message dialog height depending on screen size
+Don't substitute external applications path for empty base folder
+Fixed prolonged tooltip time not being evaluated
+
+
FreeFileSync 8.2 [2016-05-30]
-----------------------------
Unified item path representation on main grid
@@ -7,7 +23,7 @@ Fixed crash when FreeFileSync is still running during OS shutdown
Fixed crash on startup due to missing root certificates
Work around start up crash on Windows installations missing certain patches
Fixed in-place progress panel height being trimmed
-Support drawing arbitrary polyons with graph control
+Support drawing arbitrary polygons with graph control
Apply Posix file name normalization (OS X)
Normalize keyboard input encoding for all text fields (OS X)
Report errors when cleaning up old log files
diff --git a/FreeFileSync/Build/Help/html/base.css b/FreeFileSync/Build/Help/html/base.css
index ec1b039b..f7fa42be 100644
--- a/FreeFileSync/Build/Help/html/base.css
+++ b/FreeFileSync/Build/Help/html/base.css
@@ -26,6 +26,7 @@ ul
table td
{
padding: 0 20px 0 0;
+ vertical-align: top;
}
table th
diff --git a/FreeFileSync/Build/Help/html/external-applications.html b/FreeFileSync/Build/Help/html/external-applications.html
index ea2ed918..566cd838 100644
--- a/FreeFileSync/Build/Help/html/external-applications.html
+++ b/FreeFileSync/Build/Help/html/external-applications.html
@@ -11,19 +11,19 @@
When you double-click on one of the rows on the main dialog, FreeFileSync opens the operating system's file browser
- by default. On Windows it calls explorer /select, "%item_path%", on
- Linux xdg-open "%item_folder%" and on OS X open -R "%item_path%".
+ by default. On Windows it calls explorer /select, "%local_path%", on
+ Linux xdg-open "%folder_path%" and on OS X open -R "%local_path%".
To customize this behavior and integrate other external applications into FreeFileSync,
navigate to Menu → Tools → Options: Customize context menu and add or replace a command.
- The first entry will be executed when double-clicking a row on main grid or when pressing ENTER. All other entries can be accessed
- quickly via the associated numeric keys or via the context menu shown after a right mouse click.
+ The first entry will be executed when double-clicking a row on main grid or when pressing ENTER. All other entries can be accessed
+ quickly by pressing the associated numeric keys or via the context menu that is shown after a right mouse click.
- In addition to regular Macros, the following specific macros are available:
+ In addition to regular Macros, the following special macros are available:
@@ -37,18 +37,20 @@
Full file or folder path
-
%item_folder%
-
Folder path only
+
%folder_path%
+
Parent folder path
-
%item2_path%
-
Counterpart of %item_path% on the opposite grid
-
-
-
%item2_folder%
-
Counterpart of %item_folder% on the opposite grid
+
%local_path%
+
Creates a temporary local copy for files located on SFTP and MTP storage. Identical to %item_path% for files on local disks and network shares.
+
+
+ Note: To refer to the item on the opposite side, append "2" to the macro name: e.g.
+ %item_path2%, %folder_path2%, %local_path2%.
+
The Two-Way variant already creates database files, therefore detection of moved files is always active.
- The Mirror variant however does not need a database file to find synchronization directions, so detection of moved files
+ The Mirror variant however does not need the database files to find synchronization directions, so detection of moved files
is not available by default. If you don't mind the creation of the database files you can enable this feature by
selecting the Detect moved files checkbox.
@@ -29,7 +29,7 @@
Note
-
Detection of moved files is not available when synchronizing a folder pair for the first time. Only beginning with the second sync
+
Detection of moved files is not yet possible when synchronizing a folder pair for the first time. Only beginning with the second sync
the database files are available to determine moved files.
Detection is not supported by all file systems. Most notably, certain file moves on the FAT file system cannot be detected.
Also virtualized file systems, e.g. a mounted WebDAV drive, might not support move detection. In these cases FreeFileSync will automatically fall back to copy and delete.
diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng
index b8857988..399999b4 100644
--- a/FreeFileSync/Build/Languages/german.lng
+++ b/FreeFileSync/Build/Languages/german.lng
@@ -7,6 +7,21 @@
n == 1 ? 0 : 1
+
+Parameter für gegenüberliegende Seite
+
+
+Temporäre lokale Kopie für SFTP und MTP Speicher
+
+
+Übergeordneter Ordnerpfad
+
+
+Kompletter Datei oder Ordnerpfad
+
+
+Lokaler Pfad ist nicht verfügbar für %x.
+
Beide Seiten wurden seit der letzten Synchronisation verändert.
@@ -722,12 +737,12 @@ Die Befehlszeile wird ausgelöst, wenn:
Schwerer Fehler
-
-Symlink
-
Ordner
+
+Symlink
+
Absoluter Pfad
@@ -1525,18 +1540,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
Integriert externe Anwendungen in das Kontextmenü. Die folgenden Makros stehen zur Verfügung:
-
-- kompletter Datei oder Ordnername
-
-
-- nur Ordneranteil
-
-
-- Entsprechung der anderen Seite zu %item_path%
-
-
-- Entsprechung der anderen Seite zu %item_folder%
-
Sollen versteckte Fenster und Warnmeldungen wieder gezeigt werden?
@@ -1828,3 +1831,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
Jetzt die normale Version von der FreeFileSync Homepage herunterladen?
+
+Die portable Version kann nicht in den gewählten Ordner installiert werden.
+
+
+Bitte wählen Sie den lokalen Installationstyp oder einen anderen Ordner für die Installation.
+
diff --git a/FreeFileSync/Build/Resources.zip b/FreeFileSync/Build/Resources.zip
index e45debd5..2a11e1f0 100644
Binary files a/FreeFileSync/Build/Resources.zip and b/FreeFileSync/Build/Resources.zip differ
diff --git a/FreeFileSync/Source/RealtimeSync/application.cpp b/FreeFileSync/Source/RealtimeSync/application.cpp
index 7b7e45d3..2fff5b97 100644
--- a/FreeFileSync/Source/RealtimeSync/application.cpp
+++ b/FreeFileSync/Source/RealtimeSync/application.cpp
@@ -67,7 +67,8 @@ bool Application::OnInit()
#endif
//Windows User Experience Interaction Guidelines: tool tips should have 5s timeout, info tips no timeout => compromise:
- wxToolTip::SetAutoPop(7000); //https://msdn.microsoft.com/en-us/library/windows/desktop/aa511495
+ wxToolTip::Enable(true); //yawn, a wxWidgets screw-up: wxToolTip::SetAutoPop is no-op if global tooltip window is not yet constructed: wxToolTip::Enable creates it
+ wxToolTip::SetAutoPop(10000); //https://msdn.microsoft.com/en-us/library/windows/desktop/aa511495
SetAppName(L"RealTimeSync");
diff --git a/FreeFileSync/Source/RealtimeSync/main_dlg.cpp b/FreeFileSync/Source/RealtimeSync/main_dlg.cpp
index 0d9f7610..bd49d38f 100644
--- a/FreeFileSync/Source/RealtimeSync/main_dlg.cpp
+++ b/FreeFileSync/Source/RealtimeSync/main_dlg.cpp
@@ -122,6 +122,8 @@ MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName)
setLastUsedConfig(currentConfigFile);
//-----------------------------------------------------------------------------------------
+ Center(); //needs to be re-applied after a dialog size change! (see addFolder() within setConfiguration())
+
if (startWatchingImmediately) //start watch mode directly
{
wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED);
diff --git a/FreeFileSync/Source/algorithm.cpp b/FreeFileSync/Source/algorithm.cpp
index 45f30926..52765e3a 100644
--- a/FreeFileSync/Source/algorithm.cpp
+++ b/FreeFileSync/Source/algorithm.cpp
@@ -7,12 +7,18 @@
#include "algorithm.h"
#include
#include
+//#include
#include
+#include
+#include
+#include //needed for TempFileBuffer only
+#include
#include "lib/norm_filter.h"
#include "lib/db_file.h"
#include "lib/cmp_filetime.h"
#include "lib/status_handler_impl.h"
#include "fs/concrete.h"
+#include "fs/native.h"
using namespace zen;
//using namespace std::rel_ops;
@@ -539,7 +545,7 @@ private:
}
//evaluation
- const bool changeOnLeft = !matchesDbEntry(file, dbEntry, ignoreTimeShiftMinutes);
+ const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(file, dbEntry, ignoreTimeShiftMinutes);
const bool changeOnRight = !matchesDbEntry(file, dbEntry, ignoreTimeShiftMinutes);
if (changeOnLeft != changeOnRight)
@@ -575,7 +581,7 @@ private:
}
//evaluation
- const bool changeOnLeft = !matchesDbEntry(symlink, dbEntry, ignoreTimeShiftMinutes);
+ const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(symlink, dbEntry, ignoreTimeShiftMinutes);
const bool changeOnRight = !matchesDbEntry(symlink, dbEntry, ignoreTimeShiftMinutes);
if (changeOnLeft != changeOnRight)
@@ -618,7 +624,7 @@ private:
if (cat != DIR_EQUAL)
{
//evaluation
- const bool changeOnLeft = !matchesDbEntry(folder, dbEntry);
+ const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(folder, dbEntry);
const bool changeOnRight = !matchesDbEntry(folder, dbEntry);
if (changeOnLeft != changeOnRight)
@@ -766,25 +772,20 @@ struct SetNewDirection
void zen::setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsObj)
{
//process subdirectories also!
- struct Recurse: public FSObjectVisitor
+ visitFSObject(fsObj, [&](const FolderPair& folder)
{
- Recurse(SyncDirection newDir) : newDir_(newDir) {}
- void visit(const FilePair& file) override
- {
- SetNewDirection::execute(const_cast(file), newDir_); //phyiscal object is not const in this method anyway
- }
- void visit(const SymlinkPair& symlink) override
- {
- SetNewDirection::execute(const_cast(symlink), newDir_); //
- }
- void visit(const FolderPair& folder) override
- {
- SetNewDirection::execute(const_cast(folder), newDir_); //
- }
- private:
- const SyncDirection newDir_;
- } setDirVisitor(newDirection);
- fsObj.accept(setDirVisitor);
+ SetNewDirection::execute(const_cast(folder), newDirection); //
+ },
+
+ [&](const FilePair& file)
+ {
+ SetNewDirection::execute(const_cast(file), newDirection); //phyiscal object is not const in this method anyway
+ },
+
+ [&](const SymlinkPair& symlink)
+ {
+ SetNewDirection::execute(const_cast(symlink), newDirection); //
+ });
}
//--------------- functions related to filtering ------------------------------------------------------------------------------------
@@ -792,7 +793,7 @@ void zen::setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsOb
namespace
{
template
-void inOrExcludeAllRows(zen::HierarchyObject& hierObj)
+void inOrExcludeAllRows(HierarchyObject& hierObj)
{
for (FilePair& file : hierObj.refSubFiles())
file.setActive(include);
@@ -807,7 +808,7 @@ void inOrExcludeAllRows(zen::HierarchyObject& hierObj)
}
-void zen::setActiveStatus(bool newStatus, zen::FolderComparison& folderCmp)
+void zen::setActiveStatus(bool newStatus, FolderComparison& folderCmp)
{
if (newStatus)
std::for_each(begin(folderCmp), end(folderCmp), [](BaseFolderPair& baseFolder) { inOrExcludeAllRows(baseFolder); }); //include all rows
@@ -816,27 +817,19 @@ void zen::setActiveStatus(bool newStatus, zen::FolderComparison& folderCmp)
}
-void zen::setActiveStatus(bool newStatus, zen::FileSystemObject& fsObj)
+void zen::setActiveStatus(bool newStatus, FileSystemObject& fsObj)
{
fsObj.setActive(newStatus);
//process subdirectories also!
- struct Recurse: public FSObjectVisitor
+ visitFSObject(fsObj, [&](const FolderPair& folder)
{
- Recurse(bool newStat) : newStatus_(newStat) {}
- void visit(const FilePair& file) override {}
- void visit(const SymlinkPair& link) override {}
- void visit(const FolderPair& folder) override
- {
- if (newStatus_)
- inOrExcludeAllRows(const_cast(folder)); //object is not physically const here anyway
- else
- inOrExcludeAllRows(const_cast(folder)); //
- }
- private:
- const bool newStatus_;
- } recurse(newStatus);
- fsObj.accept(recurse);
+ if (newStatus)
+ inOrExcludeAllRows(const_cast(folder)); //object is not physically const here anyway
+ else
+ inOrExcludeAllRows(const_cast(folder)); //
+ },
+ [](const FilePair& file) {}, [](const SymlinkPair& symlink) {});
}
namespace
@@ -1121,8 +1114,8 @@ void zen::applyTimeSpanFilter(FolderComparison& folderCmp, std::int64_t timeFrom
//############################################################################################################
-std::pair zen::getSelectedItemsAsString(const std::vector& selectionLeft,
- const std::vector& selectionRight)
+std::pair zen::getSelectedItemsAsString(const std::vector& selectionLeft,
+ const std::vector& selectionRight)
{
//don't use wxString! its rather dumb linear allocation strategy brings perf down to a crawl!
std::wstring fileList; //
@@ -1148,34 +1141,8 @@ std::pair zen::getSelectedItemsAsString(const std::vector& onFolder,
- const std::function& onFile,
- const std::function& onSymlink)
- {
- FSObjectLambdaVisitor visitor(onFolder, onFile, onSymlink);
- fsObj.accept(visitor);
- }
-
-private:
- FSObjectLambdaVisitor(const std::function& onFolder,
- const std::function& onFile,
- const std::function& onSymlink) : onFolder_(onFolder), onFile_(onFile), onSymlink_(onSymlink) {}
-
- void visit(const FolderPair& folder) override { if (onFolder_ ) onFolder_ (folder); }
- void visit(const FilePair& file ) override { if (onFile_ ) onFile_ (file); }
- void visit(const SymlinkPair& link ) override { if (onSymlink_) onSymlink_(link); }
-
- const std::function onFolder_;
- const std::function onFile_;
- const std::function onSymlink_;
-};
-
-
template
-void copyToAlternateFolderFrom(const std::vector& rowsToCopy,
+void copyToAlternateFolderFrom(const std::vector& rowsToCopy,
const AbstractPath& targetFolderPath,
bool keepRelPaths,
bool overwriteIfExists,
@@ -1190,7 +1157,7 @@ void copyToAlternateFolderFrom(const std::vector& rowsToCopy,
const std::wstring txtCreatingFile (_("Creating file %x" ));
const std::wstring txtCreatingLink (_("Creating symbolic link %x"));
- auto copyItem = [&](FileSystemObject& fsObj, const AbstractPath& targetPath) //throw FileError
+ auto copyItem = [&](const FileSystemObject& fsObj, const AbstractPath& targetPath) //throw FileError
{
const std::function deleteTargetItem = [&]
{
@@ -1210,7 +1177,7 @@ void copyToAlternateFolderFrom(const std::vector& rowsToCopy,
}
};
- FSObjectLambdaVisitor::visit(fsObj, [&](const FolderPair& folder)
+ visitFSObject(fsObj, [&](const FolderPair& folder)
{
StatisticsReporter statReporter(1, 0, callback);
notifyItemCopy(txtCreatingFolder, AFS::getDisplayPath(targetPath));
@@ -1252,7 +1219,7 @@ void copyToAlternateFolderFrom(const std::vector& rowsToCopy,
});
};
- for (FileSystemObject* fsObj : rowsToCopy)
+ for (const FileSystemObject* fsObj : rowsToCopy)
tryReportingError([&]
{
const Zstring& relPath = keepRelPaths ? fsObj->getRelativePath() : fsObj->getItemName();
@@ -1280,34 +1247,36 @@ void copyToAlternateFolderFrom(const std::vector& rowsToCopy,
}
-void zen::copyToAlternateFolder(const std::vector& rowsToCopyOnLeft,
- const std::vector& rowsToCopyOnRight,
+void zen::copyToAlternateFolder(const std::vector& rowsToCopyOnLeft,
+ const std::vector& rowsToCopyOnRight,
const Zstring& targetFolderPathPhrase,
bool keepRelPaths,
bool overwriteIfExists,
ProcessCallback& callback)
{
- std::vector itemSelectionLeft = rowsToCopyOnLeft;
- std::vector itemSelectionRight = rowsToCopyOnRight;
- erase_if(itemSelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty(); });
+ std::vector itemSelectionLeft = rowsToCopyOnLeft;
+ std::vector itemSelectionRight = rowsToCopyOnRight;
+ erase_if(itemSelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); });
erase_if(itemSelectionRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty(); });
const int itemCount = static_cast(itemSelectionLeft.size() + itemSelectionRight.size());
std::int64_t dataToProcess = 0;
- for (FileSystemObject* fsObj : itemSelectionLeft)
- FSObjectLambdaVisitor::visit(*fsObj, nullptr /*onDir*/,
- [&](const FilePair& file) {dataToProcess += static_cast(file.getFileSize()); }, nullptr /*onSymlink*/);
+ for (const FileSystemObject* fsObj : itemSelectionLeft)
+ visitFSObject(*fsObj, [](const FolderPair& folder) {},
+ [&](const FilePair& file) { dataToProcess += static_cast(file.getFileSize()); }, [](const SymlinkPair& symlink) {});
- for (FileSystemObject* fsObj : itemSelectionRight)
- FSObjectLambdaVisitor::visit(*fsObj, nullptr /*onDir*/,
- [&](const FilePair& file) {dataToProcess += static_cast(file.getFileSize()); }, nullptr /*onSymlink*/);
+ for (const FileSystemObject* fsObj : itemSelectionRight)
+ visitFSObject(*fsObj, [](const FolderPair& folder) {},
+ [&](const FilePair& file) { dataToProcess += static_cast(file.getFileSize()); }, [](const SymlinkPair& symlink) {});
callback.initNewPhase(itemCount, dataToProcess, ProcessCallback::PHASE_SYNCHRONIZING); //throw X
+ //------------------------------------------------------------------------------
+
const AbstractPath targetFolderPath = createAbstractPath(targetFolderPathPhrase);
- copyToAlternateFolderFrom(itemSelectionLeft, targetFolderPath, keepRelPaths, overwriteIfExists, callback);
+ copyToAlternateFolderFrom< LEFT_SIDE>(itemSelectionLeft, targetFolderPath, keepRelPaths, overwriteIfExists, callback);
copyToAlternateFolderFrom(itemSelectionRight, targetFolderPath, keepRelPaths, overwriteIfExists, callback);
}
@@ -1350,7 +1319,7 @@ void deleteFromGridAndHDOneSide(std::vector& rowsToDelete,
if (!fsObj->isEmpty()) //element may be implicitly deleted, e.g. if parent folder was deleted first
{
- FSObjectLambdaVisitor::visit(*fsObj,
+ visitFSObject(*fsObj,
[&](const FolderPair& folder)
{
if (useRecycleBin)
@@ -1470,12 +1439,14 @@ void zen::deleteFromGridAndHD(const std::vector& rowsToDelete
std::vector deleteLeft = rowsToDeleteOnLeft;
std::vector deleteRight = rowsToDeleteOnRight;
- erase_if(deleteLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty(); }); //needed?
+ erase_if(deleteLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); //needed?
erase_if(deleteRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty(); }); //yes, for correct stats:
const int itemCount = static_cast(deleteLeft.size() + deleteRight.size());
callback.initNewPhase(itemCount, 0, ProcessCallback::PHASE_SYNCHRONIZING); //throw X
+ //------------------------------------------------------------------------------
+
//ensure cleanup: redetermination of sync-directions and removal of invalid rows
auto updateDirection = [&]
{
@@ -1522,7 +1493,7 @@ void zen::deleteFromGridAndHD(const std::vector& rowsToDelete
std::vector deleteRecylerRight;
std::map recyclerSupported;
- categorize(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, recyclerSupported, callback);
+ categorize< LEFT_SIDE>(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, recyclerSupported, callback);
categorize(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, recyclerSupported, callback);
//windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong
@@ -1544,3 +1515,113 @@ void zen::deleteFromGridAndHD(const std::vector& rowsToDelete
deleteFromGridAndHDOneSide(deleteRecylerRight, true, callback);
deleteFromGridAndHDOneSide(deletePermanentRight, false, callback);
}
+
+//############################################################################################################
+
+bool zen::operator<(const TempFileBuffer::FileDetails& lhs, const TempFileBuffer::FileDetails& rhs)
+{
+ if (lhs.descr.lastWriteTimeRaw != rhs.descr.lastWriteTimeRaw)
+ return lhs.descr.lastWriteTimeRaw < rhs.descr.lastWriteTimeRaw;
+
+ if (lhs.descr.fileSize != rhs.descr.fileSize)
+ return lhs.descr.fileSize < rhs.descr.fileSize;
+
+ if (lhs.descr.fileId != rhs.descr.fileId)
+ return lhs.descr.fileId < rhs.descr.fileId;
+
+ if (lhs.descr.isFollowedSymlink != rhs.descr.isFollowedSymlink)
+ return lhs.descr.isFollowedSymlink < rhs.descr.isFollowedSymlink;
+
+ return AFS::LessAbstractPath()(lhs.path, rhs.path);
+}
+
+
+TempFileBuffer::~TempFileBuffer()
+{
+ if (!tempFolderPath.empty())
+ try
+ {
+ removeDirectoryRecursively(tempFolderPath); //throw FileError
+ }
+ catch (FileError&) { assert(false); }
+}
+
+
+//returns empty if not available (item not existing, error during copy)
+Zstring TempFileBuffer::getTempPath(const FileDetails& details) const
+{
+ auto it = tempFilePaths.find(details);
+ if (it != tempFilePaths.end())
+ return it->second;
+ return Zstring();
+}
+
+
+void TempFileBuffer::createTempFiles(const std::set& workLoad, ProcessCallback& callback)
+{
+ const int itemCount = workLoad.size();
+ std::int64_t dataToProcess = 0;
+
+ for (const FileDetails& details : workLoad)
+ dataToProcess += details.descr.fileSize;
+
+ callback.initNewPhase(itemCount, dataToProcess, ProcessCallback::PHASE_SYNCHRONIZING); //throw X
+
+ //------------------------------------------------------------------------------
+
+ if (tempFolderPath.empty())
+ {
+ Opt errMsg = tryReportingError([&]
+ {
+ //generate random temp folder path e.g. C:\Users\Zenju\AppData\Local\Temp\FFS-068b2e88
+ Zstring tempPathTmp = appendSeparator(getTempFolderPath()); //throw FileError
+ tempPathTmp += Zstr("FFS-");
+
+ const std::string guid = generateGUID(); //no need for full-blown (pseudo-)random numbers for this one-time invocation
+ const uint32_t crc32 = getCrc32(guid.begin(), guid.end());
+ tempPathTmp += printNumber(Zstr("%08x"), static_cast(crc32));
+
+ makeDirectoryRecursively(tempPathTmp); //throw FileError
+
+ tempFolderPath = tempPathTmp;
+ }, callback); //throw X?
+ if (errMsg) return;
+ }
+
+ for (const FileDetails& details : workLoad)
+ {
+ assert(tempFilePaths.find(details) == tempFilePaths.end()); //ensure correct stats, NO overwrite-copy => caller-contract!
+
+ MemoryStreamOut cookie; //create hash to distinguish different versions and file locations
+ writeNumber (cookie, details.descr.lastWriteTimeRaw);
+ writeNumber (cookie, details.descr.fileSize);
+ writeContainer(cookie, details.descr.fileId);
+ writeNumber (cookie, details.descr.isFollowedSymlink);
+ writeContainer(cookie, AFS::getInitPathPhrase(details.path));
+
+ const uint16_t crc16 = getCrc16(cookie.ref().begin(), cookie.ref().end());
+ const Zstring detailsHash = printNumber(Zstr("%04x"), static_cast(crc16));
+
+ const Zstring fileName = AFS::getFileShortName(details.path);
+
+ auto it = std::find(fileName.begin(), fileName.end(), Zchar('.')); //gracefully handle case of missing "."
+ const Zstring tempFileName = Zstring(fileName.begin(), it) + Zchar('-') + detailsHash + Zstring(it, fileName.end());
+
+ const Zstring tempFilePath = appendSeparator(tempFolderPath) + tempFileName;
+
+ tryReportingError([&]
+ {
+ StatisticsReporter statReporter(1, details.descr.fileSize, callback);
+
+ callback.reportInfo(replaceCpy(_("Creating file %x"), L"%x", fmtPath(tempFilePath)));
+
+ auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); };
+ AFS::copyFileTransactional(details.path, createItemPathNative(tempFilePath), //throw FileError, ErrorFileLocked
+ false /*copyFilePermissions*/, true /*transactionalCopy*/, nullptr /*onDeleteTargetFile*/, onNotifyCopyStatus);
+ statReporter.reportDelta(1, 0);
+
+ tempFilePaths[details] = tempFilePath;
+ statReporter.reportFinished();
+ }, callback); //throw X?
+ }
+}
diff --git a/FreeFileSync/Source/algorithm.h b/FreeFileSync/Source/algorithm.h
index cd8d8a7c..83201fd2 100644
--- a/FreeFileSync/Source/algorithm.h
+++ b/FreeFileSync/Source/algorithm.h
@@ -44,12 +44,12 @@ void setActiveStatus(bool newStatus, FolderComparison& folderCmp); //activate or
void setActiveStatus(bool newStatus, FileSystemObject& fsObj); //activate or deactivate row: (not recursively anymore)
std::pair getSelectedItemsAsString( //returns string with item names and total count of selected(!) items, NOT total files/dirs!
- const std::vector& selectionLeft, //all pointers need to be bound!
- const std::vector& selectionRight); //
+ const std::vector& selectionLeft, //all pointers need to be bound!
+ const std::vector& selectionRight); //
//manual copy to alternate folder:
-void copyToAlternateFolder(const std::vector& rowsToCopyOnLeft, //all pointers need to be bound!
- const std::vector& rowsToCopyOnRight, //
+void copyToAlternateFolder(const std::vector& rowsToCopyOnLeft, //all pointers need to be bound!
+ const std::vector& rowsToCopyOnRight, //
const Zstring& targetFolderPathPhrase,
bool keepRelPaths,
bool overwriteIfExists,
@@ -64,6 +64,31 @@ void deleteFromGridAndHD(const std::vector& rowsToDeleteOnLef
//global warnings:
bool& warningRecyclerMissing,
ProcessCallback& callback);
-}
+//get native Win32 paths or create temporary copy for SFTP/MTP, ect.
+class TempFileBuffer
+{
+public:
+ TempFileBuffer() {}
+ ~TempFileBuffer();
+
+ struct FileDetails
+ {
+ AbstractPath path;
+ FileDescriptor descr;
+ };
+ Zstring getTempPath(const FileDetails& details) const; //returns empty if not in buffer (item not existing, error during copy)
+
+ //contract: only add files not yet in the buffer!
+ void createTempFiles(const std::set& workLoad, ProcessCallback& callback);
+
+private:
+ TempFileBuffer (const TempFileBuffer&) = delete;
+ TempFileBuffer& operator=(const TempFileBuffer&) = delete;
+
+ std::map tempFilePaths;
+ Zstring tempFolderPath;
+};
+bool operator<(const TempFileBuffer::FileDetails& lhs, const TempFileBuffer::FileDetails& rhs);
+}
#endif //ALGORITHM_H_34218518475321452548
diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp
index bd48da14..bec95254 100644
--- a/FreeFileSync/Source/application.cpp
+++ b/FreeFileSync/Source/application.cpp
@@ -152,7 +152,8 @@ bool Application::OnInit()
#endif
//Windows User Experience Interaction Guidelines: tool tips should have 5s timeout, info tips no timeout => compromise:
- wxToolTip::SetAutoPop(7000); //https://msdn.microsoft.com/en-us/library/windows/desktop/aa511495
+ wxToolTip::Enable(true); //yawn, a wxWidgets screw-up: wxToolTip::SetAutoPop is no-op if global tooltip window is not yet constructed: wxToolTip::Enable creates it
+ wxToolTip::SetAutoPop(10000); //https://msdn.microsoft.com/en-us/library/windows/desktop/aa511495
SetAppName(L"FreeFileSync"); //if not set, the default is the executable's name!
diff --git a/FreeFileSync/Source/comparison.cpp b/FreeFileSync/Source/comparison.cpp
index 0aa82f41..9d556815 100644
--- a/FreeFileSync/Source/comparison.cpp
+++ b/FreeFileSync/Source/comparison.cpp
@@ -249,7 +249,7 @@ std::wstring getConflictInvalidDate(const std::wstring& displayPath, std::int64_
std::wstring getConflictSameDateDiffSize(const FilePair& file)
{
return replaceCpy(_("Files %x have the same date but a different size."), L"%x", fmtPath(file.getPairRelativePath())) + L"\n" +
- L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(file.getLastWriteTime()) + L" " + _("Size:") + L" " + toGuiString(file.getFileSize()) + L"\n" +
+ L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(file.getLastWriteTime< LEFT_SIDE>()) + L" " + _("Size:") + L" " + toGuiString(file.getFileSize()) + L"\n" +
L" " + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(file.getLastWriteTime()) + L" " + _("Size:") + L" " + toGuiString(file.getFileSize());
}
@@ -263,7 +263,7 @@ std::wstring getConflictSkippedBinaryComparison(const FilePair& file)
std::wstring getDescrDiffMetaShortnameCase(const FileSystemObject& fsObj)
{
return _("Items differ in attributes only") + L"\n" +
- L" " + arrowLeft + L" " + fmtPath(fsObj.getItemName()) + L"\n" +
+ L" " + arrowLeft + L" " + fmtPath(fsObj.getItemName< LEFT_SIDE>()) + L"\n" +
L" " + arrowRight + L" " + fmtPath(fsObj.getItemName());
}
@@ -272,7 +272,7 @@ template
std::wstring getDescrDiffMetaDate(const FileOrLinkPair& file)
{
return _("Items differ in attributes only") + L"\n" +
- L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(file.template getLastWriteTime()) + L"\n" +
+ L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(file.template getLastWriteTime< LEFT_SIDE>()) + L"\n" +
L" " + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(file.template getLastWriteTime());
}
@@ -390,7 +390,7 @@ void categorizeSymlinkByContent(SymlinkPair& symlink, ProcessCallback& callback)
if (targetPathRawL == targetPathRawR
#ifdef ZEN_WIN //type of symbolic link is relevant for Windows only
&&
- AFS::folderExists(symlink.getAbstractPath()) == //check if dir-symlink
+ AFS::folderExists(symlink.getAbstractPath< LEFT_SIDE>()) == //check if dir-symlink
AFS::folderExists(symlink.getAbstractPath()) //
#endif
)
@@ -673,7 +673,7 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer
using FileData = const FolderContainer::FileList::value_type;
linearMerge(lhs.files, rhs.files,
- [&](const FileData& fileLeft ) { FilePair& newItem = output.addSubFile(fileLeft .first, fileLeft .second); checkFailedRead(newItem, errorMsg); }, //left only
+ [&](const FileData& fileLeft ) { FilePair& newItem = output.addSubFile< LEFT_SIDE>(fileLeft .first, fileLeft .second); checkFailedRead(newItem, errorMsg); }, //left only
[&](const FileData& fileRight) { FilePair& newItem = output.addSubFile(fileRight.first, fileRight.second); checkFailedRead(newItem, errorMsg); }, //right only
[&](const FileData& fileLeft, const FileData& fileRight) //both sides
@@ -692,7 +692,7 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer
using SymlinkData = const FolderContainer::SymlinkList::value_type;
linearMerge(lhs.symlinks, rhs.symlinks,
- [&](const SymlinkData& symlinkLeft ) { SymlinkPair& newItem = output.addSubLink(symlinkLeft .first, symlinkLeft .second); checkFailedRead(newItem, errorMsg); }, //left only
+ [&](const SymlinkData& symlinkLeft ) { SymlinkPair& newItem = output.addSubLink< LEFT_SIDE>(symlinkLeft .first, symlinkLeft .second); checkFailedRead(newItem, errorMsg); }, //left only
[&](const SymlinkData& symlinkRight) { SymlinkPair& newItem = output.addSubLink(symlinkRight.first, symlinkRight.second); checkFailedRead(newItem, errorMsg); }, //right only
[&](const SymlinkData& symlinkLeft, const SymlinkData& symlinkRight) //both sides
diff --git a/FreeFileSync/Source/file_hierarchy.cpp b/FreeFileSync/Source/file_hierarchy.cpp
index 3c044831..12ce9944 100644
--- a/FreeFileSync/Source/file_hierarchy.cpp
+++ b/FreeFileSync/Source/file_hierarchy.cpp
@@ -40,7 +40,7 @@ namespace
SyncOperation getIsolatedSyncOperation(bool itemExistsLeft,
bool itemExistsRight,
CompareFilesResult cmpResult,
- bool selectedForSynchronization,
+ bool selectedForSync,
SyncDirection syncDir,
bool hasDirectionConflict) //perf: std::wstring was wasteful here
{
@@ -52,7 +52,7 @@ SyncOperation getIsolatedSyncOperation(bool itemExistsLeft,
assert(!hasDirectionConflict || syncDir == SyncDirection::NONE);
- if (!selectedForSynchronization)
+ if (!selectedForSync)
return cmpResult == FILE_EQUAL ?
SO_EQUAL :
SO_DO_NOTHING;
@@ -149,7 +149,7 @@ SyncOperation FileSystemObject::testSyncOperation(SyncDirection testSyncDir) con
SyncOperation FileSystemObject::getSyncOperation() const
{
- return getIsolatedSyncOperation(!isEmpty(), !isEmpty(), getCategory(), selectedForSynchronization, getSyncDir(), syncDirectionConflict.get() != nullptr);
+ return getIsolatedSyncOperation(!isEmpty(), !isEmpty(), getCategory(), selectedForSync, getSyncDir(), syncDirectionConflict.get() != nullptr);
//do *not* make a virtual call to testSyncOperation()! See FilePair::testSyncOperation()! <- better not implement one in terms of the other!!!
}
@@ -377,7 +377,7 @@ std::wstring zen::getSyncOpDescription(const FileSystemObject& fsObj)
//harmonize with synchronization.cpp::SynchronizeFolderPair::synchronizeFileInt, ect!!
{
Zstring shortNameOld = fsObj.getItemName();
- Zstring shortNameNew = fsObj.getItemName();
+ Zstring shortNameNew = fsObj.getItemName< LEFT_SIDE>();
if (op == SO_COPY_METADATA_TO_LEFT)
std::swap(shortNameOld, shortNameNew);
diff --git a/FreeFileSync/Source/file_hierarchy.h b/FreeFileSync/Source/file_hierarchy.h
index f83eb720..d5dad824 100644
--- a/FreeFileSync/Source/file_hierarchy.h
+++ b/FreeFileSync/Source/file_hierarchy.h
@@ -375,7 +375,7 @@ public:
void setSyncDir(SyncDirection newDir);
void setSyncDirConflict(const std::wstring& description); //set syncDir = SyncDirection::NONE + fill conflict description
- bool isActive() const;
+ bool isActive() const { return selectedForSync; }
void setActive(bool active);
//sync operation
@@ -430,7 +430,7 @@ private:
std::unique_ptr cmpResultDescr; //only filled if getCategory() == FILE_CONFLICT or FILE_DIFFERENT_METADATA
CompareFilesResult cmpResult; //although this uses 4 bytes there is currently *no* space wasted in class layout!
- bool selectedForSynchronization = true;
+ bool selectedForSync = true;
//Note: we model *four* states with following two variables => "syncDirectionConflict is empty or syncDir == NONE" is a class invariant!!!
SyncDirection syncDir_ = SyncDirection::NONE; //1 byte: optimize memory layout!
@@ -579,7 +579,28 @@ std::wstring getSyncOpDescription (const FileSystemObject& fsObj);
//------------------------------------------------------------------
+template
+struct FSObjectLambdaVisitor : public FSObjectVisitor
+{
+ FSObjectLambdaVisitor(Function1 onFolder,
+ Function2 onFile,
+ Function3 onSymlink) : onFolder_(onFolder), onFile_(onFile), onSymlink_(onSymlink) {}
+private:
+ void visit(const FolderPair& folder) override { onFolder_ (folder); }
+ void visit(const FilePair& file ) override { onFile_ (file); }
+ void visit(const SymlinkPair& link ) override { onSymlink_(link); }
+
+ Function1 onFolder_;
+ Function2 onFile_;
+ Function3 onSymlink_;
+};
+template inline
+void visitFSObject(const FileSystemObject& fsObj, Function1 onFolder, Function2 onFile, Function3 onSymlink)
+{
+ FSObjectLambdaVisitor visitor(onFolder, onFile, onSymlink);
+ fsObj.accept(visitor);
+}
@@ -662,17 +683,10 @@ std::wstring FileSystemObject::getSyncOpConflict() const
}
-inline
-bool FileSystemObject::isActive() const
-{
- return selectedForSynchronization;
-}
-
-
inline
void FileSystemObject::setActive(bool active)
{
- selectedForSynchronization = active;
+ selectedForSync = active;
notifySyncCfgChanged();
}
@@ -717,6 +731,7 @@ Zstring FileSystemObject::getPairRelativePath() const
inline
Zstring FileSystemObject::getPairItemName() const
{
+ assert(!isEmpty() || !isEmpty());
return isEmpty() ? getItemName() : getItemName();
}
diff --git a/FreeFileSync/Source/fs/native.cpp b/FreeFileSync/Source/fs/native.cpp
index 572881f5..bf4571c7 100644
--- a/FreeFileSync/Source/fs/native.cpp
+++ b/FreeFileSync/Source/fs/native.cpp
@@ -517,7 +517,8 @@ Zstring RecycleSessionNative::getOrCreateRecyclerTempDirPf() //throw FileError
-> 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!
+ const int64_t seed = dist(getTicks(), TickVal()) + std::time(nullptr); //high-precision seed
+ static ??? std::mt19937 rng(static_cast(seed)); //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
diff --git a/FreeFileSync/Source/lib/db_file.cpp b/FreeFileSync/Source/lib/db_file.cpp
index 959d1858..3acc06bb 100644
--- a/FreeFileSync/Source/lib/db_file.cpp
+++ b/FreeFileSync/Source/lib/db_file.cpp
@@ -520,8 +520,8 @@ private:
//create or update new "in-sync" state
InSyncFile& dbFile = updateItem(dbFiles, file.getPairItemName(),
- InSyncFile(InSyncDescrFile(file.getLastWriteTime(),
- file.getFileId ()),
+ InSyncFile(InSyncDescrFile(file.getLastWriteTime< LEFT_SIDE>(),
+ file.getFileId < LEFT_SIDE>()),
InSyncDescrFile(file.getLastWriteTime(),
file.getFileId ()),
activeCmpVar_,
@@ -691,10 +691,10 @@ private:
std::shared_ptr zen::loadLastSynchronousState(const BaseFolderPair& baseFolder, //throw FileError, FileErrorDatabaseNotExisting -> return value always bound!
const std::function& notifyProgress)
{
- const AbstractPath dbPathLeft = getDatabaseFilePath(baseFolder);
+ const AbstractPath dbPathLeft = getDatabaseFilePath< LEFT_SIDE>(baseFolder);
const AbstractPath dbPathRight = getDatabaseFilePath(baseFolder);
- if (!baseFolder.isExisting() ||
+ if (!baseFolder.isExisting< LEFT_SIDE>() ||
!baseFolder.isExisting())
{
//avoid race condition with directory existence check: reading sync.ffs_db may succeed although first dir check had failed => conflicts!
@@ -728,10 +728,10 @@ std::shared_ptr zen::loadLastSynchronousState(const BaseFolderPair
void zen::saveLastSynchronousState(const BaseFolderPair& baseFolder, const std::function& notifyProgress) //throw FileError
{
//transactional behaviour! write to tmp files first
- const AbstractPath dbPathLeft = getDatabaseFilePath(baseFolder);
+ const AbstractPath dbPathLeft = getDatabaseFilePath< LEFT_SIDE>(baseFolder);
const AbstractPath dbPathRight = getDatabaseFilePath(baseFolder);
- const AbstractPath dbPathLeftTmp = getDatabaseFilePath(baseFolder, true);
+ const AbstractPath dbPathLeftTmp = getDatabaseFilePath< LEFT_SIDE>(baseFolder, true);
const AbstractPath dbPathRightTmp = getDatabaseFilePath(baseFolder, true);
//delete old tmp file, if necessary -> throws if deletion fails!
diff --git a/FreeFileSync/Source/lib/process_xml.cpp b/FreeFileSync/Source/lib/process_xml.cpp
index 64176dde..acd9b226 100644
--- a/FreeFileSync/Source/lib/process_xml.cpp
+++ b/FreeFileSync/Source/lib/process_xml.cpp
@@ -15,7 +15,7 @@
#include "ffs_paths.h"
#ifdef ZEN_WIN
-#include
+ #include
#endif
using namespace zen;
@@ -26,9 +26,9 @@ using namespace std::rel_ops;
namespace
{
//-------------------------------------------------------------------------------------------------------------------------------
-const int XML_FORMAT_VER_GLOBAL = 2; //
-const int XML_FORMAT_VER_FFS_GUI = 5; //for FFS 7.7
-const int XML_FORMAT_VER_FFS_BATCH = 5; //
+const int XML_FORMAT_VER_GLOBAL = 3;
+const int XML_FORMAT_VER_FFS_GUI = 5;
+const int XML_FORMAT_VER_FFS_BATCH = 5;
//-------------------------------------------------------------------------------------------------------------------------------
}
@@ -79,16 +79,16 @@ void setXmlType(XmlDoc& doc, XmlType type) //throw()
}
-XmlGlobalSettings::XmlGlobalSettings()
+XmlGlobalSettings::XmlGlobalSettings()
{
#ifdef ZEN_WIN
- static const wchar_t* winMergeExePath = running64BitWindows() ?
- L"C:\\Program Files (x86)\\WinMerge\\WinMergeU.exe" :
- L"C:\\Program Files\\WinMerge\\WinMergeU.exe";
- static const bool winMergeInstalled = fileExists(winMergeExePath); //magic statics!
+ static const wchar_t* winMergeExePath = running64BitWindows() ?
+ L"C:\\Program Files (x86)\\WinMerge\\WinMergeU.exe" :
+ L"C:\\Program Files\\WinMerge\\WinMergeU.exe";
+ static const bool winMergeInstalled = fileExists(winMergeExePath); //magic statics!
- if (winMergeInstalled)
- gui.externelApplications.emplace_back(L"WinMerge", std::wstring(L"\"") + winMergeExePath + L"\" \"%item_path%\" \"%item2_path%\"");
+ if (winMergeInstalled)
+ gui.externelApplications.emplace_back(L"WinMerge", Zstring(L"\"") + winMergeExePath + L"\" \"%local_path%\" \"%local_path2%\"");
#endif
}
@@ -936,7 +936,7 @@ void readConfig(const XmlIn& in, FolderPairEnh& enhPair)
}
-void readConfig(const XmlIn& in, MainConfiguration& mainCfg)
+void readConfig(const XmlIn& in, MainConfiguration& mainCfg, int formatVer)
{
//read compare settings
XmlIn inMain = in["MainConfig"];
@@ -974,9 +974,9 @@ void readConfig(const XmlIn& in, MainConfiguration& mainCfg)
}
-void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config)
+void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config, int formatVer)
{
- readConfig(in, config.mainCfg); //read main config
+ readConfig(in, config.mainCfg, formatVer); //read main config
//read GUI specific config data
XmlIn inGuiCfg = in["GuiConfig"];
@@ -989,9 +989,9 @@ void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config)
}
-void readConfig(const XmlIn& in, xmlAccess::XmlBatchConfig& config)
+void readConfig(const XmlIn& in, xmlAccess::XmlBatchConfig& config, int formatVer)
{
- readConfig(in, config.mainCfg); //read main config
+ readConfig(in, config.mainCfg, formatVer); //read main config
//read GUI specific config data
XmlIn inBatchCfg = in["BatchConfig"];
@@ -1003,7 +1003,7 @@ void readConfig(const XmlIn& in, xmlAccess::XmlBatchConfig& config)
}
-void readConfig(const XmlIn& in, XmlGlobalSettings& config)
+void readConfig(const XmlIn& in, XmlGlobalSettings& config, int formatVer)
{
XmlIn inGeneral = in["General"];
@@ -1121,44 +1121,67 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config)
//external applications
warn_static("remove old parameter after migration! 2016-05-28")
if (inGui["ExternalApplications"])
- {
- inGui["ExternalApplications"](config.gui.externelApplications);
- if (config.gui.externelApplications.empty()) //who knows, let's repair some old failed data migrations
- config.gui.externelApplications = XmlGlobalSettings().gui.externelApplications;
- else
- {
+ {
+ inGui["ExternalApplications"](config.gui.externelApplications);
+ if (config.gui.externelApplications.empty()) //who knows, let's repair some old failed data migrations
+ config.gui.externelApplications = XmlGlobalSettings().gui.externelApplications;
+ else
+ {
#ifdef ZEN_WIN
- if (std::none_of(config.gui.externelApplications.begin(), config.gui.externelApplications.end(),
- [](const auto& item) { return contains(makeUpperCopy(item.second), L"WINMERGEU.EXE"); } ))
- {
- static const wchar_t* winMergeExePath = running64BitWindows() ?
- L"C:\\Program Files (x86)\\WinMerge\\WinMergeU.exe" :
- L"C:\\Program Files\\WinMerge\\WinMergeU.exe";
- static const bool winMergeInstalled = fileExists(winMergeExePath); //magic statics!
- if (winMergeInstalled)
- config.gui.externelApplications.emplace_back(L"WinMerge", std::wstring(L"\"") + winMergeExePath + L"\" \"%item_path%\" \"%item2_path%\"");
- }
+ if (std::none_of(config.gui.externelApplications.begin(), config.gui.externelApplications.end(),
+ [](const auto& item) { return contains(makeUpperCopy(item.second), L"WINMERGEU.EXE"); } ))
+ {
+ static const wchar_t* winMergeExePath = running64BitWindows() ?
+ L"C:\\Program Files (x86)\\WinMerge\\WinMergeU.exe" :
+ L"C:\\Program Files\\WinMerge\\WinMergeU.exe";
+ static const bool winMergeInstalled = fileExists(winMergeExePath); //magic statics!
+ if (winMergeInstalled)
+ config.gui.externelApplications.emplace_back(L"WinMerge", Zstring(L"\"") + winMergeExePath + L"\" \"%local_path%\" \"%local_path2%\"");
+ }
#endif
- }
- }
- else
- inGui["ExternalApps"](config.gui.externelApplications);
+ }
+ }
+ else
+ inGui["ExternalApps"](config.gui.externelApplications);
+
+ warn_static("remove macro migration after some time! 2016-06-30")
+ if (formatVer < 3)
+ for (auto& item : config.gui.externelApplications)
+ {
+ replace(item.second, Zstr("%item2_path%"), Zstr("%item_path2%"));
+ replace(item.second, Zstr("%item_folder%"), Zstr("%folder_path%"));
+ replace(item.second, Zstr("%item2_folder%"), Zstr("%folder_path2%"));
+
+ replace(item.second, Zstr("explorer /select, \"%item_path%\""), Zstr("explorer /select, \"%local_path%\""));
+ replace(item.second, Zstr("\"%item_path%\""), Zstr("\"%local_path%\""));
+ replace(item.second, Zstr("xdg-open \"%item_path%\""), Zstr("xdg-open \"%local_path%\""));
+ replace(item.second, Zstr("open -R \"%item_path%\""), Zstr("open -R \"%local_path%\""));
+ replace(item.second, Zstr("open \"%item_path%\""), Zstr("open \"%local_path%\""));
+
+ if (contains(makeUpperCopy(item.second), Zstr("WINMERGEU.EXE")) ||
+ contains(makeUpperCopy(item.second), Zstr("PSPAD.EXE")))
+ {
+ replace(item.second, Zstr("%item_path%"), Zstr("%local_path%"));
+ replace(item.second, Zstr("%item_path2%"), Zstr("%local_path2%"));
+ }
+ }
//last update check
inGui["LastOnlineCheck" ](config.gui.lastUpdateCheck);
inGui["LastOnlineVersion"](config.gui.lastOnlineVersion);
+ inGui["LastOnlineChanges"](config.gui.lastOnlineChangeLog);
//batch specific global settings
//XmlIn inBatch = in["Batch"];
}
-bool needsMigration(const XmlDoc& doc, int currentXmlFormatVer)
+int getConfigFormatVersion(const XmlDoc& doc)
{
//(try to) migrate old configuration if needed
int xmlFormatVer = 0;
/*bool success = */doc.root().getAttribute("XmlFormat", xmlFormatVer);
- return xmlFormatVer < currentXmlFormatVer;
+ return xmlFormatVer;
}
@@ -1170,15 +1193,17 @@ void readConfig(const Zstring& filepath, XmlType type, ConfigType& cfg, int curr
if (getXmlTypeNoThrow(doc) != type) //noexcept
throw FileError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filepath)));
+ const int formatVer = getConfigFormatVersion(doc);
+
XmlIn in(doc);
- ::readConfig(in, cfg);
+ ::readConfig(in, cfg, formatVer);
try
{
checkForMappingErrors(in, filepath); //throw FileError
//(try to) migrate old configuration if needed
- if (needsMigration(doc, currentXmlFormatVer))
+ if (formatVer< currentXmlFormatVer)
try { xmlAccess::writeConfig(cfg, filepath); /*throw FileError*/ }
catch (FileError&) { assert(false); } //don't bother user!
}
@@ -1213,16 +1238,18 @@ namespace
template
XmlCfg parseConfig(const XmlDoc& doc, const Zstring& filepath, int currentXmlFormatVer, std::wstring& warningMsg) //nothrow
{
+ const int formatVer = getConfigFormatVersion(doc);
+
XmlIn in(doc);
XmlCfg cfg;
- ::readConfig(in, cfg);
+ ::readConfig(in, cfg, formatVer);
try
{
checkForMappingErrors(in, filepath); //throw FileError
//(try to) migrate old configuration if needed
- if (needsMigration(doc, currentXmlFormatVer))
+ if (formatVer < currentXmlFormatVer)
try { xmlAccess::writeConfig(cfg, filepath); /*throw FileError*/ }
catch (FileError&) { assert(false); } //don't bother user!
}
@@ -1536,6 +1563,7 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out)
//last update check
outGui["LastOnlineCheck" ](config.gui.lastUpdateCheck);
outGui["LastOnlineVersion"](config.gui.lastOnlineVersion);
+ outGui["LastOnlineChanges"](config.gui.lastOnlineChangeLog);
//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 a752dc85..2bd8c0c0 100644
--- a/FreeFileSync/Source/lib/process_xml.h
+++ b/FreeFileSync/Source/lib/process_xml.h
@@ -41,7 +41,7 @@ enum OnGuiError
};
using Description = std::wstring;
-using Commandline = std::wstring;
+using Commandline = Zstring;
using ExternalApps = std::vector>;
//---------------------------------------------------------------------
@@ -160,6 +160,7 @@ struct XmlGlobalSettings
//---------------------------------------------------------------------
struct Gui
{
+ Gui() {} //clang needs this anyway
struct
{
wxPoint dlgPos;
@@ -220,7 +221,7 @@ struct XmlGlobalSettings
std::vector lastUsedConfigFiles;
std::vector cfgFileHistory;
- size_t cfgFileHistMax = 50;
+ size_t cfgFileHistMax = 100;
std::vector folderHistoryLeft;
std::vector folderHistoryRight;
@@ -231,25 +232,26 @@ struct XmlGlobalSettings
ExternalApps externelApplications
{
- //default external apps will be translated "on the fly"!!!
- //CONTRACT: first entry will be used for [Enter] or mouse double-click, second for open with default app!
+ //default external app descriptions will be translated "on the fly"!!!
+ //CONTRACT: first entry will be used for [Enter] or mouse double-click!
#ifdef ZEN_WIN
- { L"Show in Explorer", L"explorer /select, \"%item_path%\"" },
- { L"Open with default application", L"\"%item_path%\"" },
+ { L"Show in Explorer", Zstr("explorer /select, \"%local_path%\"") },
+ { L"Open with default application", Zstr("\"%local_path%\"") },
//mark for extraction: _("Show in Explorer")
//mark for extraction: _("Open with default application")
#elif defined ZEN_LINUX
- { L"Browse directory", L"xdg-open \"%item_folder%\"" },
- { L"Open with default application", L"xdg-open \"%item_path%\"" },
+ { L"Browse directory", Zstr("xdg-open \"%item_folder%\"") },
+ { L"Open with default application", Zstr("xdg-open \"%local_path%\"") },
//mark for extraction: _("Browse directory") Linux doesn't use the term "folder"
#elif defined ZEN_MAC
- { L"Browse directory", L"open -R \"%item_path%\"" },
- { L"Open with default application", L"open \"%item_path%\"" },
+ { L"Browse directory", Zstr("open -R \"%local_path%\"") },
+ { L"Open with default application", Zstr("open \"%local_path%\"") },
#endif
};
time_t lastUpdateCheck = 0; //number of seconds since 00:00 hours, Jan 1, 1970 UTC
std::wstring lastOnlineVersion;
+ std::wstring lastOnlineChangeLog;
} gui;
};
diff --git a/FreeFileSync/Source/synchronization.cpp b/FreeFileSync/Source/synchronization.cpp
index b268fafa..2f7576e4 100644
--- a/FreeFileSync/Source/synchronization.cpp
+++ b/FreeFileSync/Source/synchronization.cpp
@@ -266,7 +266,7 @@ namespace
bool significantDifferenceDetected(const SyncStatistics& folderPairStat)
{
//initial file copying shall not be detected as major difference
- if ((folderPairStat.createCount() == 0 ||
+ if ((folderPairStat.createCount< LEFT_SIDE>() == 0 ||
folderPairStat.createCount() == 0) &&
folderPairStat.updateCount () == 0 &&
folderPairStat.deleteCount () == 0 &&
@@ -546,7 +546,7 @@ public:
}
private:
- MinimumDiskSpaceNeeded() : spaceNeededLeft(), spaceNeededRight() {}
+ MinimumDiskSpaceNeeded() {}
void recurse(const HierarchyObject& hierObj)
{
@@ -606,8 +606,8 @@ private:
recurse(folder);
}
- std::int64_t spaceNeededLeft;
- std::int64_t spaceNeededRight;
+ std::int64_t spaceNeededLeft = 0;
+ std::int64_t spaceNeededRight = 0;
};
//----------------------------------------------------------------------------------------
@@ -1940,7 +1940,7 @@ void zen::synchronize(const TimeComp& timeStamp,
append(unresolvedConflicts, folderPairStat.getConflicts());
//exclude a few pathological cases (including empty left, right folders)
- if (AFS::equalAbstractPath(j->getAbstractPath(),
+ if (AFS::equalAbstractPath(j->getAbstractPath< LEFT_SIDE>(),
j->getAbstractPath()))
{
jobType[folderIndex] = FolderPairJobType::SKIP;
@@ -1983,7 +1983,7 @@ void zen::synchronize(const TimeComp& timeStamp,
}
//check for empty target folder paths: this only makes sense if empty field is source (and no DB files need to be created)
- if ((AFS::isNullPath(j->getAbstractPath()) && (writeLeft || folderPairCfg.saveSyncDB_)) ||
+ if ((AFS::isNullPath(j->getAbstractPath< LEFT_SIDE>()) && (writeLeft || folderPairCfg.saveSyncDB_)) ||
(AFS::isNullPath(j->getAbstractPath()) && (writeRight || folderPairCfg.saveSyncDB_)))
{
callback.reportFatalError(_("Target folder input field must not be empty."));
@@ -1994,7 +1994,7 @@ 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 (baseFolderDrop(*j, folderAccessTimeout, callback) ||
+ if (baseFolderDrop< LEFT_SIDE>(*j, folderAccessTimeout, callback) ||
baseFolderDrop(*j, folderAccessTimeout, callback))
{
jobType[folderIndex] = FolderPairJobType::SKIP;
@@ -2016,7 +2016,7 @@ void zen::synchronize(const TimeComp& timeStamp,
}
return false;
};
- if (sourceFolderMissing(j->getAbstractPath(), j->isExisting()) ||
+ if (sourceFolderMissing(j->getAbstractPath< LEFT_SIDE>(), j->isExisting< LEFT_SIDE>()) ||
sourceFolderMissing(j->getAbstractPath(), j->isExisting()))
{
jobType[folderIndex] = FolderPairJobType::SKIP;
@@ -2037,10 +2037,10 @@ void zen::synchronize(const TimeComp& timeStamp,
}
//check if more than 50% of total number of files/dirs are to be created/overwritten/deleted
- if (!AFS::isNullPath(j->getAbstractPath()) &&
+ if (!AFS::isNullPath(j->getAbstractPath< LEFT_SIDE>()) &&
!AFS::isNullPath(j->getAbstractPath()))
if (significantDifferenceDetected(folderPairStat))
- significantDiffPairs.emplace_back(j->getAbstractPath(),
+ significantDiffPairs.emplace_back(j->getAbstractPath< LEFT_SIDE>(),
j->getAbstractPath());
//check for sufficient free diskspace
@@ -2058,7 +2058,7 @@ void zen::synchronize(const TimeComp& timeStamp,
catch (FileError&) {} //for warning only => no need for tryReportingError()
};
const std::pair spaceNeeded = MinimumDiskSpaceNeeded::calculate(*j);
- checkSpace(j->getAbstractPath(), spaceNeeded.first);
+ checkSpace(j->getAbstractPath< LEFT_SIDE>(), spaceNeeded.first);
checkSpace(j->getAbstractPath(), spaceNeeded.second);
//windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong
@@ -2181,18 +2181,18 @@ void zen::synchronize(const TimeComp& timeStamp,
//------------------------------------------------------------------------------------------
callback.reportInfo(_("Synchronizing folder pair:") + L" [" + getVariantName(folderPairCfg.syncVariant_) + L"]\n" +
- L" " + AFS::getDisplayPath(j->getAbstractPath()) + L"\n" +
+ L" " + AFS::getDisplayPath(j->getAbstractPath< LEFT_SIDE>()) + L"\n" +
L" " + AFS::getDisplayPath(j->getAbstractPath()));
//------------------------------------------------------------------------------------------
//checking a second time: (a long time may have passed since the intro checks!)
- if (baseFolderDrop(*j, folderAccessTimeout, callback) ||
+ if (baseFolderDrop< LEFT_SIDE>(*j, folderAccessTimeout, callback) ||
baseFolderDrop(*j, folderAccessTimeout, callback))
continue;
//create base folders if not yet existing
if (folderPairStat.createCount() > 0 || folderPairCfg.saveSyncDB_) //else: temporary network drop leading to deletions already caught by "sourceFolderMissing" check!
- if (!createBaseFolder(*j, callback) || //+ detect temporary network drop!!
+ if (!createBaseFolder< LEFT_SIDE>(*j, callback) || //+ detect temporary network drop!!
!createBaseFolder(*j, callback)) //
continue;
@@ -2219,7 +2219,7 @@ void zen::synchronize(const TimeComp& timeStamp,
tryReportingError([&]
{
copyPermissionsFp = copyFilePermissions && //copy permissions only if asked for and supported by *both* sides!
- !AFS::isNullPath(j->getAbstractPath()) && //scenario: directory selected on one side only
+ !AFS::isNullPath(j->getAbstractPath< LEFT_SIDE>()) && //scenario: directory selected on one side only
!AFS::isNullPath(j->getAbstractPath()) && //
AFS::supportPermissionCopy(j->getAbstractPath(), j->getAbstractPath()); //throw FileError
}, callback); //throw X?
diff --git a/FreeFileSync/Source/ui/batch_config.cpp b/FreeFileSync/Source/ui/batch_config.cpp
index f43c9c06..869228d6 100644
--- a/FreeFileSync/Source/ui/batch_config.cpp
+++ b/FreeFileSync/Source/ui/batch_config.cpp
@@ -85,8 +85,7 @@ BatchDialog::BatchDialog(wxWindow* parent,
GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize()
//=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!!
-
- // Layout();
+ Center(); //needs to be re-applied after a dialog size change!
m_buttonSaveAs->SetFocus();
}
diff --git a/FreeFileSync/Source/ui/custom_grid.cpp b/FreeFileSync/Source/ui/custom_grid.cpp
index 7cacbac3..0296d4fd 100644
--- a/FreeFileSync/Source/ui/custom_grid.cpp
+++ b/FreeFileSync/Source/ui/custom_grid.cpp
@@ -344,23 +344,10 @@ private:
DisplayType output = DisplayType::NORMAL;
//mark directories and symlinks
- struct GetRowType : public FSObjectVisitor
- {
- GetRowType(DisplayType& result) : result_(result) {}
+ visitFSObject(*fsObj, [&](const FolderPair& folder) { output = DisplayType::FOLDER; },
+ [](const FilePair& file) {},
+ [&](const SymlinkPair& symlink) { output = DisplayType::SYMLINK; });
- void visit(const FilePair& file) override {}
- void visit(const SymlinkPair& symlink) override
- {
- result_ = DisplayType::SYMLINK;
- }
- void visit(const FolderPair& folder) override
- {
- result_ = DisplayType::FOLDER;
- }
- private:
- DisplayType& result_;
- } getType(output);
- fsObj->accept(getType);
return output;
}
@@ -368,103 +355,97 @@ private:
{
if (const FileSystemObject* fsObj = getRawData(row))
{
- struct GetTextValue : public FSObjectVisitor
- {
- GetTextValue(ColumnTypeRim ctr, ItemPathFormat fmt) : colType_(ctr), itemPathFormat_(fmt) {}
+ const ColumnTypeRim colTypeRim = static_cast(colType);
- void visit(const FilePair& file) override
+ std::wstring value;
+ visitFSObject(*fsObj, [&](const FolderPair& folder)
+ {
+ value = [&]
{
- value = [&]
+ switch (colTypeRim)
{
- switch (colType_)
- {
- case ColumnTypeRim::ITEM_PATH:
- switch (itemPathFormat_)
- {
- case ItemPathFormat::FULL_PATH:
- return file.isEmpty() ? std::wstring() : AFS::getDisplayPath(file.getAbstractPath());
- case ItemPathFormat::RELATIVE_PATH:
- return utfCvrtTo(file.getRelativePath());
- case ItemPathFormat::ITEM_NAME:
- return utfCvrtTo(file.getItemName());
- }
- break;
- case ColumnTypeRim::SIZE:
- //return file.isEmpty() ? std::wstring() : utfCvrtTo(file.getFileId()); // -> test file id
- return file.isEmpty() ? std::wstring() : toGuiString(file.getFileSize());
- case ColumnTypeRim::DATE:
- return file.isEmpty() ? std::wstring() : utcToLocalTimeString(file.getLastWriteTime());
- case ColumnTypeRim::EXTENSION:
- return utfCvrtTo(getFileExtension(file.getItemName()));
- }
- assert(false);
- return std::wstring();
- }();
- }
+ case ColumnTypeRim::ITEM_PATH:
+ switch (itemPathFormat)
+ {
+ case ItemPathFormat::FULL_PATH:
+ return folder.isEmpty() ? std::wstring() : AFS::getDisplayPath(folder.getAbstractPath());
+ case ItemPathFormat::RELATIVE_PATH:
+ return utfCvrtTo(folder.getRelativePath());
+ case ItemPathFormat::ITEM_NAME:
+ return utfCvrtTo(folder.getItemName());
+ }
+ break;
+ case ColumnTypeRim::SIZE:
+ return folder.isEmpty() ? std::wstring() : L"<" + _("Folder") + L">";
+ case ColumnTypeRim::DATE:
+ return std::wstring();
+ case ColumnTypeRim::EXTENSION:
+ return std::wstring();
+ }
+ assert(false);
+ return std::wstring();
+ }();
+ },
- void visit(const SymlinkPair& symlink) override
+ [&](const FilePair& file)
+ {
+ value = [&]
{
- value = [&]
+ switch (colTypeRim)
{
- switch (colType_)
- {
- case ColumnTypeRim::ITEM_PATH:
- switch (itemPathFormat_)
- {
- case ItemPathFormat::FULL_PATH:
- return symlink.isEmpty() ? std::wstring() : AFS::getDisplayPath(symlink.getAbstractPath());
- case ItemPathFormat::RELATIVE_PATH:
- return utfCvrtTo(symlink.getRelativePath());
- case ItemPathFormat::ITEM_NAME:
- return utfCvrtTo(symlink.getItemName());
- }
- break;
- case ColumnTypeRim::SIZE:
- return symlink.isEmpty() ? std::wstring() : L"<" + _("Symlink") + L">";
- case ColumnTypeRim::DATE:
- return symlink.isEmpty() ? std::wstring() : utcToLocalTimeString(symlink.getLastWriteTime());
- case ColumnTypeRim::EXTENSION:
- return utfCvrtTo(getFileExtension(symlink.getItemName()));
- }
- assert(false);
- return std::wstring();
- }();
- }
+ case ColumnTypeRim::ITEM_PATH:
+ switch (itemPathFormat)
+ {
+ case ItemPathFormat::FULL_PATH:
+ return file.isEmpty() ? std::wstring() : AFS::getDisplayPath(file.getAbstractPath());
+ case ItemPathFormat::RELATIVE_PATH:
+ return utfCvrtTo(file.getRelativePath());
+ case ItemPathFormat::ITEM_NAME:
+ return utfCvrtTo(file.getItemName());
+ }
+ break;
+ case ColumnTypeRim::SIZE:
+ //return file.isEmpty() ? std::wstring() : utfCvrtTo(file.getFileId()); // -> test file id
+ return file.isEmpty() ? std::wstring() : toGuiString(file.getFileSize());
+ case ColumnTypeRim::DATE:
+ return file.isEmpty() ? std::wstring() : utcToLocalTimeString(file.getLastWriteTime());
+ case ColumnTypeRim::EXTENSION:
+ return utfCvrtTo(getFileExtension(file.getItemName()));
+ }
+ assert(false);
+ return std::wstring();
+ }();
+ },
- void visit(const FolderPair& folder) override
+ [&](const SymlinkPair& symlink)
+ {
+ value = [&]
{
- value = [&]
+ switch (colTypeRim)
{
- switch (colType_)
- {
- case ColumnTypeRim::ITEM_PATH:
- switch (itemPathFormat_)
- {
- case ItemPathFormat::FULL_PATH:
- return folder.isEmpty() ? std::wstring() : AFS::getDisplayPath(folder.getAbstractPath());
- case ItemPathFormat::RELATIVE_PATH:
- return utfCvrtTo(folder.getRelativePath());
- case ItemPathFormat::ITEM_NAME:
- return utfCvrtTo