diff options
author | Daniel Wilhelm <shieldwed@outlook.com> | 2017-03-12 22:00:35 -0600 |
---|---|---|
committer | Daniel Wilhelm <shieldwed@outlook.com> | 2017-03-12 22:00:35 -0600 |
commit | 3ba62ef1de77153e5a8c7bad4451b96f6a1678b0 (patch) | |
tree | e6e69717e394a528a2e2aca3af036d4befaa9658 | |
parent | 8.9 (diff) | |
download | FreeFileSync-3ba62ef1de77153e5a8c7bad4451b96f6a1678b0.tar.gz FreeFileSync-3ba62ef1de77153e5a8c7bad4451b96f6a1678b0.tar.bz2 FreeFileSync-3ba62ef1de77153e5a8c7bad4451b96f6a1678b0.zip |
8.10
95 files changed, 3480 insertions, 1691 deletions
diff --git a/Changelog.txt b/Changelog.txt index c24b773c..36843a51 100755 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,20 @@ +FreeFileSync 8.10 [2017-03-12]
+------------------------------
+Fully preserve case-sensitive file paths (Windows, macOS)
+Support SFTP connections to local hosts
+Warn if versioning folder is contained in a base folder
+Use natural string sorting algorithm for item lists
+Consider exclude filter settings for folder dependency checks
+Fixed file not found error on case-sensitive SFTP volume
+Fixed failure when creating MTP sub directories
+Fixed crash when loading database file during comparison
+Refactored UTF conversion routines
+Use pipe symbol as filter separator instead of semicolon
+Iterate over all matching SFTP connections available on a server (macOS)
+Reduced folder matching time by 12%, average memory use by 11%
+Added experimental FTP support
+
+
FreeFileSync 8.9 [2017-02-08]
-----------------------------
Detect when database file was copied and avoid "second part missing" error
diff --git a/FreeFileSync/Build/Help/html/exclude-items.html b/FreeFileSync/Build/Help/html/exclude-items.html index bb1d110b..1a8626ce 100755 --- a/FreeFileSync/Build/Help/html/exclude-items.html +++ b/FreeFileSync/Build/Help/html/exclude-items.html @@ -21,7 +21,7 @@ <ul style="margin: 0">
<li>Each list item must be a file or directory path <b>relative</b> to synchronization base directories.
- <li>Multiple items must be separated by <b>;</b> or a new line.
+ <li>Multiple items must be separated by <b>|</b> or a <b>new line</b>.
<li>Wild cards <b>*</b> and <b>?</b> may be used: <b>*</b> means zero or more characters while <b>?</b> represents exactly one character.
</ul>
@@ -57,7 +57,7 @@ </tr>
<tr>
<td>Multiple entries separated by semicolon</td>
- <td><span class="file-path">*.tmp; *.doc; *.bak</span></td>
+ <td><span class="file-path">*.tmp | *.doc | *.bak</span></td>
</tr>
<tr>
<td>All subdirectories of the base directories</td>
diff --git a/FreeFileSync/Build/Help/images/filter.png b/FreeFileSync/Build/Help/images/filter.png Binary files differindex e6251537..d2ad979d 100755 --- a/FreeFileSync/Build/Help/images/filter.png +++ b/FreeFileSync/Build/Help/images/filter.png diff --git a/FreeFileSync/Source/RealTimeSync/application.cpp b/FreeFileSync/Source/RealTimeSync/application.cpp index efdff388..847fb83e 100755 --- a/FreeFileSync/Source/RealTimeSync/application.cpp +++ b/FreeFileSync/Source/RealTimeSync/application.cpp @@ -11,7 +11,6 @@ #include <wx/event.h>
#include <wx/log.h>
#include <wx/tooltip.h>
-#include <wx+/string_conv.h>
#include <wx+/popup_dlg.h>
#include <wx+/image_resources.h>
#include "xml_proc.h"
@@ -57,7 +56,7 @@ bool Application::OnInit() }
catch (const FileError& e)
{
- warn_static("Bug? (exit on frame delete)")
+ //following dialog does NOT trigger "exit on frame delete" while we are in OnInit(): http://docs.wxwidgets.org/trunk/overview_app.html#overview_app_shutdown
showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString()));
//continue!
}
@@ -91,7 +90,7 @@ void Application::onEnterEventLoop(wxEvent& event) std::vector<Zstring> commandArgs;
for (int i = 1; i < argc; ++i)
{
- Zstring filePath = getResolvedFilePath(toZ(argv[i]));
+ Zstring filePath = getResolvedFilePath(utfTo<Zstring>(argv[i]));
if (!fileAvailable(filePath)) //be a little tolerant
{
diff --git a/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp b/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp index a0b04405..b892fb44 100755 --- a/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp +++ b/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp @@ -10,7 +10,6 @@ #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"
#include <gtk/gtk.h>
@@ -24,15 +23,15 @@ namespace void setFolderPath(const Zstring& dirpath, wxTextCtrl* txtCtrl, wxWindow& tooltipWnd, wxStaticText* staticText) //pointers are optional
{
if (txtCtrl)
- txtCtrl->ChangeValue(toWx(dirpath));
+ txtCtrl->ChangeValue(utfTo<wxString>(dirpath));
const Zstring folderPathFmt = getResolvedFilePath(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(folderPathFmt)); //who knows when the real bugfix reaches mere mortals via an official release...
+ tooltipWnd.SetToolTip(utfTo<wxString>(folderPathFmt)); //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(folderPathFmt)) ? wxString(_("Drag && drop")) : toWx(folderPathFmt));
+ staticText->SetLabel(equalFilePath(appendSeparator(trimCpy(dirpath)), appendSeparator(folderPathFmt)) ? wxString(_("Drag && drop")) : utfTo<wxString>(folderPathFmt));
}
}
@@ -113,7 +112,7 @@ void FolderSelector2::onFilesDropped(FileDropEvent& event) void FolderSelector2::onEditFolderPath(wxCommandEvent& event)
{
- setFolderPath(toZ(event.GetString()), nullptr, folderPathCtrl_, staticText_);
+ setFolderPath(utfTo<Zstring>(event.GetString()), nullptr, folderPathCtrl_, staticText_);
event.Skip();
}
@@ -135,10 +134,10 @@ void FolderSelector2::onSelectDir(wxCommandEvent& event) }
}
- wxDirDialog dirPicker(&selectButton_, _("Select a folder"), toWx(defaultFolderPath)); //put modal wxWidgets dialogs on stack: creating on freestore leads to memleak!
+ wxDirDialog dirPicker(&selectButton_, _("Select a folder"), utfTo<wxString>(defaultFolderPath)); //put modal wxWidgets dialogs on stack: creating on freestore leads to memleak!
if (dirPicker.ShowModal() != wxID_OK)
return;
- const Zstring newFolderPath = toZ(dirPicker.GetPath());
+ const Zstring newFolderPath = utfTo<Zstring>(dirPicker.GetPath());
setFolderPath(newFolderPath, &folderPathCtrl_, folderPathCtrl_, staticText_);
}
@@ -146,7 +145,7 @@ void FolderSelector2::onSelectDir(wxCommandEvent& event) Zstring FolderSelector2::getPath() const
{
- Zstring path = utfCvrtTo<Zstring>(folderPathCtrl_.GetValue());
+ Zstring path = utfTo<Zstring>(folderPathCtrl_.GetValue());
return path;
}
diff --git a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp index 9376e2f7..91db8b2e 100755 --- a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp +++ b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp @@ -8,7 +8,6 @@ #include <wx/wupdlock.h>
#include <wx/filedlg.h>
#include <wx+/bitmap_button.h>
-#include <wx+/string_conv.h>
#include <wx+/font_size.h>
#include <wx+/popup_dlg.h>
#include <wx+/image_resources.h>
@@ -195,7 +194,7 @@ void MainDialog::OnStart(wxCommandEvent& event) xmlAccess::XmlRealConfig currentCfg = getConfiguration();
- switch (rts::startDirectoryMonitor(currentCfg, xmlAccess::extractJobName(utfCvrtTo<Zstring>(currentConfigFileName_))))
+ switch (rts::startDirectoryMonitor(currentCfg, xmlAccess::extractJobName(utfTo<Zstring>(currentConfigFileName_))))
{
case rts::EXIT_APP:
Close();
@@ -220,14 +219,14 @@ void MainDialog::OnConfigSave(wxCommandEvent& event) wxFileDialog filePicker(this,
wxString(),
//OS X really needs dir/file separated like this:
- utfCvrtTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir
- utfCvrtTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file
+ utfTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir
+ utfTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file
wxString(L"RealTimeSync (*.ffs_real)|*.ffs_real") + L"|" +_("All files") + L" (*.*)|*",
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (filePicker.ShowModal() != wxID_OK)
return;
- const Zstring newFileName = utfCvrtTo<Zstring>(filePicker.GetPath());
+ const Zstring newFileName = utfTo<Zstring>(filePicker.GetPath());
//write config to XML
const xmlAccess::XmlRealConfig currentCfg = getConfiguration();
@@ -276,7 +275,7 @@ void MainDialog::setLastUsedConfig(const Zstring& filepath) }
else
{
- SetTitle(utfCvrtTo<wxString>(filepath));
+ SetTitle(utfTo<wxString>(filepath));
currentConfigFileName_ = filepath;
}
}
@@ -286,12 +285,12 @@ void MainDialog::OnConfigLoad(wxCommandEvent& event) {
wxFileDialog filePicker(this,
wxString(),
- utfCvrtTo<wxString>(beforeLast(currentConfigFileName_, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir
+ utfTo<wxString>(beforeLast(currentConfigFileName_, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir
wxString(), //default file
wxString(L"RealTimeSync (*.ffs_real; *.ffs_batch)|*.ffs_real;*.ffs_batch") + L"|" +_("All files") + L" (*.*)|*",
wxFD_OPEN);
if (filePicker.ShowModal() == wxID_OK)
- loadConfig(utfCvrtTo<Zstring>(filePicker.GetPath()));
+ loadConfig(utfTo<Zstring>(filePicker.GetPath()));
}
@@ -299,7 +298,7 @@ void MainDialog::onFilesDropped(FileDropEvent& event) {
const auto& filePaths = event.getPaths();
if (!filePaths.empty())
- loadConfig(utfCvrtTo<Zstring>(filePaths[0]));
+ loadConfig(utfTo<Zstring>(filePaths[0]));
}
@@ -319,7 +318,7 @@ void MainDialog::setConfiguration(const xmlAccess::XmlRealConfig& cfg) }
//fill commandline
- m_textCtrlCommand->SetValue(utfCvrtTo<wxString>(cfg.commandline));
+ m_textCtrlCommand->SetValue(utfTo<wxString>(cfg.commandline));
//set delay
m_spinCtrlDelay->SetValue(static_cast<int>(cfg.delay));
@@ -330,11 +329,11 @@ xmlAccess::XmlRealConfig MainDialog::getConfiguration() {
xmlAccess::XmlRealConfig output;
- output.directories.push_back(utfCvrtTo<Zstring>(dirpathFirst->getPath()));
+ output.directories.push_back(utfTo<Zstring>(dirpathFirst->getPath()));
for (const DirectoryPanel* dne : dirpathsExtra)
- output.directories.push_back(utfCvrtTo<Zstring>(dne->getPath()));
+ output.directories.push_back(utfTo<Zstring>(dne->getPath()));
- output.commandline = utfCvrtTo<Zstring>(m_textCtrlCommand->GetValue());
+ output.commandline = utfTo<Zstring>(m_textCtrlCommand->GetValue());
output.delay = m_spinCtrlDelay->GetValue();
return output;
@@ -343,7 +342,7 @@ xmlAccess::XmlRealConfig MainDialog::getConfiguration() void MainDialog::OnAddFolder(wxCommandEvent& event)
{
- const Zstring topFolder = utfCvrtTo<Zstring>(dirpathFirst->getPath());
+ const Zstring topFolder = utfTo<Zstring>(dirpathFirst->getPath());
//clear existing top folder first
dirpathFirst->setPath(Zstring());
diff --git a/FreeFileSync/Source/RealTimeSync/monitor.cpp b/FreeFileSync/Source/RealTimeSync/monitor.cpp index 6d80e689..2d88dc3e 100755 --- a/FreeFileSync/Source/RealTimeSync/monitor.cpp +++ b/FreeFileSync/Source/RealTimeSync/monitor.cpp @@ -31,9 +31,9 @@ std::vector<Zstring> getFormattedDirs(const std::vector<Zstring>& folderPathPhra std::set<Zstring, LessFilePath> folderPaths; //make unique
for (const Zstring& phrase : std::set<Zstring, LessFilePath>(folderPathPhrases.begin(), folderPathPhrases.end()))
{
- if (ciStartsWith(trimCpy(phrase), Zstr("ftp:")) ||
- ciStartsWith(trimCpy(phrase), Zstr("sftp:")) ||
- ciStartsWith(trimCpy(phrase), Zstr("mtp:")))
+ if (startsWith(trimCpy(phrase), Zstr("ftp:"), CmpAsciiNoCase()) ||
+ startsWith(trimCpy(phrase), Zstr("sftp:"), CmpAsciiNoCase()) ||
+ startsWith(trimCpy(phrase), Zstr("mtp:"), CmpAsciiNoCase()))
throw FileError(_("The following path does not support directory monitoring:") + L"\n\n" + fmtPath(phrase));
//make unique: no need to resolve duplicate phrases more than once! (consider "[volume name]" syntax) -> shouldn't this be already buffered by OS?
@@ -257,7 +257,7 @@ void rts::monitorDirectories(const std::vector<Zstring>& folderPathPhrases, unsi }
catch (ExecCommandNowException&) {}
- ::wxSetEnv(L"change_path", utfCvrtTo<wxString>(lastChangeDetected.filepath_)); //some way to output what file changed to the user
+ ::wxSetEnv(L"change_path", utfTo<wxString>(lastChangeDetected.filepath_)); //some way to output what file changed to the user
::wxSetEnv(L"change_action", toString(lastChangeDetected.action_)); //
//execute command
diff --git a/FreeFileSync/Source/RealTimeSync/xml_proc.cpp b/FreeFileSync/Source/RealTimeSync/xml_proc.cpp index 998b2921..1e097e98 100755 --- a/FreeFileSync/Source/RealTimeSync/xml_proc.cpp +++ b/FreeFileSync/Source/RealTimeSync/xml_proc.cpp @@ -7,7 +7,6 @@ #include "xml_proc.h"
#include <zen/file_access.h>
#include <wx/filefn.h>
-#include <wx+/string_conv.h>
#include "../lib/process_xml.h"
#include "../lib/ffs_paths.h"
diff --git a/FreeFileSync/Source/algorithm.cpp b/FreeFileSync/Source/algorithm.cpp index 442a1aa8..43654f71 100755 --- a/FreeFileSync/Source/algorithm.cpp +++ b/FreeFileSync/Source/algorithm.cpp @@ -40,12 +40,12 @@ namespace class Redetermine
{
public:
- static void execute(const DirectionSet& dirCfgIn, HierarchyObject& hierObj) { Redetermine(dirCfgIn).recurse(hierObj); }
+ static void execute(const DirectionSet& dirCfgIn, ContainerObject& hierObj) { Redetermine(dirCfgIn).recurse(hierObj); }
private:
Redetermine(const DirectionSet& dirCfgIn) : dirCfg(dirCfgIn) {}
- void recurse(HierarchyObject& hierObj) const
+ void recurse(ContainerObject& hierObj) const
{
for (FilePair& file : hierObj.refSubFiles())
processFile(file);
@@ -168,7 +168,7 @@ private: //---------------------------------------------------------------------------------------------------------------
//test if non-equal items exist in scanned data
-bool allItemsCategoryEqual(const HierarchyObject& hierObj)
+bool allItemsCategoryEqual(const ContainerObject& hierObj)
{
return std::all_of(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(),
[](const FilePair& file) { return file.getCategory() == FILE_EQUAL; })&& //files
@@ -338,7 +338,7 @@ private: detectMovePairs(dbFolder);
}
- void recurse(HierarchyObject& hierObj, const InSyncFolder* dbFolder)
+ void recurse(ContainerObject& hierObj, const InSyncFolder* dbFolder)
{
for (FilePair& file : hierObj.refSubFiles())
{
@@ -510,7 +510,7 @@ private: recurse(baseFolder, &dbFolder);
}
- void recurse(HierarchyObject& hierObj, const InSyncFolder* dbFolder) const
+ void recurse(ContainerObject& hierObj, const InSyncFolder* dbFolder) const
{
for (FilePair& file : hierObj.refSubFiles())
processFile(file, dbFolder);
@@ -791,7 +791,7 @@ void zen::setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsOb namespace
{
template <bool include>
-void inOrExcludeAllRows(HierarchyObject& hierObj)
+void inOrExcludeAllRows(ContainerObject& hierObj)
{
for (FilePair& file : hierObj.refSubFiles())
file.setActive(include);
@@ -860,12 +860,12 @@ template <FilterStrategy strategy> class ApplyHardFilter
{
public:
- static void execute(HierarchyObject& hierObj, const HardFilter& filterProcIn) { ApplyHardFilter(hierObj, filterProcIn); }
+ static void execute(ContainerObject& hierObj, const HardFilter& filterProcIn) { ApplyHardFilter(hierObj, filterProcIn); }
private:
- ApplyHardFilter(HierarchyObject& hierObj, const HardFilter& filterProcIn) : filterProc(filterProcIn) { recurse(hierObj); }
+ ApplyHardFilter(ContainerObject& hierObj, const HardFilter& filterProcIn) : filterProc(filterProcIn) { recurse(hierObj); }
- void recurse(HierarchyObject& hierObj) const
+ void recurse(ContainerObject& hierObj) const
{
for (FilePair& file : hierObj.refSubFiles())
processFile(file);
@@ -912,12 +912,12 @@ template <FilterStrategy strategy> class ApplySoftFilter //falsify only! -> can run directly after "hard/base filter"
{
public:
- static void execute(HierarchyObject& hierObj, const SoftFilter& timeSizeFilter) { ApplySoftFilter(hierObj, timeSizeFilter); }
+ static void execute(ContainerObject& hierObj, const SoftFilter& timeSizeFilter) { ApplySoftFilter(hierObj, timeSizeFilter); }
private:
- ApplySoftFilter(HierarchyObject& hierObj, const SoftFilter& timeSizeFilter) : timeSizeFilter_(timeSizeFilter) { recurse(hierObj); }
+ ApplySoftFilter(ContainerObject& hierObj, const SoftFilter& timeSizeFilter) : timeSizeFilter_(timeSizeFilter) { recurse(hierObj); }
- void recurse(zen::HierarchyObject& hierObj) const
+ void recurse(zen::ContainerObject& hierObj) const
{
for (FilePair& file : hierObj.refSubFiles())
processFile(file);
@@ -1046,16 +1046,16 @@ void zen::applyFiltering(FolderComparison& folderCmp, const MainConfiguration& m class FilterByTimeSpan
{
public:
- static void execute(HierarchyObject& hierObj, int64_t timeFrom, int64_t timeTo) { FilterByTimeSpan(hierObj, timeFrom, timeTo); }
+ static void execute(ContainerObject& hierObj, int64_t timeFrom, int64_t timeTo) { FilterByTimeSpan(hierObj, timeFrom, timeTo); }
private:
- FilterByTimeSpan(HierarchyObject& hierObj,
+ FilterByTimeSpan(ContainerObject& hierObj,
int64_t timeFrom,
int64_t timeTo) :
timeFrom_(timeFrom),
timeTo_(timeTo) { recurse(hierObj); }
- void recurse(HierarchyObject& hierObj) const
+ void recurse(ContainerObject& hierObj) const
{
for (FilePair& file : hierObj.refSubFiles())
processFile(file);
@@ -1110,6 +1110,45 @@ void zen::applyTimeSpanFilter(FolderComparison& folderCmp, int64_t timeFrom, int std::for_each(begin(folderCmp), end(folderCmp), [&](BaseFolderPair& baseFolder) { FilterByTimeSpan::execute(baseFolder, timeFrom, timeTo); });
}
+
+Opt<PathDependency> zen::getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL,
+ const AbstractPath& basePathR, const HardFilter& filterR)
+{
+ if (!AFS::isNullPath(basePathL) && !AFS::isNullPath(basePathR))
+ {
+ const AFS::PathComponents compL = AFS::getPathComponents(basePathL);
+ const AFS::PathComponents compR = AFS::getPathComponents(basePathR);
+ if (AFS::compareAbstractPath(compL.rootPath, compR.rootPath) == 0)
+ {
+ const bool leftParent = compL.relPath.size() <= compR.relPath.size();
+
+ const auto& relPathP = leftParent ? compL.relPath : compR.relPath;
+ const auto& relPathC = leftParent ? compR.relPath : compL.relPath;
+
+ if (std::equal(relPathP.begin(), relPathP.end(), relPathC.begin(), [](const Zstring& lhs, const Zstring& rhs) { return equalFilePath(lhs, rhs); }))
+ {
+ Zstring relDirPath;
+ std::for_each(relPathC.begin() + relPathP.size(), relPathC.end(), [&](const Zstring& itemName)
+ {
+ relDirPath = AFS::appendPaths(relDirPath, itemName, FILE_NAME_SEPARATOR);
+ });
+ const AbstractPath& basePathP = leftParent ? basePathL : basePathR;
+ const AbstractPath& basePathC = leftParent ? basePathR : basePathL;
+
+ const HardFilter& filterP = leftParent ? filterL : filterR;
+ //if there's a dependency, check if the sub directory is (fully) excluded via filter
+ //=> easy to check but still insufficient in general:
+ // - one folder may have a *.txt include-filter, the other a *.lng include filter => no dependencies, but "childItemMightMatch = true" below!
+ // - user may have manually excluded the conflicting items or changed the filter settings without running a re-compare
+ bool childItemMightMatch = true;
+ if (relDirPath.empty() || filterP.passDirFilter(relDirPath, &childItemMightMatch) || childItemMightMatch)
+ return PathDependency({ basePathP, basePathC, relDirPath });
+ }
+ }
+ }
+ return NoValue();
+}
+
//############################################################################################################
std::pair<std::wstring, int> zen::getSelectedItemsAsString(const std::vector<const FileSystemObject*>& selectionLeft,
@@ -1172,8 +1211,8 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT }
catch (FileError&)
{
- Opt<AFS::PathDetails> pd;
- try { pd = AFS::getPathDetails(targetPath); /*throw FileError*/ }
+ Opt<AFS::PathStatus> pd;
+ try { pd = AFS::getPathStatus(targetPath); /*throw FileError*/ }
catch (FileError&) {} //previous exception is more relevant
if (pd)
@@ -1216,8 +1255,8 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT }
catch (FileError&)
{
- Opt<AFS::PathDetails> pd;
- try { pd = AFS::getPathDetails(targetPath); /*throw FileError*/ }
+ Opt<AFS::PathStatus> pd;
+ try { pd = AFS::getPathStatus(targetPath); /*throw FileError*/ }
catch (FileError&) {} //previous exception is more relevant
if (pd)
@@ -1441,7 +1480,7 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete FolderComparison& folderCmp, //attention: rows will be physically deleted!
const std::vector<DirectionConfig>& directCfgs,
bool useRecycleBin,
- bool& warningRecyclerMissing,
+ bool& warnRecyclerMissing,
ProcessCallback& callback)
{
if (folderCmp.empty())
@@ -1524,7 +1563,7 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete if (!item.second)
msg += L"\n" + AFS::getDisplayPath(item.first);
- callback.reportWarning(msg, warningRecyclerMissing); //throw?
+ callback.reportWarning(msg, warnRecyclerMissing); //throw?
}
deleteFromGridAndHDOneSide<LEFT_SIDE>(deleteRecylerLeft, true, callback);
diff --git a/FreeFileSync/Source/algorithm.h b/FreeFileSync/Source/algorithm.h index e905a751..e97692ee 100755 --- a/FreeFileSync/Source/algorithm.h +++ b/FreeFileSync/Source/algorithm.h @@ -43,6 +43,15 @@ void applyTimeSpanFilter(FolderComparison& folderCmp, int64_t timeFrom, int64_t void setActiveStatus(bool newStatus, FolderComparison& folderCmp); //activate or deactivate all rows
void setActiveStatus(bool newStatus, FileSystemObject& fsObj); //activate or deactivate row: (not recursively anymore)
+struct PathDependency
+{
+ AbstractPath basePathParent;
+ AbstractPath basePathChild;
+ Zstring relPath; //filled if child path is sub folder of parent path; empty if child path == parent path
+};
+Opt<PathDependency> getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL,
+ const AbstractPath& basePathR, const HardFilter& filterR);
+
std::pair<std::wstring, int> getSelectedItemsAsString( //returns string with item names and total count of selected(!) items, NOT total files/dirs!
const std::vector<const FileSystemObject*>& selectionLeft, //all pointers need to be bound!
const std::vector<const FileSystemObject*>& selectionRight); //
@@ -62,7 +71,7 @@ void deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDeleteOnLef const std::vector<DirectionConfig>& directCfgs,
bool useRecycleBin,
//global warnings:
- bool& warningRecyclerMissing,
+ bool& warnRecyclerMissing,
ProcessCallback& callback);
//get native Win32 paths or create temporary copy for SFTP/MTP, ect.
diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp index da40581c..199bb01c 100755 --- a/FreeFileSync/Source/application.cpp +++ b/FreeFileSync/Source/application.cpp @@ -7,10 +7,10 @@ #include "application.h"
#include <memory>
#include <zen/file_access.h>
+#include <zen/perf.h>
#include <wx/tooltip.h>
#include <wx/log.h>
#include <wx+/app_main.h>
-#include <wx+/string_conv.h>
#include <wx+/popup_dlg.h>
#include <wx+/image_resources.h>
#include "comparison.h"
@@ -40,7 +40,7 @@ std::vector<Zstring> getCommandlineArgs(const wxApp& app) {
std::vector<Zstring> args;
for (int i = 1; i < app.argc; ++i) //wxWidgets screws up once again making "argv implicitly convertible to a wxChar**" in 2.9.3,
- args.push_back(toZ(wxString(app.argv[i]))); //so we are forced to use this pitiful excuse for a range construction!!
+ args.push_back(utfTo<Zstring>(wxString(app.argv[i]))); //so we are forced to use this pitiful excuse for a range construction!!
return args;
}
@@ -150,7 +150,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs) //error handling strategy unknown and no sync log output available at this point! => show message box
- logFatalError(utfCvrtTo<std::string>(msg));
+ logFatalError(utfTo<std::string>(msg));
wxSafeShowMessage(title, msg);
@@ -174,30 +174,30 @@ void Application::launch(const std::vector<Zstring>& commandArgs) if (it == arg.begin()) return false; //require at least one prefix character
const Zstring argTmp(it, arg.end());
- return ciEqual(argTmp, Zstr("help")) ||
- ciEqual(argTmp, Zstr("h")) ||
+ return strEqual(argTmp, Zstr("help"), CmpAsciiNoCase()) ||
+ strEqual(argTmp, Zstr("h"), CmpAsciiNoCase()) ||
argTmp == Zstr("?");
};
for (auto it = commandArgs.begin(); it != commandArgs.end(); ++it)
if (syntaxHelpRequested(*it))
return showSyntaxHelp();
- else if (ciEqual(*it, optionEdit))
+ else if (strEqual(*it, optionEdit, CmpAsciiNoCase()))
openForEdit = true;
- else if (ciEqual(*it, optionLeftDir))
+ else if (strEqual(*it, optionLeftDir, CmpAsciiNoCase()))
{
if (++it == commandArgs.end())
{
- notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfCvrtTo<std::wstring>(optionLeftDir)), _("Syntax error"));
+ notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo<std::wstring>(optionLeftDir)), _("Syntax error"));
return;
}
dirPathPhrasesLeft.push_back(*it);
}
- else if (ciEqual(*it, optionRightDir))
+ else if (strEqual(*it, optionRightDir, CmpAsciiNoCase()))
{
if (++it == commandArgs.end())
{
- notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfCvrtTo<std::wstring>(optionRightDir)), _("Syntax error"));
+ notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo<std::wstring>(optionRightDir)), _("Syntax error"));
return;
}
dirPathPhrasesRight.push_back(*it);
@@ -433,7 +433,7 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat if (batchCfg.handleError == ON_ERROR_POPUP)
showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(msg));
else //"exit" or "ignore"
- logFatalError(utfCvrtTo<std::string>(msg));
+ logFatalError(utfTo<std::string>(msg));
raiseReturnCode(returnCode, rc);
};
diff --git a/FreeFileSync/Source/comparison.cpp b/FreeFileSync/Source/comparison.cpp index 485758cb..83202257 100755 --- a/FreeFileSync/Source/comparison.cpp +++ b/FreeFileSync/Source/comparison.cpp @@ -101,7 +101,7 @@ ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& cfgL {
errorMsg += L"\n___________________________________________";
for (const auto& fc : status.failedChecks)
- errorMsg += std::wstring(L"\n\n") + replaceCpy(fc.second.toString(), L"\n\n", L"\n");
+ errorMsg += L"\n\n" + replaceCpy(fc.second.toString(), L"\n\n", L"\n");
}
throw FileError(errorMsg);
@@ -111,48 +111,6 @@ ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& cfgL return output;
}
-
-void checkForIncompleteInput(const std::vector<ResolvedFolderPair>& folderPairs, bool& warningInputFieldEmpty, ProcessCallback& callback)
-{
- bool havePartialPair = false;
- bool haveFullPair = false;
-
- for (const ResolvedFolderPair& fp : folderPairs)
- if (AFS::isNullPath(fp.folderPathLeft) != AFS::isNullPath(fp.folderPathRight))
- havePartialPair = true;
- else if (!AFS::isNullPath(fp.folderPathLeft))
- haveFullPair = true;
-
- if (havePartialPair == haveFullPair) //error if: all empty or exist both full and partial pairs -> support single-folder comparison scenario
- callback.reportWarning(_("A folder input field is empty.") + L" \n\n" +
- _("The corresponding folder will be considered as empty."), warningInputFieldEmpty);
-}
-
-
-//check whether one side is subdirectory of other side (folder pair wise!)
-//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<ResolvedFolderPair> dependentFolderPairs;
-
- for (const ResolvedFolderPair& fp : folderPairs)
- if (!AFS::isNullPath(fp.folderPathLeft) && !AFS::isNullPath(fp.folderPathRight)) //empty folders names may be accepted by user
- //test wheter leftDirectory begins with rightDirectory or the other way round
- if (AFS::havePathDependency(fp.folderPathLeft, fp.folderPathRight))
- dependentFolderPairs.push_back(fp);
-
- if (!dependentFolderPairs.empty())
- {
- std::wstring warningMsg = _("The following folder paths are dependent from each other:");
- for (const ResolvedFolderPair& pair : dependentFolderPairs)
- warningMsg += L"\n\n" +
- AFS::getDisplayPath(pair.folderPathLeft) + L"\n" +
- AFS::getDisplayPath(pair.folderPathRight);
-
- callback.reportWarning(warningMsg, warningDependentFolders);
- }
-}
-
//#############################################################################################################################
class ComparisonBuffer
@@ -546,7 +504,7 @@ public: undefinedFiles(undefinedFilesOut),
undefinedSymlinks(undefinedSymlinksOut) {}
- void execute(const FolderContainer& lhs, const FolderContainer& rhs, HierarchyObject& output)
+ void execute(const FolderContainer& lhs, const FolderContainer& rhs, ContainerObject& output)
{
auto it = failedItemReads_.find(Zstring()); //empty path if read-error for whole base directory
@@ -556,10 +514,10 @@ public: }
private:
- void mergeTwoSides(const FolderContainer& lhs, const FolderContainer& rhs, const std::wstring* errorMsg, HierarchyObject& output);
+ void mergeTwoSides(const FolderContainer& lhs, const FolderContainer& rhs, const std::wstring* errorMsg, ContainerObject& output);
template <SelectedSide side>
- void fillOneSide(const FolderContainer& folderCont, const std::wstring* errorMsg, HierarchyObject& output);
+ void fillOneSide(const FolderContainer& folderCont, const std::wstring* errorMsg, ContainerObject& output);
const std::wstring* checkFailedRead(FileSystemObject& fsObj, const std::wstring* errorMsg);
@@ -589,7 +547,7 @@ const std::wstring* MergeSides::checkFailedRead(FileSystemObject& fsObj, const s template <SelectedSide side>
-void MergeSides::fillOneSide(const FolderContainer& folderCont, const std::wstring* errorMsg, HierarchyObject& output)
+void MergeSides::fillOneSide(const FolderContainer& folderCont, const std::wstring* errorMsg, ContainerObject& output)
{
for (const auto& file : folderCont.files)
{
@@ -653,7 +611,7 @@ void linearMerge(const MapType& mapLeft, const MapType& mapRight, ProcessLeftOnl }
-void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer& rhs, const std::wstring* errorMsg, HierarchyObject& output)
+void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer& rhs, const std::wstring* errorMsg, ContainerObject& output)
{
using FileData = const FolderContainer::FileList::value_type;
@@ -670,7 +628,7 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer fileRight.second);
if (!checkFailedRead(newItem, errorMsg))
undefinedFiles.push_back(&newItem);
- static_assert(IsSameType<HierarchyObject::FileList, FixedList<FilePair>>::value, ""); //HierarchyObject::addSubFile() must NOT invalidate references used in "undefinedFiles"!
+ static_assert(IsSameType<ContainerObject::FileList, FixedList<FilePair>>::value, ""); //ContainerObject::addSubFile() must NOT invalidate references used in "undefinedFiles"!
});
//-----------------------------------------------------------------------------------------------
@@ -724,7 +682,7 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer //-----------------------------------------------------------------------------------------------
//uncheck excluded directories (see fillBuffer()) + remove superfluous excluded subdirectories
-void stripExcludedDirectories(HierarchyObject& hierObj, const HardFilter& filterProc)
+void stripExcludedDirectories(ContainerObject& hierObj, const HardFilter& filterProc)
{
for (FolderPair& folder : hierObj.refSubFolders())
stripExcludedDirectories(folder, filterProc);
@@ -732,7 +690,7 @@ void stripExcludedDirectories(HierarchyObject& hierObj, const HardFilter& filter //remove superfluous directories:
// this does not invalidate "std::vector<FilePair*>& undefinedFiles", since we delete folders only
// and there is no side-effect for memory positions of FilePair and SymlinkPair thanks to zen::FixedList!
- static_assert(IsSameType<FixedList<FolderPair>, HierarchyObject::FolderList>::value, "");
+ static_assert(IsSameType<FixedList<FolderPair>, ContainerObject::FolderList>::value, "");
hierObj.refSubFolders().remove_if([&](FolderPair& folder)
{
@@ -900,17 +858,52 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings, auto basefolderExisting = [&](const AbstractPath& folderPath) { return resInfo.existingBaseFolders.find(folderPath) != resInfo.existingBaseFolders.end(); };
- //-----------execute basic checks all at once before starting comparison----------
-
- checkForIncompleteInput(resInfo.resolvedPairs, warnings.warningInputFieldEmpty, callback);
- checkFolderDependency (resInfo.resolvedPairs, warnings.warningDependentFolders, callback);
-
- //-------------------end of basic checks------------------------------------------
std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>> workLoad;
for (size_t i = 0; i < cfgList.size(); ++i)
workLoad.emplace_back(resInfo.resolvedPairs[i], cfgList[i]);
+ //-----------execute basic checks all at once before starting comparison----------
+
+ //check for incomplete input
+ {
+ bool havePartialPair = false;
+ bool haveFullPair = false;
+
+ for (const ResolvedFolderPair& fp : resInfo.resolvedPairs)
+ if (AFS::isNullPath(fp.folderPathLeft) != AFS::isNullPath(fp.folderPathRight))
+ havePartialPair = true;
+ else if (!AFS::isNullPath(fp.folderPathLeft))
+ haveFullPair = true;
+
+ if (havePartialPair == haveFullPair) //error if: all empty or exist both full and partial pairs -> support single-folder comparison scenario
+ callback.reportWarning(_("A folder input field is empty.") + L" \n\n" +
+ _("The corresponding folder will be considered as empty."), warnings.warnInputFieldEmpty);
+ }
+
+ //check whether one side is a sub directory of the other side (folder-pair-wise!)
+ //similar check (warnDependentBaseFolders) if one directory is read/written by multiple pairs not before beginning of synchronization
+ {
+ std::wstring msg;
+
+ for (const auto& w : workLoad)
+ if (Opt<PathDependency> pd = getPathDependency(w.first.folderPathLeft, *w.second.filter.nameFilter,
+ w.first.folderPathRight, *w.second.filter.nameFilter))
+ {
+ msg += L"\n\n" +
+ AFS::getDisplayPath(w.first.folderPathLeft) + L"\n" +
+ AFS::getDisplayPath(w.first.folderPathRight);
+ if (!pd->relPath.empty())
+ msg += L"\n" + _("Exclude:") + L" " + utfTo<std::wstring>(FILE_NAME_SEPARATOR + pd->relPath + FILE_NAME_SEPARATOR);
+ }
+
+ if (!msg.empty())
+ callback.reportWarning(_("One base folder of a folder pair is contained in the other one.") + L"\n" +
+ _("You may want to exclude it from synchronization via filter.") + msg, warnings.warnDependentFolderPair);
+ }
+
+ //-------------------end of basic checks------------------------------------------
+
//lock (existing) directories before comparison
if (createDirLocks)
{
@@ -919,7 +912,7 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings, if (Opt<Zstring> nativePath = AFS::getNativeItemPath(folderPath)) //restrict directory locking to native paths until further
dirPathsExisting.insert(*nativePath);
- dirLocks = std::make_unique<LockHolder>(dirPathsExisting, warnings.warningDirectoryLockFailed, callback);
+ dirLocks = std::make_unique<LockHolder>(dirPathsExisting, warnings.warnDirectoryLockFailed, callback);
}
try
@@ -990,7 +983,7 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings, callback.forceUiRefresh();
zen::redetermineSyncDirection(fpCfg.directionCfg, *it,
- [&](const std::wstring& msg) { callback.reportWarning(msg, warnings.warningDatabaseError); }, //throw X
+ [&](const std::wstring& msg) { callback.reportWarning(msg, warnings.warnDatabaseError); }, //throw X
[&](const std::wstring& msg) { callback.reportStatus(msg); }); //throw X
}
@@ -998,7 +991,7 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings, }
catch (const std::bad_alloc& e)
{
- callback.reportFatalError(_("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what()));
+ callback.reportFatalError(_("Out of memory.") + L" " + utfTo<std::wstring>(e.what()));
//we need to maintain the "output.size() == cfgList.size()" contract in ALL cases! => abort
callback.abortProcessNow(); //throw X
throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
diff --git a/FreeFileSync/Source/file_hierarchy.cpp b/FreeFileSync/Source/file_hierarchy.cpp index e3eed9c7..9640b201 100755 --- a/FreeFileSync/Source/file_hierarchy.cpp +++ b/FreeFileSync/Source/file_hierarchy.cpp @@ -14,12 +14,12 @@ using namespace zen; -void HierarchyObject::removeEmptyRec()
+void ContainerObject::removeEmptyRec()
{
bool emptyExisting = false;
auto isEmpty = [&](const FileSystemObject& fsObj) -> bool
{
- const bool objEmpty = fsObj.isEmpty();
+ const bool objEmpty = fsObj.isPairEmpty();
if (objEmpty)
emptyExisting = true;
return objEmpty;
@@ -30,7 +30,7 @@ void HierarchyObject::removeEmptyRec() refSubFolders().remove_if(isEmpty);
if (emptyExisting) //notify if actual deletion happened
- notifySyncCfgChanged(); //mustn't call this in ~FileSystemObject(), since parent, usually a FolderPair, may already be partially destroyed and existing as a pure HierarchyObject!
+ notifySyncCfgChanged(); //mustn't call this in ~FileSystemObject(), since parent, usually a FolderPair, may already be partially destroyed and existing as a pure ContainerObject!
for (FolderPair& folder : refSubFolders())
folder.removeEmptyRec(); //recurse
@@ -134,7 +134,7 @@ SyncOperation getIsolatedSyncOperation(bool itemExistsLeft, template <class Predicate> inline
-bool hasDirectChild(const HierarchyObject& hierObj, Predicate p)
+bool hasDirectChild(const ContainerObject& hierObj, Predicate p)
{
return std::any_of(hierObj.refSubFiles ().begin(), hierObj.refSubFiles ().end(), p) ||
std::any_of(hierObj.refSubLinks ().begin(), hierObj.refSubLinks ().end(), p) ||
@@ -164,16 +164,13 @@ SyncOperation FileSystemObject::getSyncOperation() const SyncOperation FolderPair::getSyncOperation() const
{
- if (!haveBufferedSyncOp_)
+ if (!syncOpBuffered_) //redetermine...
{
- haveBufferedSyncOp_ = true;
- //redetermine...
-
//suggested operation *not* considering child elements
syncOpBuffered_ = FileSystemObject::getSyncOperation();
//action for child elements may occassionally have to overwrite parent task:
- switch (syncOpBuffered_)
+ switch (*syncOpBuffered_)
{
case SO_MOVE_LEFT_SOURCE:
case SO_MOVE_LEFT_TARGET:
@@ -205,11 +202,11 @@ SyncOperation FolderPair::getSyncOperation() const }))
syncOpBuffered_ = SO_CREATE_NEW_LEFT;
//2. cancel parent deletion if only a single child is not also scheduled for deletion
- else if (syncOpBuffered_ == SO_DELETE_RIGHT &&
+ else if (*syncOpBuffered_ == SO_DELETE_RIGHT &&
hasDirectChild(*this,
[](const FileSystemObject& fsObj)
{
- if (fsObj.isEmpty())
+ if (fsObj.isPairEmpty())
return false; //fsObj may already be empty because it once contained a "move source"
const SyncOperation op = fsObj.getSyncOperation();
return op != SO_DELETE_RIGHT &&
@@ -227,11 +224,11 @@ SyncOperation FolderPair::getSyncOperation() const op == SO_MOVE_RIGHT_TARGET;
}))
syncOpBuffered_ = SO_CREATE_NEW_RIGHT;
- else if (syncOpBuffered_ == SO_DELETE_LEFT &&
+ else if (*syncOpBuffered_ == SO_DELETE_LEFT &&
hasDirectChild(*this,
[](const FileSystemObject& fsObj)
{
- if (fsObj.isEmpty())
+ if (fsObj.isPairEmpty())
return false;
const SyncOperation op = fsObj.getSyncOperation();
return op != SO_DELETE_LEFT &&
@@ -242,7 +239,7 @@ SyncOperation FolderPair::getSyncOperation() const break;
}
}
- return syncOpBuffered_;
+ return *syncOpBuffered_;
}
@@ -315,7 +312,7 @@ std::wstring zen::getCategoryDescription(CompareFilesResult cmpRes) std::wstring zen::getCategoryDescription(const FileSystemObject& fsObj)
{
- const std::wstring footer = L"\n[" + utfCvrtTo<std::wstring>(fsObj. getPairItemName()) + L"]";
+ const std::wstring footer = L"\n[" + utfTo<std::wstring>(fsObj. getPairItemName()) + L"]";
const CompareFilesResult cmpRes = fsObj.getCategory();
switch (cmpRes)
@@ -396,7 +393,7 @@ std::wstring zen::getSyncOpDescription(SyncOperation op) std::wstring zen::getSyncOpDescription(const FileSystemObject& fsObj)
{
- const std::wstring footer = L"\n[" + utfCvrtTo<std::wstring>(fsObj. getPairItemName()) + L"]";
+ const std::wstring footer = L"\n[" + utfTo<std::wstring>(fsObj. getPairItemName()) + L"]";
const SyncOperation op = fsObj.getSyncOperation();
switch (op)
@@ -431,8 +428,8 @@ std::wstring zen::getSyncOpDescription(const FileSystemObject& fsObj) case SO_MOVE_LEFT_TARGET:
case SO_MOVE_RIGHT_SOURCE:
case SO_MOVE_RIGHT_TARGET:
- if (const FilePair* sourceFile = dynamic_cast<const FilePair*>(&fsObj))
- if (const FilePair* targetFile = dynamic_cast<const FilePair*>(FileSystemObject::retrieve(sourceFile->getMoveRef())))
+ if (auto sourceFile = dynamic_cast<const FilePair*>(&fsObj))
+ if (auto targetFile = dynamic_cast<const FilePair*>(FileSystemObject::retrieve(sourceFile->getMoveRef())))
{
const bool onLeft = op == SO_MOVE_LEFT_SOURCE || op == SO_MOVE_LEFT_TARGET;
const bool isSource = op == SO_MOVE_LEFT_SOURCE || op == SO_MOVE_RIGHT_SOURCE;
@@ -442,8 +439,8 @@ std::wstring zen::getSyncOpDescription(const FileSystemObject& fsObj) auto getRelName = [&](const FileSystemObject& fso, bool leftSide) { return leftSide ? fso.getRelativePath<LEFT_SIDE>() : fso.getRelativePath<RIGHT_SIDE>(); };
- const Zstring relSource = getRelName(*sourceFile, onLeft);
- const Zstring relTarget = getRelName(*targetFile, !onLeft);
+ const Zstring relSource = getRelName(*sourceFile, onLeft);
+ const Zstring relTarget = getRelName(*targetFile, onLeft);
//attention: ::SetWindowText() doesn't handle tab characters correctly in combination with certain file names, so don't use them
return getSyncOpDescription(op) + L"\n" +
diff --git a/FreeFileSync/Source/file_hierarchy.h b/FreeFileSync/Source/file_hierarchy.h index 05982e20..547da2af 100755 --- a/FreeFileSync/Source/file_hierarchy.h +++ b/FreeFileSync/Source/file_hierarchy.h @@ -151,18 +151,46 @@ class FileSystemObject; /*------------------------------------------------------------------
inheritance diagram:
- ObjectMgr
- /|\
- |
- FileSystemObject HierarchyObject
- /|\ /|\
- _______________|_______________ ______|______
- | | | | |
- SymlinkPair FilePair FolderPair BaseFolderPair
+ ObjectMgr PathInformation
+ /|\ /|\
+ |________ _________|_________
+ || |
+ FileSystemObject ContainerObject
+ /|\ /|\
+ ___________|___________ ______|______
+ | | | | |
+ SymlinkPair FilePair FolderPair BaseFolderPair
------------------------------------------------------------------*/
-class HierarchyObject
+struct PathInformation //diamond-shaped inheritence!
+{
+ virtual ~PathInformation() {}
+
+ template <SelectedSide side> AbstractPath getAbstractPath() const;
+ template <SelectedSide side> Zstring getRelativePath() const; //get path relative to base sync dir (without leading/trailing FILE_NAME_SEPARATOR)
+
+ Zstring getPairRelativePath() const;
+
+private:
+ virtual AbstractPath getAbstractPathL() const = 0; //implemented by FileSystemObject + BaseFolderPair
+ virtual AbstractPath getAbstractPathR() const = 0; //
+
+ virtual Zstring getRelativePathL() const = 0; //implemented by SymlinkPair/FilePair + ContainerObject
+ virtual Zstring getRelativePathR() const = 0; //
+};
+
+template <> inline AbstractPath PathInformation::getAbstractPath<LEFT_SIDE >() const { return getAbstractPathL(); }
+template <> inline AbstractPath PathInformation::getAbstractPath<RIGHT_SIDE>() const { return getAbstractPathR(); }
+
+template <> inline Zstring PathInformation::getRelativePath<LEFT_SIDE >() const { return getRelativePathL(); }
+template <> inline Zstring PathInformation::getRelativePath<RIGHT_SIDE>() const { return getRelativePathR(); }
+
+inline Zstring PathInformation::getPairRelativePath() const { return getRelativePathL(); } //side doesn't matter
+
+//------------------------------------------------------------------
+
+class ContainerObject : public virtual PathInformation
{
friend class FolderPair;
friend class FileSystemObject;
@@ -172,30 +200,30 @@ public: using SymlinkList = FixedList<SymlinkPair>; //
using FolderList = FixedList<FolderPair>;
- FolderPair& addSubFolder(const Zstring& itemNameLeft,
- const FolderDescriptor& left, //file exists on both sides
- CompareDirResult defaultCmpResult,
- const Zstring& itemNameRight,
+ FolderPair& addSubFolder(const Zstring& itemNameL,
+ const FolderDescriptor& left, //file exists on both sides
+ CompareDirResult defaultCmpResult,
+ const Zstring& itemNameR,
const FolderDescriptor& right);
template <SelectedSide side>
FolderPair& addSubFolder(const Zstring& itemName, //dir exists on one side only
const FolderDescriptor& descr);
- FilePair& addSubFile(const Zstring& itemNameLeft,
+ FilePair& addSubFile(const Zstring& itemNameL,
const FileDescriptor& left, //file exists on both sides
CompareFilesResult defaultCmpResult,
- const Zstring& itemNameRight,
+ const Zstring& itemNameR,
const FileDescriptor& right);
template <SelectedSide side>
- FilePair& addSubFile(const Zstring& itemName, //file exists on one side only
- const FileDescriptor& descr);
+ FilePair& addSubFile(const Zstring& itemName, //file exists on one side only
+ const FileDescriptor& descr);
- SymlinkPair& addSubLink(const Zstring& itemNameLeft,
+ SymlinkPair& addSubLink(const Zstring& itemNameL,
const LinkDescriptor& left, //link exists on both sides
CompareSymlinkResult defaultCmpResult,
- const Zstring& itemNameRight,
+ const Zstring& itemNameR,
const LinkDescriptor& right);
template <SelectedSide side>
@@ -213,37 +241,43 @@ public: BaseFolderPair& getBase() { return base_; }
- const Zstring& getPairRelativePathPf() const { return pairRelPathPf_; } //postfixed or empty!
-
protected:
- HierarchyObject(const Zstring& relPathPf,
- BaseFolderPair& baseFolder) :
- pairRelPathPf_(relPathPf),
- base_(baseFolder) {}
+ ContainerObject(BaseFolderPair& baseFolder) : //used during BaseFolderPair constructor
+ base_(baseFolder) {} //take reference only: baseFolder *not yet* fully constructed at this point!
- virtual ~HierarchyObject() {} //don't need polymorphic deletion, but we have a vtable anyway
+ ContainerObject(const FileSystemObject& fsAlias); //used during FolderPair constructor
+
+ virtual ~ContainerObject() {} //don't need polymorphic deletion, but we have a vtable anyway
virtual void flip();
void removeEmptyRec();
+ template <SelectedSide side>
+ void updateRelPathsRecursion(const FileSystemObject& fsAlias);
+
private:
+ ContainerObject (const ContainerObject&) = delete; //this class is referenced by its child elements => make it non-copyable/movable!
+ ContainerObject& operator=(const ContainerObject&) = delete;
+
virtual void notifySyncCfgChanged() {}
- HierarchyObject (const HierarchyObject&) = delete; //this class is referenced by it's child elements => make it non-copyable/movable!
- HierarchyObject& operator=(const HierarchyObject&) = delete;
+ Zstring getRelativePathL() const override { return relPathL_; }
+ Zstring getRelativePathR() const override { return relPathR_; }
- FileList subFiles_; //contained file maps
- SymlinkList subLinks_; //contained symbolic link maps
- FolderList subFolders_; //contained directory maps
+ FileList subFiles_;
+ SymlinkList subLinks_;
+ FolderList subFolders_;
+
+ Zstring relPathL_; //path relative to base sync dir (without leading/trailing FILE_NAME_SEPARATOR)
+ Zstring relPathR_; //
- Zstring pairRelPathPf_; //postfixed or empty
BaseFolderPair& base_;
};
//------------------------------------------------------------------
-class BaseFolderPair : public HierarchyObject //synchronization base directory
+class BaseFolderPair : public ContainerObject //synchronization base directory
{
public:
BaseFolderPair(const AbstractPath& folderPathLeft,
@@ -254,15 +288,13 @@ public: CompareVariant cmpVar,
int fileTimeTolerance,
const std::vector<unsigned int>& ignoreTimeShiftMinutes) :
- HierarchyObject(Zstring(), *this),
+ ContainerObject(*this), //trust that ContainerObject knows that *this is not yet fully constructed!
filter_(filter), cmpVar_(cmpVar), fileTimeTolerance_(fileTimeTolerance), ignoreTimeShiftMinutes_(ignoreTimeShiftMinutes),
folderAvailableLeft_ (folderAvailableLeft),
folderAvailableRight_(folderAvailableRight),
folderPathLeft_(folderPathLeft),
folderPathRight_(folderPathRight) {}
- template <SelectedSide side> const AbstractPath& getAbstractPath() const;
-
static void removeEmpty(BaseFolderPair& baseFolder) { baseFolder.removeEmptyRec(); } //physically remove all invalid entries (where both sides are empty) recursively
template <SelectedSide side> bool isAvailable() const; //base folder status at the time of comparison!
@@ -277,6 +309,9 @@ public: void flip() override;
private:
+ AbstractPath getAbstractPathL() const override { return folderPathLeft_; }
+ AbstractPath getAbstractPathR() const override { return folderPathRight_; }
+
const HardFilter::FilterRef filter_; //filter used while scanning directory: represents sub-view of actual files!
const CompareVariant cmpVar_;
const int fileTimeTolerance_;
@@ -290,13 +325,6 @@ private: };
-template <> inline
-const AbstractPath& BaseFolderPair::getAbstractPath<LEFT_SIDE>() const { return folderPathLeft_; }
-
-template <> inline
-const AbstractPath& BaseFolderPair::getAbstractPath<RIGHT_SIDE>() const { return folderPathRight_; }
-
-
//get rid of shared_ptr indirection
template <class IterImpl, //underlying iterator type
class V> //target value type
@@ -374,19 +402,17 @@ private: //------------------------------------------------------------------
-class FileSystemObject : public ObjectMgr<FileSystemObject>
+class FileSystemObject : public ObjectMgr<FileSystemObject>, public virtual PathInformation
{
public:
virtual void accept(FSObjectVisitor& visitor) const = 0;
- Zstring getPairItemName () const; //like getItemName() but also returns value if either side is empty
- 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 path relative to base sync dir without FILE_NAME_SEPARATOR prefix
+ Zstring getPairItemName () const; //like getItemName() but without bias to which side is returned
+ bool isPairEmpty() const; //true, if both sides are empty
-public:
- template <SelectedSide side> AbstractPath getAbstractPath() const; //precondition: !isEmpty<side>()
+ //path getters always return valid values, even if isEmpty<side>()!
+ template <SelectedSide side> const Zstring& getItemName() const; //case sensitive!
+ template <SelectedSide side> bool isEmpty() const;
//comparison result
CompareFilesResult getCategory() const { return cmpResult_; }
@@ -407,10 +433,8 @@ public: template <SelectedSide side> void removeObject(); //removes file or directory (recursively!) without physically removing the element: used by manual deletion
- bool isEmpty() const; //true, if both sides are empty
-
- const HierarchyObject& parent() const { return parent_; }
- /**/ HierarchyObject& parent() { return parent_; }
+ const ContainerObject& parent() const { return parent_; }
+ /**/ ContainerObject& parent() { return parent_; }
const BaseFolderPair& base() const { return parent_.getBase(); }
/**/ BaseFolderPair& base() { return parent_.getBase(); }
@@ -420,21 +444,21 @@ public: void setCategoryDiffMetadata(const std::wstring& description);
protected:
- FileSystemObject(const Zstring& itemNameLeft,
- const Zstring& itemNameRight,
- HierarchyObject& parentObj,
+ FileSystemObject(const Zstring& itemNameL,
+ const Zstring& itemNameR,
+ ContainerObject& parent,
CompareFilesResult defaultCmpResult) :
cmpResult_(defaultCmpResult),
- itemNameLeft_(itemNameLeft),
- itemNameRight_(itemNameRight),
- //itemNameRight_(itemNameRight == itemNameLeft ? itemNameLeft : itemNameRight), -> strangely doesn't seem to shrink peak memory consumption at all!
- parent_(parentObj)
+ itemNameL_(itemNameL),
+ itemNameR_(itemNameL == itemNameR ? itemNameL : itemNameR), //perf: no measurable speed drawback; -3% peak memory => further needed by ContainerObject construction!
+ parent_(parent)
{
+ assert(itemNameL_.c_str() == itemNameR_.c_str() || itemNameL_ != itemNameR_); //also checks ref-counted string precondition
parent_.notifySyncCfgChanged();
}
virtual ~FileSystemObject() {} //don't need polymorphic deletion, but we have a vtable anyway
- //mustn't call parent here, it is already partially destroyed and nothing more than a pure HierarchyObject!
+ //must not call parent here, it is already partially destroyed and nothing more than a pure ContainerObject!
virtual void flip();
virtual void notifySyncCfgChanged() { parent().notifySyncCfgChanged(); /*propagate!*/ }
@@ -445,9 +469,15 @@ private: FileSystemObject (const FileSystemObject&) = delete;
FileSystemObject& operator=(const FileSystemObject&) = delete;
+ AbstractPath getAbstractPathL() const override { return AFS::appendRelPath(base().getAbstractPath<LEFT_SIDE >(), getRelativePath<LEFT_SIDE >()); }
+ AbstractPath getAbstractPathR() const override { return AFS::appendRelPath(base().getAbstractPath<RIGHT_SIDE>(), getRelativePath<RIGHT_SIDE>()); }
+
virtual void removeObjectL() = 0;
virtual void removeObjectR() = 0;
+ template <SelectedSide side>
+ void propagateChangedItemName(const Zstring& itemNameOld); //required after any itemName changes
+
//categorization
std::unique_ptr<std::wstring> 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!
@@ -459,33 +489,34 @@ private: std::unique_ptr<std::wstring> syncDirectionConflict_; //non-empty if we have a conflict setting sync-direction
//get rid of std::wstring small string optimization (consumes 32/48 byte on VS2010 x86/x64!)
- Zstring itemNameLeft_; //slightly redundant under linux, but on windows the "same" filepaths can differ in case
- Zstring itemNameRight_; //use as indicator: an empty name means: not existing!
+ Zstring itemNameL_; //slightly redundant under linux, but on windows the "same" file paths can differ in case
+ Zstring itemNameR_; //use as indicator: an empty name means: not existing on this side!
- HierarchyObject& parent_;
+ ContainerObject& parent_;
};
//------------------------------------------------------------------
-class FolderPair : public FileSystemObject, public HierarchyObject
+
+class FolderPair : public FileSystemObject, public ContainerObject
{
- friend class HierarchyObject;
+ friend class ContainerObject;
public:
void accept(FSObjectVisitor& visitor) const override;
CompareDirResult getDirCategory() const; //returns actually used subset of CompareFilesResult
- FolderPair(const Zstring& itemNameLeft, //use empty itemName if "not existing"
- const FolderDescriptor& left,
+ FolderPair(const Zstring& itemNameL, //use empty itemName if "not existing"
+ const FolderDescriptor& descrL,
CompareDirResult defaultCmpResult,
- const Zstring& itemNameRight,
- const FolderDescriptor& right,
- HierarchyObject& parentObj) :
- FileSystemObject(itemNameLeft, itemNameRight, parentObj, static_cast<CompareFilesResult>(defaultCmpResult)),
- HierarchyObject(getPairRelativePath() + FILE_NAME_SEPARATOR, parentObj.getBase()),
- dataLeft_(left),
- dataRight_(right) {}
+ const Zstring& itemNameR,
+ const FolderDescriptor& descrR,
+ ContainerObject& parent) :
+ FileSystemObject(itemNameL, itemNameR, parent, static_cast<CompareFilesResult>(defaultCmpResult)),
+ ContainerObject(static_cast<FileSystemObject&>(*this)), //FileSystemObject fully constructed at this point!
+ descrL_(descrL),
+ descrR_(descrR) {}
template <SelectedSide side> bool isFollowedSymlink() const;
@@ -498,33 +529,33 @@ private: void flip () override;
void removeObjectL() override;
void removeObjectR() override;
- void notifySyncCfgChanged() override { haveBufferedSyncOp_ = false; FileSystemObject::notifySyncCfgChanged(); HierarchyObject::notifySyncCfgChanged(); }
+ void notifySyncCfgChanged() override { syncOpBuffered_ = NoValue(); FileSystemObject::notifySyncCfgChanged(); ContainerObject::notifySyncCfgChanged(); }
- mutable SyncOperation syncOpBuffered_ = SO_DO_NOTHING; //determining sync-op for directory may be expensive as it depends on child-objects -> buffer it
- mutable bool haveBufferedSyncOp_ = false; //
+ mutable Opt<SyncOperation> syncOpBuffered_; //determining sync-op for directory may be expensive as it depends on child-objects => buffer
- FolderDescriptor dataLeft_;
- FolderDescriptor dataRight_;
+ FolderDescriptor descrL_;
+ FolderDescriptor descrR_;
};
+
//------------------------------------------------------------------
class FilePair : public FileSystemObject
{
- friend class HierarchyObject; //construction
+ friend class ContainerObject; //construction
public:
void accept(FSObjectVisitor& visitor) const override;
- FilePair(const Zstring& itemNameLeft, //use empty string if "not existing"
- const FileDescriptor& left,
+ FilePair(const Zstring& itemNameL, //use empty string if "not existing"
+ const FileDescriptor& descrL,
CompareFilesResult defaultCmpResult,
- const Zstring& itemNameRight, //
- const FileDescriptor& right,
- HierarchyObject& parentObj) :
- FileSystemObject(itemNameLeft, itemNameRight, parentObj, defaultCmpResult),
- dataLeft_(left),
- dataRight_(right) {}
+ const Zstring& itemNameR, //
+ const FileDescriptor& descrR,
+ ContainerObject& parent) :
+ FileSystemObject(itemNameL, itemNameR, parent, defaultCmpResult),
+ descrL_(descrL),
+ descrR_(descrR) {}
template <SelectedSide side> int64_t getLastWriteTime() const;
template <SelectedSide side> uint64_t getFileSize() const;
@@ -550,14 +581,17 @@ public: bool isSymlinkSrc);
private:
+ Zstring getRelativePathL() const override { return AFS::appendPaths(parent().getRelativePath<LEFT_SIDE >(), getItemName<LEFT_SIDE >(), FILE_NAME_SEPARATOR); }
+ Zstring getRelativePathR() const override { return AFS::appendPaths(parent().getRelativePath<RIGHT_SIDE>(), getItemName<RIGHT_SIDE>(), FILE_NAME_SEPARATOR); }
+
SyncOperation applyMoveOptimization(SyncOperation op) const;
void flip () override;
- void removeObjectL() override { dataLeft_ = FileDescriptor(); }
- void removeObjectR() override { dataRight_ = FileDescriptor(); }
+ void removeObjectL() override { descrL_ = FileDescriptor(); }
+ void removeObjectR() override { descrR_ = FileDescriptor(); }
- FileDescriptor dataLeft_;
- FileDescriptor dataRight_;
+ FileDescriptor descrL_;
+ FileDescriptor descrR_;
ObjectId moveFileRef_ = nullptr; //optional, filled by redetermineSyncDirection()
};
@@ -566,7 +600,7 @@ private: class SymlinkPair : public FileSystemObject //this class models a TRUE symbolic link, i.e. one that is NEVER dereferenced: deref-links should be directly placed in class File/FolderPair
{
- friend class HierarchyObject; //construction
+ friend class ContainerObject; //construction
public:
void accept(FSObjectVisitor& visitor) const override;
@@ -575,15 +609,15 @@ public: CompareSymlinkResult getLinkCategory() const; //returns actually used subset of CompareFilesResult
- SymlinkPair(const Zstring& itemNameLeft, //use empty string if "not existing"
- const LinkDescriptor& left,
+ SymlinkPair(const Zstring& itemNameL, //use empty string if "not existing"
+ const LinkDescriptor& descrL,
CompareSymlinkResult defaultCmpResult,
- const Zstring& itemNameRight, //use empty string if "not existing"
- const LinkDescriptor& right,
- HierarchyObject& parentObj) :
- FileSystemObject(itemNameLeft, itemNameRight, parentObj, static_cast<CompareFilesResult>(defaultCmpResult)),
- dataLeft_(left),
- dataRight_(right) {}
+ const Zstring& itemNameR, //use empty string if "not existing"
+ const LinkDescriptor& descrR,
+ ContainerObject& parent) :
+ FileSystemObject(itemNameL, itemNameR, parent, static_cast<CompareFilesResult>(defaultCmpResult)),
+ descrL_(descrL),
+ descrR_(descrR) {}
template <SelectedSide sideTrg>
void setSyncedTo(const Zstring& itemName, //call after sync, sets SYMLINK_EQUAL
@@ -591,12 +625,15 @@ public: int64_t lastWriteTimeSrc);
private:
+ Zstring getRelativePathL() const override { return AFS::appendPaths(parent().getRelativePath<LEFT_SIDE >(), getItemName<LEFT_SIDE >(), FILE_NAME_SEPARATOR); }
+ Zstring getRelativePathR() const override { return AFS::appendPaths(parent().getRelativePath<RIGHT_SIDE>(), getItemName<RIGHT_SIDE>(), FILE_NAME_SEPARATOR); }
+
void flip() override;
- void removeObjectL() override { dataLeft_ = LinkDescriptor(); }
- void removeObjectR() override { dataRight_ = LinkDescriptor(); }
+ void removeObjectL() override { descrL_ = LinkDescriptor(); }
+ void removeObjectR() override { descrR_ = LinkDescriptor(); }
- LinkDescriptor dataLeft_;
- LinkDescriptor dataRight_;
+ LinkDescriptor descrL_;
+ LinkDescriptor descrR_;
};
//------------------------------------------------------------------
@@ -726,12 +763,12 @@ void FileSystemObject::setActive(bool active) template <SelectedSide side> inline
bool FileSystemObject::isEmpty() const
{
- return SelectParam<side>::ref(itemNameLeft_, itemNameRight_).empty();
+ return SelectParam<side>::ref(itemNameL_, itemNameR_).empty();
}
inline
-bool FileSystemObject::isEmpty() const
+bool FileSystemObject::isPairEmpty() const
{
return isEmpty<LEFT_SIDE>() && isEmpty<RIGHT_SIDE>();
}
@@ -740,72 +777,63 @@ bool FileSystemObject::isEmpty() const template <SelectedSide side> inline
const Zstring& FileSystemObject::getItemName() const
{
- return SelectParam<side>::ref(itemNameLeft_, itemNameRight_); //empty if not existing
-}
-
+ assert(!itemNameL_.empty() || !itemNameR_.empty());
-template <SelectedSide side> inline
-Zstring FileSystemObject::getRelativePath() const
-{
- if (isEmpty<side>()) //avoid ternary-WTF! (implicit copy-constructor call!!!!!!)
- return Zstring();
- return parent_.getPairRelativePathPf() + getItemName<side>();
-}
-
-
-inline
-Zstring FileSystemObject::getPairRelativePath() const
-{
- return parent_.getPairRelativePathPf() + getPairItemName();
+ const Zstring& itemName = SelectParam<side>::ref(itemNameL_, itemNameR_); //empty if not existing
+ if (!itemName.empty()) //avoid ternary-WTF! (implicit copy-constructor call!!!!!!)
+ return itemName;
+ return SelectParam<OtherSide<side>::result>::ref(itemNameL_, itemNameR_); //empty if not existing
}
inline
Zstring FileSystemObject::getPairItemName() const
{
- assert(!isEmpty<LEFT_SIDE>() || !isEmpty<RIGHT_SIDE>());
- return isEmpty<LEFT_SIDE>() ? getItemName<RIGHT_SIDE>() : getItemName<LEFT_SIDE>();
-}
-
-
-template <SelectedSide side> inline
-AbstractPath FileSystemObject::getAbstractPath() const
-{
- assert(!isEmpty<side>());
- const Zstring& itemName = isEmpty<side>() ? getItemName<OtherSide<side>::result>() : getItemName<side>();
- return AFS::appendRelPath(base().getAbstractPath<side>(), parent_.getPairRelativePathPf() + itemName);
+ return getItemName<LEFT_SIDE>(); //side doesn't matter
}
template <> inline
void FileSystemObject::removeObject<LEFT_SIDE>()
{
+ const Zstring itemNameOld = getItemName<LEFT_SIDE>();
+
cmpResult_ = isEmpty<RIGHT_SIDE>() ? FILE_EQUAL : FILE_RIGHT_SIDE_ONLY;
- itemNameLeft_.clear();
+ itemNameL_.clear();
removeObjectL();
setSyncDir(SyncDirection::NONE); //calls notifySyncCfgChanged()
+ propagateChangedItemName<LEFT_SIDE>(itemNameOld);
}
template <> inline
void FileSystemObject::removeObject<RIGHT_SIDE>()
{
+ const Zstring itemNameOld = getItemName<RIGHT_SIDE>();
+
cmpResult_ = isEmpty<LEFT_SIDE>() ? FILE_EQUAL : FILE_LEFT_SIDE_ONLY;
- itemNameRight_.clear();
+ itemNameR_.clear();
removeObjectR();
setSyncDir(SyncDirection::NONE); //calls notifySyncCfgChanged()
+ propagateChangedItemName<RIGHT_SIDE>(itemNameOld);
}
inline
void FileSystemObject::setSynced(const Zstring& itemName)
{
- assert(!isEmpty());
- itemNameRight_ = itemNameLeft_ = itemName;
+ const Zstring itemNameOldL = getItemName<LEFT_SIDE>();
+ const Zstring itemNameOldR = getItemName<RIGHT_SIDE>();
+
+ assert(!isPairEmpty());
+ itemNameR_ = itemNameL_ = itemName;
cmpResult_ = FILE_EQUAL;
setSyncDir(SyncDirection::NONE);
+
+ propagateChangedItemName<LEFT_SIDE >(itemNameOldL);
+ propagateChangedItemName<RIGHT_SIDE>(itemNameOldR);
}
@@ -836,7 +864,7 @@ void FileSystemObject::setCategoryDiffMetadata(const std::wstring& description) inline
void FileSystemObject::flip()
{
- std::swap(itemNameLeft_, itemNameRight_);
+ std::swap(itemNameL_, itemNameR_);
switch (cmpResult_)
{
@@ -863,8 +891,48 @@ void FileSystemObject::flip() }
+template <SelectedSide side> inline
+void FileSystemObject::propagateChangedItemName(const Zstring& itemNameOld)
+{
+ if (itemNameL_.empty() && itemNameR_.empty()) return; //both sides might just have been deleted by removeObject<>
+
+ if (itemNameOld != getItemName<side>()) //perf: premature optimization?
+ if (auto hierObj = dynamic_cast<ContainerObject*>(this))
+ hierObj->updateRelPathsRecursion<side>(*this);
+}
+
+
+template <SelectedSide side> inline
+void ContainerObject::updateRelPathsRecursion(const FileSystemObject& fsAlias)
+{
+ assert(SelectParam<side>::ref(relPathL_, relPathR_) != //perf: only call if actual item name changed!
+ AFS::appendPaths(fsAlias.parent().getRelativePath<side>(), fsAlias.getItemName<side>(), FILE_NAME_SEPARATOR));
+
+ SelectParam<side>::ref(relPathL_, relPathR_) = AFS::appendPaths(fsAlias.parent().getRelativePath<side>(), fsAlias.getItemName<side>(), FILE_NAME_SEPARATOR);
+
+ for (FolderPair& folder : subFolders_)
+ folder.updateRelPathsRecursion<side>(folder);
+}
+
+
inline
-void HierarchyObject::flip()
+ContainerObject::ContainerObject(const FileSystemObject& fsAlias) :
+ relPathL_(AFS::appendPaths(fsAlias.parent().relPathL_, fsAlias.getItemName<LEFT_SIDE>(), FILE_NAME_SEPARATOR)),
+ relPathR_(
+ fsAlias.parent().relPathL_.c_str() == //
+ fsAlias.parent().relPathR_.c_str() && //take advantage of FileSystemObject's Zstring reuse:
+ fsAlias.getItemName<LEFT_SIDE >().c_str() == //=> perf: 12% faster merge phase; -4% peak memory
+ fsAlias.getItemName<RIGHT_SIDE>().c_str() ? //
+ relPathL_ : //ternary-WTF! (implicit copy-constructor call!!) => no big deal for a Zstring
+ AFS::appendPaths(fsAlias.parent().relPathR_, fsAlias.getItemName<RIGHT_SIDE>(), FILE_NAME_SEPARATOR)),
+ base_(fsAlias.parent().base_)
+{
+ assert(relPathL_.c_str() == relPathR_.c_str() || relPathL_ != relPathR_);
+}
+
+
+inline
+void ContainerObject::flip()
{
for (FilePair& file : refSubFiles())
file.flip();
@@ -872,23 +940,25 @@ void HierarchyObject::flip() link.flip();
for (FolderPair& folder : refSubFolders())
folder.flip();
+
+ std::swap(relPathL_, relPathR_);
}
inline
-FolderPair& HierarchyObject::addSubFolder(const Zstring& itemNameLeft,
+FolderPair& ContainerObject::addSubFolder(const Zstring& itemNameL,
const FolderDescriptor& left,
CompareDirResult defaultCmpResult,
- const Zstring& itemNameRight,
+ const Zstring& itemNameR,
const FolderDescriptor& right)
{
- subFolders_.emplace_back(itemNameLeft, left, defaultCmpResult, itemNameRight, right, *this);
+ subFolders_.emplace_back(itemNameL, left, defaultCmpResult, itemNameR, right, *this);
return subFolders_.back();
}
template <> inline
-FolderPair& HierarchyObject::addSubFolder<LEFT_SIDE>(const Zstring& itemName, const FolderDescriptor& descr)
+FolderPair& ContainerObject::addSubFolder<LEFT_SIDE>(const Zstring& itemName, const FolderDescriptor& descr)
{
subFolders_.emplace_back(itemName, descr, DIR_LEFT_SIDE_ONLY, Zstring(), FolderDescriptor(), *this);
return subFolders_.back();
@@ -896,7 +966,7 @@ FolderPair& HierarchyObject::addSubFolder<LEFT_SIDE>(const Zstring& itemName, co template <> inline
-FolderPair& HierarchyObject::addSubFolder<RIGHT_SIDE>(const Zstring& itemName, const FolderDescriptor& descr)
+FolderPair& ContainerObject::addSubFolder<RIGHT_SIDE>(const Zstring& itemName, const FolderDescriptor& descr)
{
subFolders_.emplace_back(Zstring(), FolderDescriptor(), DIR_RIGHT_SIDE_ONLY, itemName, descr, *this);
return subFolders_.back();
@@ -904,19 +974,19 @@ FolderPair& HierarchyObject::addSubFolder<RIGHT_SIDE>(const Zstring& itemName, c inline
-FilePair& HierarchyObject::addSubFile(const Zstring& itemNameLeft,
+FilePair& ContainerObject::addSubFile(const Zstring& itemNameL,
const FileDescriptor& left, //file exists on both sides
CompareFilesResult defaultCmpResult,
- const Zstring& itemNameRight,
+ const Zstring& itemNameR,
const FileDescriptor& right)
{
- subFiles_.emplace_back(itemNameLeft, left, defaultCmpResult, itemNameRight, right, *this);
+ subFiles_.emplace_back(itemNameL, left, defaultCmpResult, itemNameR, right, *this);
return subFiles_.back();
}
template <> inline
-FilePair& HierarchyObject::addSubFile<LEFT_SIDE>(const Zstring& itemName, const FileDescriptor& descr)
+FilePair& ContainerObject::addSubFile<LEFT_SIDE>(const Zstring& itemName, const FileDescriptor& descr)
{
subFiles_.emplace_back(itemName, descr, FILE_LEFT_SIDE_ONLY, Zstring(), FileDescriptor(), *this);
return subFiles_.back();
@@ -924,7 +994,7 @@ FilePair& HierarchyObject::addSubFile<LEFT_SIDE>(const Zstring& itemName, const template <> inline
-FilePair& HierarchyObject::addSubFile<RIGHT_SIDE>(const Zstring& itemName, const FileDescriptor& descr)
+FilePair& ContainerObject::addSubFile<RIGHT_SIDE>(const Zstring& itemName, const FileDescriptor& descr)
{
subFiles_.emplace_back(Zstring(), FileDescriptor(), FILE_RIGHT_SIDE_ONLY, itemName, descr, *this);
return subFiles_.back();
@@ -932,19 +1002,19 @@ FilePair& HierarchyObject::addSubFile<RIGHT_SIDE>(const Zstring& itemName, const inline
-SymlinkPair& HierarchyObject::addSubLink(const Zstring& itemNameLeft,
+SymlinkPair& ContainerObject::addSubLink(const Zstring& itemNameL,
const LinkDescriptor& left, //link exists on both sides
CompareSymlinkResult defaultCmpResult,
- const Zstring& itemNameRight,
+ const Zstring& itemNameR,
const LinkDescriptor& right)
{
- subLinks_.emplace_back(itemNameLeft, left, defaultCmpResult, itemNameRight, right, *this);
+ subLinks_.emplace_back(itemNameL, left, defaultCmpResult, itemNameR, right, *this);
return subLinks_.back();
}
template <> inline
-SymlinkPair& HierarchyObject::addSubLink<LEFT_SIDE>(const Zstring& itemName, const LinkDescriptor& descr)
+SymlinkPair& ContainerObject::addSubLink<LEFT_SIDE>(const Zstring& itemName, const LinkDescriptor& descr)
{
subLinks_.emplace_back(itemName, descr, SYMLINK_LEFT_SIDE_ONLY, Zstring(), LinkDescriptor(), *this);
return subLinks_.back();
@@ -952,7 +1022,7 @@ SymlinkPair& HierarchyObject::addSubLink<LEFT_SIDE>(const Zstring& itemName, con template <> inline
-SymlinkPair& HierarchyObject::addSubLink<RIGHT_SIDE>(const Zstring& itemName, const LinkDescriptor& descr)
+SymlinkPair& ContainerObject::addSubLink<RIGHT_SIDE>(const Zstring& itemName, const LinkDescriptor& descr)
{
subLinks_.emplace_back(Zstring(), LinkDescriptor(), SYMLINK_RIGHT_SIDE_ONLY, itemName, descr, *this);
return subLinks_.back();
@@ -962,7 +1032,7 @@ SymlinkPair& HierarchyObject::addSubLink<RIGHT_SIDE>(const Zstring& itemName, co inline
void BaseFolderPair::flip()
{
- HierarchyObject::flip();
+ ContainerObject::flip();
std::swap(folderAvailableLeft_, folderAvailableRight_);
std::swap(folderPathLeft_, folderPathRight_);
}
@@ -971,9 +1041,9 @@ void BaseFolderPair::flip() inline
void FolderPair::flip()
{
- HierarchyObject ::flip(); //call base class versions
+ ContainerObject ::flip(); //call base class versions
FileSystemObject::flip(); //
- std::swap(dataLeft_, dataRight_);
+ std::swap(descrL_, descrR_);
}
@@ -987,7 +1057,7 @@ void FolderPair::removeObjectL() for (FolderPair& folder : refSubFolders())
folder.removeObject<LEFT_SIDE>();
- dataLeft_ = FolderDescriptor();
+ descrL_ = FolderDescriptor();
}
@@ -1001,7 +1071,7 @@ void FolderPair::removeObjectR() for (FolderPair& folder : refSubFolders())
folder.removeObject<RIGHT_SIDE>();
- dataRight_ = FolderDescriptor();
+ descrR_ = FolderDescriptor();
}
@@ -1023,42 +1093,42 @@ inline void FilePair::flip()
{
FileSystemObject::flip(); //call base class version
- std::swap(dataLeft_, dataRight_);
+ std::swap(descrL_, descrR_);
}
template <SelectedSide side> inline
int64_t FilePair::getLastWriteTime() const
{
- return SelectParam<side>::ref(dataLeft_, dataRight_).lastWriteTimeRaw;
+ return SelectParam<side>::ref(descrL_, descrR_).lastWriteTimeRaw;
}
template <SelectedSide side> inline
uint64_t FilePair::getFileSize() const
{
- return SelectParam<side>::ref(dataLeft_, dataRight_).fileSize;
+ return SelectParam<side>::ref(descrL_, descrR_).fileSize;
}
template <SelectedSide side> inline
AFS::FileId FilePair::getFileId() const
{
- return SelectParam<side>::ref(dataLeft_, dataRight_).fileId;
+ return SelectParam<side>::ref(descrL_, descrR_).fileId;
}
template <SelectedSide side> inline
bool FilePair::isFollowedSymlink() const
{
- return SelectParam<side>::ref(dataLeft_, dataRight_).isFollowedSymlink;
+ return SelectParam<side>::ref(descrL_, descrR_).isFollowedSymlink;
}
template <SelectedSide side> inline
bool FolderPair::isFollowedSymlink() const
{
- return SelectParam<side>::ref(dataLeft_, dataRight_).isFollowedSymlink;
+ return SelectParam<side>::ref(descrL_, descrR_).isFollowedSymlink;
}
@@ -1075,8 +1145,8 @@ void FilePair::setSyncedTo(const Zstring& itemName, //FILE_EQUAL is only allowed for same short name and file size: enforced by this method!
static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
- SelectParam<sideTrg>::ref(dataLeft_, dataRight_) = FileDescriptor(lastWriteTimeTrg, fileSize, fileIdTrg, isSymlinkTrg);
- SelectParam<sideSrc>::ref(dataLeft_, dataRight_) = FileDescriptor(lastWriteTimeSrc, fileSize, fileIdSrc, isSymlinkSrc);
+ SelectParam<sideTrg>::ref(descrL_, descrR_) = FileDescriptor(lastWriteTimeTrg, fileSize, fileIdTrg, isSymlinkTrg);
+ SelectParam<sideSrc>::ref(descrL_, descrR_) = FileDescriptor(lastWriteTimeSrc, fileSize, fileIdSrc, isSymlinkSrc);
moveFileRef_ = nullptr;
FileSystemObject::setSynced(itemName); //set FileSystemObject specific part
@@ -1090,8 +1160,8 @@ void SymlinkPair::setSyncedTo(const Zstring& itemName, {
static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
- SelectParam<sideTrg>::ref(dataLeft_, dataRight_) = LinkDescriptor(lastWriteTimeTrg);
- SelectParam<sideSrc>::ref(dataLeft_, dataRight_) = LinkDescriptor(lastWriteTimeSrc);
+ SelectParam<sideTrg>::ref(descrL_, descrR_) = LinkDescriptor(lastWriteTimeTrg);
+ SelectParam<sideSrc>::ref(descrL_, descrR_) = LinkDescriptor(lastWriteTimeSrc);
FileSystemObject::setSynced(itemName); //set FileSystemObject specific part
}
@@ -1104,8 +1174,8 @@ void FolderPair::setSyncedTo(const Zstring& itemName, {
static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
- SelectParam<sideTrg>::ref(dataLeft_, dataRight_) = FolderDescriptor(isSymlinkTrg);
- SelectParam<sideSrc>::ref(dataLeft_, dataRight_) = FolderDescriptor(isSymlinkSrc);
+ SelectParam<sideTrg>::ref(descrL_, descrR_) = FolderDescriptor(isSymlinkTrg);
+ SelectParam<sideSrc>::ref(descrL_, descrR_) = FolderDescriptor(isSymlinkSrc);
FileSystemObject::setSynced(itemName); //set FileSystemObject specific part
}
@@ -1114,7 +1184,7 @@ void FolderPair::setSyncedTo(const Zstring& itemName, template <SelectedSide side> inline
int64_t SymlinkPair::getLastWriteTime() const
{
- return SelectParam<side>::ref(dataLeft_, dataRight_).lastWriteTimeRaw;
+ return SelectParam<side>::ref(descrL_, descrR_).lastWriteTimeRaw;
}
@@ -1129,7 +1199,7 @@ inline void SymlinkPair::flip()
{
FileSystemObject::flip(); //call base class versions
- std::swap(dataLeft_, dataRight_);
+ std::swap(descrL_, descrR_);
}
}
diff --git a/FreeFileSync/Source/fs/abstract.cpp b/FreeFileSync/Source/fs/abstract.cpp index bfebf24f..3ecacd1a 100755 --- a/FreeFileSync/Source/fs/abstract.cpp +++ b/FreeFileSync/Source/fs/abstract.cpp @@ -36,43 +36,23 @@ int AFS::compareAbstractPath(const AbstractPath& lhs, const AbstractPath& rhs) if (rv != 0)
return rv;
- return cmpFilePath(lhs.afsPath.value.c_str(), lhs.afsPath.value.size(),
- rhs.afsPath.value.c_str(), rhs.afsPath.value.size());
+ return CmpFilePath()(lhs.afsPath.value.c_str(), lhs.afsPath.value.size(),
+ rhs.afsPath.value.c_str(), rhs.afsPath.value.size());
}
-Opt<AFS::PathComplement> AFS::getPathComplement(const AbstractPath& lhs, const AbstractPath& rhs)
+AFS::PathComponents AFS::getPathComponents(const AbstractPath& ap)
{
- if (typeid(*lhs.afs) != typeid(*rhs.afs))
- return NoValue();
-
- const int rv = lhs.afs->compareDeviceRootSameAfsType(*rhs.afs);
- if (rv != 0)
- return NoValue();
-
- const Zstring& lhsPf = appendSeparator(lhs.afsPath.value);
- const Zstring& rhsPf = appendSeparator(rhs.afsPath.value);
-
- const size_t lenMin = std::min(lhsPf.length(), rhsPf.length());
-
- if (cmpFilePath(lhsPf.c_str(), lenMin,
- rhsPf.c_str(), lenMin) != 0)
- return NoValue();
-
- Zstring relPathL(lhsPf.begin() + lenMin, lhsPf.end());
- Zstring relPathR(rhsPf.begin() + lenMin, rhsPf.end());
-
- if (endsWith(relPathL, FILE_NAME_SEPARATOR)) relPathL.pop_back();
- if (endsWith(relPathR, FILE_NAME_SEPARATOR)) relPathR.pop_back();
-
- return PathComplement({ relPathL, relPathR });
+ return { AbstractPath(ap.afs, AfsPath(Zstring())), split(ap.afsPath.value, FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY) };
}
-bool AFS::havePathDependency(const AbstractPath& lhs, const AbstractPath& rhs)
+Opt<AbstractPath> AFS::getParentFolderPath(const AbstractPath& ap)
{
- warn_static("remove after migration")
- return static_cast<bool>(getPathComplement(lhs, rhs));
+ if (const Opt<AfsPath> parentAfsPath = getParentAfsPath(ap.afsPath))
+ return AbstractPath(ap.afs, *parentAfsPath);
+
+ return NoValue();
}
@@ -91,7 +71,7 @@ AFS::FileAttribAfterCopy AFS::copyFileAsStream(const AfsPath& afsPathSource, con int64_t totalUnbufferedIO = 0;
auto streamIn = getInputStream(afsPathSource, IOCallbackDivider(notifyUnbufferedIO, totalUnbufferedIO)); //throw FileError, ErrorFileLocked
-
+ warn_static("save following file access to improve SFTP perf?")
const uint64_t fileSizeExpected = streamIn->getFileSize (); //throw FileError
const int64_t modificationTime = streamIn->getModificationTime(); //throw FileError
const FileId sourceFileId = streamIn->getFileId (); //throw FileError
@@ -205,8 +185,8 @@ void AFS::createFolderIfMissingRecursion(const AbstractPath& ap) //throw FileErr }
catch (FileError&)
{
- Opt<PathDetails> pd;
- try { pd = getPathDetails(ap); /*throw FileError*/ }
+ Opt<PathStatus> pd;
+ try { pd = getPathStatus(ap); /*throw FileError*/ }
catch (FileError&) {} //previous exception is more relevant
if (pd && pd->existingType != ItemType::FILE)
@@ -239,7 +219,7 @@ private: }
-AFS::PathDetailsImpl AFS::getPathDetailsViaFolderTraversal(const AfsPath& afsPath) const //throw FileError
+AFS::PathStatusImpl AFS::getPathStatusViaFolderTraversal(const AfsPath& afsPath) const //throw FileError
{
const Opt<AfsPath> parentAfsPath = getParentAfsPath(afsPath);
try
@@ -257,11 +237,11 @@ AFS::PathDetailsImpl AFS::getPathDetailsViaFolderTraversal(const AfsPath& afsPat const Zstring itemName = getItemName(afsPath);
assert(!itemName.empty());
- PathDetailsImpl pd = getPathDetailsViaFolderTraversal(*parentAfsPath); //throw FileError
- if (!pd.relPath.empty())
+ PathStatusImpl ps = getPathStatusViaFolderTraversal(*parentAfsPath); //throw FileError
+ if (!ps.relPath.empty())
{
- pd.relPath.push_back(itemName);
- return { pd.existingType, pd.existingAfsPath, pd.relPath };
+ ps.relPath.push_back(itemName);
+ return { ps.existingType, ps.existingAfsPath, ps.relPath };
}
try
@@ -269,7 +249,7 @@ AFS::PathDetailsImpl AFS::getPathDetailsViaFolderTraversal(const AfsPath& afsPat ItemSearchCallback iscb(itemName);
traverseFolder(*parentAfsPath, iscb); //throw FileError, ItemType
- return { pd.existingType, *parentAfsPath, { itemName } }; //throw FileError
+ return { ps.existingType, *parentAfsPath, { itemName } }; //throw FileError
}
catch (const ItemType& type) { return { type, afsPath, {} }; } //yes, exceptions for control-flow are bad design... but, but...
//we're not CPU-bound here and finding the item after getItemType() previously failed is exceptional (even C:\pagefile.sys should be found)
@@ -278,16 +258,16 @@ AFS::PathDetailsImpl AFS::getPathDetailsViaFolderTraversal(const AfsPath& afsPat Opt<AFS::ItemType> AFS::getItemTypeIfExists(const AbstractPath& ap) //throw FileError
{
- const PathDetails pd = getPathDetails(ap); //throw FileError
+ const PathStatus pd = getPathStatus(ap); //throw FileError
if (pd.relPath.empty())
return pd.existingType;
return NoValue();
}
-AFS::PathDetails AFS::getPathDetails(const AbstractPath& ap) //throw FileError
+AFS::PathStatus AFS::getPathStatus(const AbstractPath& ap) //throw FileError
{
- const PathDetailsImpl pdi = ap.afs->getPathDetails(ap.afsPath); //throw FileError
+ const PathStatusImpl pdi = ap.afs->getPathStatus(ap.afsPath); //throw FileError
return { pdi.existingType, AbstractPath(ap.afs, pdi.existingAfsPath), pdi.relPath };
}
@@ -360,7 +340,7 @@ void AFS::removeFolderIfExistsRecursion(const AbstractPath& ap, //throw FileErro {
if (Opt<ItemType> type = AFS::getItemTypeIfExists(ap)) //throw FileError
{
- if (*type == AFS::ItemType::SYMLINK)
+ if (*type == AFS::ItemType::SYMLINK)
{
if (onBeforeFileDeletion)
onBeforeFileDeletion(AFS::getDisplayPath(ap));
diff --git a/FreeFileSync/Source/fs/abstract.h b/FreeFileSync/Source/fs/abstract.h index cff30195..eece5646 100755 --- a/FreeFileSync/Source/fs/abstract.h +++ b/FreeFileSync/Source/fs/abstract.h @@ -59,30 +59,22 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t static std::wstring getDisplayPath(const AbstractPath& ap) { return ap.afs->getDisplayPath(ap.afsPath); }
- static bool isNullPath(const AbstractPath& ap) { return ap.afsPath.value.empty() && ap.afs->isNullFileSystem(); }
+ static bool isNullPath(const AbstractPath& ap) { return ap.afs->isNullFileSystem() /*&& ap.afsPath.value.empty()*/; }
static AbstractPath appendRelPath(const AbstractPath& ap, const Zstring& relPath);
- struct PathComplement
- {
- Zstring relPathL; //- relative path is filled only if the corresponding abstract path is a sub folder of the other (=> both relative paths are empty if abstract paths match)
- Zstring relPathR; //- without FILE_NAME_SEPARATOR prefix/postfix
- };
- static Opt<PathComplement> getPathComplement(const AbstractPath& lhs, const AbstractPath& rhs);
-
static Zstring getItemName(const AbstractPath& ap) { assert(getParentFolderPath(ap)); return getItemName(ap.afsPath); }
- static bool havePathDependency(const AbstractPath& lhs, const AbstractPath& rhs);
-
static Opt<Zstring> getNativeItemPath(const AbstractPath& ap) { return ap.afs->getNativeItemPath(ap.afsPath); }
- static Opt<AbstractPath> getParentFolderPath(const AbstractPath& ap)
- {
- if (const Opt<AfsPath> parentAfsPath = getParentAfsPath(ap.afsPath))
- return AbstractPath(ap.afs, *parentAfsPath);
- return NoValue();
- }
+ static Opt<AbstractPath> getParentFolderPath(const AbstractPath& ap);
+ struct PathComponents
+ {
+ AbstractPath rootPath; //itemPath =: rootPath + relPath
+ std::vector<Zstring> relPath;
+ };
+ static PathComponents getPathComponents(const AbstractPath& ap);
//----------------------------------------------------------------------------------------------------------------
enum class ItemType
{
@@ -90,7 +82,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t FOLDER,
SYMLINK,
};
- struct PathDetails
+ struct PathStatus
{
ItemType existingType;
AbstractPath existingPath; //itemPath =: existingPath + relPath
@@ -100,7 +92,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t static ItemType getItemType(const AbstractPath& ap) { return ap.afs->getItemType(ap.afsPath); } //throw FileError
//execute potentially SLOW folder traversal but distinguish error/not existing
static Opt<ItemType> getItemTypeIfExists(const AbstractPath& ap); //throw FileError
- static PathDetails getPathDetails(const AbstractPath& ap); //throw FileError
+ static PathStatus getPathStatus(const AbstractPath& ap); //throw FileError
//----------------------------------------------------------------------------------------------------------------
//- error if already existing
@@ -292,13 +284,13 @@ protected: //grant derived classes access to AbstractPath: static Zstring getItemName(const AfsPath& afsPath) { return afterLast(afsPath.value, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); }
static Opt<AfsPath> getParentAfsPath(const AfsPath& afsPath);
- struct PathDetailsImpl
+ struct PathStatusImpl
{
ItemType existingType;
AfsPath existingAfsPath; //afsPath =: existingAfsPath + relPath
std::vector<Zstring> relPath; //
};
- PathDetailsImpl getPathDetailsViaFolderTraversal(const AfsPath& afsPath) const; //throw FileError
+ PathStatusImpl getPathStatusViaFolderTraversal(const AfsPath& afsPath) const; //throw FileError
FileAttribAfterCopy copyFileAsStream(const AfsPath& afsPathSource, const AbstractPath& apTarget, //throw FileError, ErrorTargetExisting, ErrorFileLocked
const IOCallback& notifyUnbufferedIO) const; //may be nullptr; throw X!
@@ -316,7 +308,7 @@ private: //----------------------------------------------------------------------------------------------------------------
virtual ItemType getItemType(const AfsPath& afsPath) const = 0; //throw FileError
- virtual PathDetailsImpl getPathDetails(const AfsPath& afsPath) const = 0; //throw FileError
+ virtual PathStatusImpl getPathStatus(const AfsPath& afsPath) const = 0; //throw FileError
//----------------------------------------------------------------------------------------------------------------
virtual void createFolderPlain(const AfsPath& afsPath) const = 0; //throw FileError
@@ -446,7 +438,11 @@ Zstring AbstractFileSystem::appendPaths(const Zstring& basePath, const Zstring& return basePath + (relPath.c_str() + 1);
}
else if (!endsWith(basePath, pathSep))
- return basePath + pathSep + relPath;
+ {
+ Zstring output = basePath;
+ output.reserve(basePath.size() + 1 + relPath.size()); //append all three strings using a single memory allocation
+ return std::move(output) + pathSep + relPath; //
+ }
return basePath + relPath;
}
diff --git a/FreeFileSync/Source/fs/native.cpp b/FreeFileSync/Source/fs/native.cpp index 93827486..9c1e1d42 100755 --- a/FreeFileSync/Source/fs/native.cpp +++ b/FreeFileSync/Source/fs/native.cpp @@ -156,7 +156,7 @@ private: Zstring getInitPathPhrase(const AfsPath& afsPath) const override { return getNativePath(afsPath); }
- std::wstring getDisplayPath(const AfsPath& afsPath) const override { return utfCvrtTo<std::wstring>(getNativePath(afsPath)); }
+ std::wstring getDisplayPath(const AfsPath& afsPath) const override { return utfTo<std::wstring>(getNativePath(afsPath)); }
bool isNullFileSystem() const override { return rootPath_.empty(); }
@@ -164,8 +164,8 @@ private: {
const Zstring& rootPathRhs = static_cast<const NativeFileSystem&>(afsRhs).rootPath_;
- return cmpFilePath(rootPath_ .c_str(), rootPath_ .size(),
- rootPathRhs.c_str(), rootPathRhs.size());
+ return CmpFilePath()(rootPath_ .c_str(), rootPath_ .size(),
+ rootPathRhs.c_str(), rootPathRhs.size());
}
//----------------------------------------------------------------------------------------------------------------
@@ -185,9 +185,9 @@ private: return AFS::ItemType::FILE;
}
- PathDetailsImpl getPathDetails(const AfsPath& afsPath) const override //throw FileError
+ PathStatusImpl getPathStatus(const AfsPath& afsPath) const override //throw FileError
{
- return getPathDetailsViaFolderTraversal(afsPath); //throw FileError
+ return getPathStatusViaFolderTraversal(afsPath); //throw FileError
}
//----------------------------------------------------------------------------------------------------------------
@@ -236,7 +236,7 @@ private: const Zstring nativePath = getNativePath(afsPath);
const Zstring resolvedPath = zen::getResolvedSymlinkPath(nativePath); //throw FileError
- const Opt<PathComponents> comp = getPathComponents(resolvedPath);
+ const Opt<zen::PathComponents> comp = parsePathComponents(resolvedPath);
if (!comp)
throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(nativePath)),
replaceCpy<std::wstring>(L"Invalid path %x.", L"%x", fmtPath(resolvedPath)));
@@ -249,7 +249,7 @@ private: initComForThread(); //throw FileError
const Zstring nativePath = getNativePath(afsPath);
- std::string content = utfCvrtTo<std::string>(getSymlinkTargetRaw(nativePath)); //throw FileError
+ std::string content = utfTo<std::string>(getSymlinkTargetRaw(nativePath)); //throw FileError
return content;
}
@@ -424,7 +424,7 @@ bool zen::acceptsItemPathPhraseNative(const Zstring& itemPathPhrase) //noexcept //don't accept relative paths!!! indistinguishable from Explorer MTP paths!
//don't accept paths missing the shared folder! (see drag & drop validation!)
- return static_cast<bool>(getPathComponents(path));
+ return static_cast<bool>(parsePathComponents(path));
}
@@ -438,11 +438,11 @@ AbstractPath zen::createItemPathNative(const Zstring& itemPathPhrase) //noexcept AbstractPath zen::createItemPathNativeNoFormatting(const Zstring& nativePath) //noexcept
{
- if (const Opt<PathComponents> comp = getPathComponents(nativePath))
+ if (const Opt<PathComponents> comp = parsePathComponents(nativePath))
return AbstractPath(std::make_shared<NativeFileSystem>(comp->rootPath), AfsPath(comp->relPath));
else
{
- assert(nativePath.empty());
+ // assert(nativePath.empty());
return AbstractPath(std::make_shared<NativeFileSystem>(nativePath), AfsPath(Zstring()));
}
}
diff --git a/FreeFileSync/Source/lib/db_file.cpp b/FreeFileSync/Source/lib/db_file.cpp index b1857a91..49ca3ec1 100755 --- a/FreeFileSync/Source/lib/db_file.cpp +++ b/FreeFileSync/Source/lib/db_file.cpp @@ -150,7 +150,7 @@ DbStreams loadStreams(const AbstractPath& dbPath, const IOCallback& notifyUnbuff catch (const std::bad_alloc& e) //still required?
{
throw FileError(_("Database file is corrupted:") + L"\n" + fmtPath(AFS::getDisplayPath(dbPath)),
- _("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what()));
+ _("Out of memory.") + L" " + utfTo<std::wstring>(e.what()));
}
}
@@ -259,7 +259,7 @@ private: }
}
- static void writeUtf8(MemoryStreamOut<ByteArray>& streamOut, const Zstring& str) { writeContainer(streamOut, utfCvrtTo<Zbase<char>>(str)); }
+ static void writeUtf8(MemoryStreamOut<ByteArray>& streamOut, const Zstring& str) { writeContainer(streamOut, utfTo<Zbase<char>>(str)); }
static void writeFileDescr(MemoryStreamOut<ByteArray>& streamOut, const InSyncDescrFile& descr)
{
@@ -388,7 +388,7 @@ public: catch (const std::bad_alloc& e)
{
throw FileError(_("Database file is corrupted:") + L"\n" + fmtPath(displayFilePathL) + L"\n" + fmtPath(displayFilePathR),
- _("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what()));
+ _("Out of memory.") + L" " + utfTo<std::wstring>(e.what()));
}
}
@@ -445,7 +445,7 @@ private: }
}
- static Zstring readUtf8(MemoryStreamIn<ByteArray>& streamIn) { return utfCvrtTo<Zstring>(readContainer<Zbase<char>>(streamIn)); } //throw UnexpectedEndOfStreamError
+ static Zstring readUtf8(MemoryStreamIn<ByteArray>& streamIn) { return utfTo<Zstring>(readContainer<Zbase<char>>(streamIn)); } //throw UnexpectedEndOfStreamError
//optional: use null-termiation: 5% overall size reduction
//optional: split into streamInText_/streamInSmallNum_: overall size increase! (why?)
@@ -543,11 +543,11 @@ private: filter_(filter),
activeCmpVar_(activeCmpVar) {}
- void recurse(const HierarchyObject& hierObj, InSyncFolder& dbFolder)
+ void recurse(const ContainerObject& hierObj, InSyncFolder& dbFolder)
{
- process(hierObj.refSubFiles (), hierObj.getPairRelativePathPf(), dbFolder.files);
- process(hierObj.refSubLinks (), hierObj.getPairRelativePathPf(), dbFolder.symlinks);
- process(hierObj.refSubFolders(), hierObj.getPairRelativePathPf(), dbFolder.folders);
+ process(hierObj.refSubFiles (), hierObj.getPairRelativePath(), dbFolder.files);
+ process(hierObj.refSubLinks (), hierObj.getPairRelativePath(), dbFolder.symlinks);
+ process(hierObj.refSubFolders(), hierObj.getPairRelativePath(), dbFolder.folders);
}
template <class M, class V>
@@ -584,12 +584,12 @@ private: */
}
- void process(const HierarchyObject::FileList& currentFiles, const Zstring& parentRelPathPf, InSyncFolder::FileList& dbFiles)
+ void process(const ContainerObject::FileList& currentFiles, const Zstring& parentRelPath, InSyncFolder::FileList& dbFiles)
{
std::unordered_set<const InSyncFile*> toPreserve; //referencing fixed-in-memory std::map elements
for (const FilePair& file : currentFiles)
- if (!file.isEmpty())
+ if (!file.isPairEmpty())
{
if (file.getCategory() == FILE_EQUAL) //data in sync: write current state
{
@@ -623,18 +623,18 @@ private: if (toPreserve.find(&v.second) != toPreserve.end())
return false;
//all items not existing in "currentFiles" have either been deleted meanwhile or been excluded via filter:
- const Zstring& itemRelPath = parentRelPathPf + v.first;
+ const Zstring& itemRelPath = AFS::appendPaths(parentRelPath, v.first, FILE_NAME_SEPARATOR);
return filter_.passFileFilter(itemRelPath);
//note: items subject to traveral errors are also excluded by this file filter here! see comparison.cpp, modified file filter for read errors
});
}
- void process(const HierarchyObject::SymlinkList& currentSymlinks, const Zstring& parentRelPathPf, InSyncFolder::SymlinkList& dbSymlinks)
+ void process(const ContainerObject::SymlinkList& currentSymlinks, const Zstring& parentRelPath, InSyncFolder::SymlinkList& dbSymlinks)
{
std::unordered_set<const InSyncSymlink*> toPreserve;
for (const SymlinkPair& symlink : currentSymlinks)
- if (!symlink.isEmpty())
+ if (!symlink.isPairEmpty())
{
if (symlink.getLinkCategory() == SYMLINK_EQUAL) //data in sync: write current state
{
@@ -661,17 +661,17 @@ private: if (toPreserve.find(&v.second) != toPreserve.end())
return false;
//all items not existing in "currentSymlinks" have either been deleted meanwhile or been excluded via filter:
- const Zstring& itemRelPath = parentRelPathPf + v.first;
+ const Zstring& itemRelPath = AFS::appendPaths(parentRelPath, v.first, FILE_NAME_SEPARATOR);
return filter_.passFileFilter(itemRelPath);
});
}
- void process(const HierarchyObject::FolderList& currentFolders, const Zstring& parentRelPathPf, InSyncFolder::FolderList& dbFolders)
+ void process(const ContainerObject::FolderList& currentFolders, const Zstring& parentRelPath, InSyncFolder::FolderList& dbFolders)
{
std::unordered_set<const InSyncFolder*> toPreserve;
for (const FolderPair& folder : currentFolders)
- if (!folder.isEmpty())
+ if (!folder.isPairEmpty())
switch (folder.getDirCategory())
{
case DIR_EQUAL:
@@ -723,7 +723,7 @@ private: if (toPreserve.find(&v.second) != toPreserve.end())
return false;
- const Zstring& itemRelPath = parentRelPathPf + v.first;
+ const Zstring& itemRelPath = AFS::appendPaths(parentRelPath, v.first, FILE_NAME_SEPARATOR);
//if directory is not included in "currentDirs", it is either not existing anymore, in which case it should be deleted from database
//or it was excluded via filter and the database entry should be preserved
diff --git a/FreeFileSync/Source/lib/dir_lock.cpp b/FreeFileSync/Source/lib/dir_lock.cpp index f6a40619..b85e9e58 100755 --- a/FreeFileSync/Source/lib/dir_lock.cpp +++ b/FreeFileSync/Source/lib/dir_lock.cpp @@ -57,7 +57,7 @@ public: }
catch (const std::exception& e) //exceptions must be catched per thread
{
- wxSafeShowMessage(L"FreeFileSync - " + _("An exception occurred"), utfCvrtTo<wxString>(e.what()) + L" (Dirlock)"); //simple wxMessageBox won't do for threads
+ wxSafeShowMessage(L"FreeFileSync - " + _("An exception occurred"), utfTo<wxString>(e.what()) + L" (Dirlock)"); //simple wxMessageBox won't do for threads
}
}
@@ -262,7 +262,7 @@ void waitOnDirLock(const Zstring& lockFilePath, DirLockCallback* callback) //thr {
const LockInformation& lockInfo = retrieveLockInfo(lockFilePath); //throw FileError
//enhance status message and show which user is holding the lock:
- infoMsg += L" | " + _("Lock owner:") + L' ' + utfCvrtTo<std::wstring>(lockInfo.userId);
+ infoMsg += L" | " + _("Lock owner:") + L' ' + utfTo<std::wstring>(lockInfo.userId);
originalLockId = lockInfo.lockId;
switch (getProcessStatus(lockInfo)) //throw FileError
diff --git a/FreeFileSync/Source/lib/ffs_paths.cpp b/FreeFileSync/Source/lib/ffs_paths.cpp index be2dbd3a..3007b479 100755 --- a/FreeFileSync/Source/lib/ffs_paths.cpp +++ b/FreeFileSync/Source/lib/ffs_paths.cpp @@ -8,7 +8,6 @@ #include <zen/file_access.h>
#include <wx/stdpaths.h>
#include <wx/app.h>
-#include <wx+/string_conv.h>
using namespace zen;
@@ -19,7 +18,7 @@ namespace inline
Zstring getExecutablePathPf() //directory containing executable WITH path separator at end
{
- return appendSeparator(beforeLast(utfCvrtTo<Zstring>(wxStandardPaths::Get().GetExecutablePath()), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE));
+ return appendSeparator(beforeLast(utfTo<Zstring>(wxStandardPaths::Get().GetExecutablePath()), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE));
}
}
@@ -43,7 +42,7 @@ Zstring zen::getResourceDirPf() if (isPortableVersion())
return getExecutablePathPf();
else //use OS' standard paths
- return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir()));
+ return appendSeparator(utfTo<Zstring>(wxStandardPathsBase::Get().GetResourcesDir()));
}
@@ -57,7 +56,7 @@ Zstring zen::getConfigDirPathPf() if (isPortableVersion())
return getExecutablePathPf();
//use OS' standard paths
- Zstring configDirPath = toZ(wxStandardPaths::Get().GetUserDataDir());
+ Zstring configDirPath = utfTo<Zstring>(wxStandardPaths::Get().GetUserDataDir());
try
{
createDirectoryIfMissingRecursion(configDirPath); //throw FileError
diff --git a/FreeFileSync/Source/lib/generate_logfile.h b/FreeFileSync/Source/lib/generate_logfile.h index 08c7c9c1..db8d8205 100755 --- a/FreeFileSync/Source/lib/generate_logfile.h +++ b/FreeFileSync/Source/lib/generate_logfile.h @@ -121,7 +121,7 @@ void streamToLogFile(const SummaryInfo& summary, //throw FileError const ErrorLog& log,
AFS::OutputStream& streamOut)
{
- const std::string header = replaceCpy(utfCvrtTo<std::string>(generateLogHeader(summary)), '\n', LINE_BREAK); //don't replace line break any earlier
+ const std::string header = replaceCpy(utfTo<std::string>(generateLogHeader(summary)), '\n', LINE_BREAK); //don't replace line break any earlier
streamOut.write(&header[0], header.size()); //throw FileError, X
@@ -131,7 +131,7 @@ void streamToLogFile(const SummaryInfo& summary, //throw FileError for (const LogEntry& entry : log)
{
- buffer += replaceCpy(utfCvrtTo<std::string>(formatMessage<std::wstring>(entry)), '\n', LINE_BREAK);
+ buffer += replaceCpy(utfTo<std::string>(formatMessage<std::wstring>(entry)), '\n', LINE_BREAK);
buffer += LINE_BREAK;
streamOut.write(&buffer[0], buffer.size()); //throw FileError, X
@@ -152,14 +152,14 @@ void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError {
const Zstring filepath = getLastSyncsLogfilePath();
- Utf8String newStream = utfCvrtTo<Utf8String>(generateLogHeader(summary));
+ Utf8String newStream = utfTo<Utf8String>(generateLogHeader(summary));
replace(newStream, '\n', LINE_BREAK); //don't replace line break any earlier
newStream += LINE_BREAK;
//check size of "newStream": memory allocation might fail - think 1 million entries!
for (const LogEntry& entry : log)
{
- newStream += replaceCpy(utfCvrtTo<Utf8String>(formatMessage<std::wstring>(entry)), '\n', LINE_BREAK);
+ newStream += replaceCpy(utfTo<Utf8String>(formatMessage<std::wstring>(entry)), '\n', LINE_BREAK);
newStream += LINE_BREAK;
if (newStream.size() > maxBytesToWrite)
diff --git a/FreeFileSync/Source/lib/hard_filter.cpp b/FreeFileSync/Source/lib/hard_filter.cpp index 2520f39c..d248789d 100755 --- a/FreeFileSync/Source/lib/hard_filter.cpp +++ b/FreeFileSync/Source/lib/hard_filter.cpp @@ -134,8 +134,7 @@ bool matchesMask(const Zchar* path, const Zchar* mask) break;
case Zstr('*'):
- //advance mask to next non-* char
- do
+ do //advance mask to next non-* char
{
m = *++mask;
}
@@ -220,11 +219,11 @@ bool matchesMaskBegin(const Zstring& name, const std::vector<Zstring>& masks) std::vector<Zstring> zen::splitByDelimiter(const Zstring& filterString)
{
- //delimiters may be ';' or '\n'
+ //delimiters may be FILTER_ITEM_SEPARATOR or '\n'
std::vector<Zstring> output;
- for (const Zstring& str : split(filterString, Zchar(';'))) //split by less common delimiter first
- for (Zstring entry : split(str, Zchar('\n')))
+ for (const Zstring& str : split(filterString, FILTER_ITEM_SEPARATOR, SplitType::SKIP_EMPTY)) //split by less common delimiter first (create few, large strings)
+ for (Zstring entry : split(str, Zchar('\n'), SplitType::SKIP_EMPTY))
{
trim(entry);
if (!entry.empty())
diff --git a/FreeFileSync/Source/lib/hard_filter.h b/FreeFileSync/Source/lib/hard_filter.h index cb7187bd..c4484d21 100755 --- a/FreeFileSync/Source/lib/hard_filter.h +++ b/FreeFileSync/Source/lib/hard_filter.h @@ -115,6 +115,7 @@ private: const NameFilter second_;
};
+const Zchar FILTER_ITEM_SEPARATOR = Zstr('|');
diff --git a/FreeFileSync/Source/lib/help_provider.h b/FreeFileSync/Source/lib/help_provider.h index b1278424..15d986a7 100755 --- a/FreeFileSync/Source/lib/help_provider.h +++ b/FreeFileSync/Source/lib/help_provider.h @@ -47,7 +47,7 @@ void displayHelpEntry(const wxString& topic, wxWindow* parent) -> what if FFS is blocked, but the web browser would have internet access??
{
const wxString section = L"html/" + topic + L".html";
- wxHtmlModalHelp dlg(parent, utfCvrtTo<wxString>(zen::getResourceDirPf()) + L"Help/FreeFileSync.hhp", section,
+ wxHtmlModalHelp dlg(parent, utfTo<wxString>(zen::getResourceDirPf()) + L"Help/FreeFileSync.hhp", section,
wxHF_DEFAULT_STYLE | wxHF_DIALOG | wxHF_MODAL | wxHF_MERGE_BOOKS);
(void)dlg;
//-> solves modal help craziness on OSX!
diff --git a/FreeFileSync/Source/lib/localization.cpp b/FreeFileSync/Source/lib/localization.cpp index 47467781..6b303912 100755 --- a/FreeFileSync/Source/lib/localization.cpp +++ b/FreeFileSync/Source/lib/localization.cpp @@ -87,19 +87,19 @@ FFSTranslation::FFSTranslation(const Zstring& lngFilePath, wxLanguage langId) : for (const auto& item : transInput)
{
- const std::wstring original = utfCvrtTo<std::wstring>(item.first);
- const std::wstring translation = utfCvrtTo<std::wstring>(item.second);
+ const std::wstring original = utfTo<std::wstring>(item.first);
+ const std::wstring translation = utfTo<std::wstring>(item.second);
transMapping.emplace(original, translation);
}
for (const auto& item : transPluralInput)
{
- const std::wstring engSingular = utfCvrtTo<std::wstring>(item.first.first);
- const std::wstring engPlural = utfCvrtTo<std::wstring>(item.first.second);
+ const std::wstring engSingular = utfTo<std::wstring>(item.first.first);
+ const std::wstring engPlural = utfTo<std::wstring>(item.first.second);
std::vector<std::wstring> plFormsWide;
for (const std::string& pf : item.second)
- plFormsWide.push_back(utfCvrtTo<std::wstring>(pf));
+ plFormsWide.push_back(utfTo<std::wstring>(pf));
transMappingPl.emplace(std::make_pair(engSingular, engPlural), plFormsWide);
}
@@ -108,17 +108,6 @@ FFSTranslation::FFSTranslation(const Zstring& lngFilePath, wxLanguage langId) : }
-struct LessTranslation
-{
- bool operator()(const TranslationInfo& lhs, const TranslationInfo& rhs) const
- {
- //use a more "natural" sort: ignore case and diacritics
- return ::wcscasecmp(lhs.languageName.c_str(), rhs.languageName.c_str()) < 0; //ignores case; locale-dependent!
-
- }
-};
-
-
std::vector<TranslationInfo> loadTranslations()
{
std::vector<TranslationInfo> locMapping;
@@ -165,13 +154,13 @@ std::vector<TranslationInfo> loadTranslations() wxLANGUAGE_SPANISH //non-unique, but still mapped correctly (or is it incidentally???)
wxLANGUAGE_SERBIAN //
*/
- if (const wxLanguageInfo* locInfo = wxLocale::FindLanguageInfo(utfCvrtTo<wxString>(lngHeader.localeName)))
+ if (const wxLanguageInfo* locInfo = wxLocale::FindLanguageInfo(utfTo<wxString>(lngHeader.localeName)))
{
TranslationInfo newEntry;
newEntry.languageID = static_cast<wxLanguage>(locInfo->Language);
- newEntry.languageName = utfCvrtTo<std::wstring>(lngHeader.languageName);
- newEntry.translatorName = utfCvrtTo<std::wstring>(lngHeader.translatorName);
- newEntry.languageFlag = utfCvrtTo<std::wstring>(lngHeader.flagFile);
+ newEntry.languageName = utfTo<std::wstring>(lngHeader.languageName);
+ newEntry.translatorName = utfTo<std::wstring>(lngHeader.translatorName);
+ newEntry.languageFlag = utfTo<std::wstring>(lngHeader.flagFile);
newEntry.langFilePath = filePath;
locMapping.push_back(newEntry);
}
@@ -181,7 +170,11 @@ std::vector<TranslationInfo> loadTranslations() catch (lngfile::ParsingError&) { assert(false); } //better not show an error message here; scenario: batch jobs
}
- std::sort(locMapping.begin(), locMapping.end(), LessTranslation());
+ std::sort(locMapping.begin(), locMapping.end(), [](const TranslationInfo& lhs, const TranslationInfo& rhs)
+ {
+ return LessNaturalSort()(utfTo<Zstring>(lhs.languageName),
+ utfTo<Zstring>(rhs.languageName)); //use a more "natural" sort: ignore case and diacritics
+ });
return locMapping;
}
diff --git a/FreeFileSync/Source/lib/lock_holder.h b/FreeFileSync/Source/lib/lock_holder.h index 32b1e7ab..cff6b075 100755 --- a/FreeFileSync/Source/lib/lock_holder.h +++ b/FreeFileSync/Source/lib/lock_holder.h @@ -19,7 +19,7 @@ class LockHolder {
public:
LockHolder(const std::set<Zstring, LessFilePath>& dirpathsExisting, //resolved paths
- bool& warningDirectoryLockFailed,
+ bool& warnDirectoryLockFailed,
ProcessCallback& procCallback)
{
for (const Zstring& dirpath : dirpathsExisting)
@@ -42,7 +42,7 @@ public: catch (const FileError& e)
{
const std::wstring msg = replaceCpy(_("Cannot set directory lock for %x."), L"%x", fmtPath(dirpath)) + L"\n\n" + e.toString();
- procCallback.reportWarning(msg, warningDirectoryLockFailed); //may throw!
+ procCallback.reportWarning(msg, warnDirectoryLockFailed); //may throw!
}
}
}
diff --git a/FreeFileSync/Source/lib/parse_lng.h b/FreeFileSync/Source/lib/parse_lng.h index 83cc33bd..c341c6d3 100755 --- a/FreeFileSync/Source/lib/parse_lng.h +++ b/FreeFileSync/Source/lib/parse_lng.h @@ -336,7 +336,7 @@ private: class LngParser
{
public:
- LngParser(const std::string& fileStream) : scn(fileStream), tk(scn.nextToken()) {}
+ LngParser(const std::string& fileStream) : scn_(fileStream), tk_(scn_.nextToken()) {}
void parse(TranslationMap& out, TranslationPluralMap& pluralOut, TransHeader& header)
{
@@ -352,7 +352,7 @@ public: }
catch (const parse_plural::InvalidPluralForm&)
{
- throw ParsingError(L"Invalid plural form definition", scn.posRow(), scn.posCol());
+ throw ParsingError(L"Invalid plural form definition", scn_.posRow(), scn_.posCol());
}
}
@@ -457,12 +457,12 @@ private: using namespace zen;
if (original.empty())
- throw ParsingError(L"Translation source text is empty", scn.posRow(), scn.posCol());
+ throw ParsingError(L"Translation source text is empty", scn_.posRow(), scn_.posCol());
- if (!isValidUtf8(original))
- throw ParsingError(L"Translation source text contains UTF-8 encoding error", scn.posRow(), scn.posCol());
- if (!isValidUtf8(translation))
- throw ParsingError(L"Translation text contains UTF-8 encoding error", scn.posRow(), scn.posCol());
+ if (!isValidUtf(original))
+ throw ParsingError(L"Translation source text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol());
+ if (!isValidUtf(translation))
+ throw ParsingError(L"Translation text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol());
if (!translation.empty())
{
@@ -471,7 +471,7 @@ private: {
if (contains(original, placeholder) &&
!contains(translation, placeholder))
- throw ParsingError(replaceCpy<std::wstring>(L"Placeholder %x missing in translation", L"%x", utfCvrtTo<std::wstring>(placeholder)), scn.posRow(), scn.posCol());
+ throw ParsingError(replaceCpy<std::wstring>(L"Placeholder %x missing in translation", L"%x", utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol());
};
checkPlaceholder("%x");
checkPlaceholder("%y");
@@ -487,19 +487,19 @@ private: const size_t ampCountOrig = ampersandTokenCount(original);
if (ampCountOrig != ampersandTokenCount(translation) ||
ampCountOrig > 1)
- throw ParsingError(L"Source and translation both need exactly one & character to mark a menu item access key or none at all", scn.posRow(), scn.posCol());
+ throw ParsingError(L"Source and translation both need exactly one & character to mark a menu item access key or none at all", scn_.posRow(), scn_.posCol());
//ampersand at the end makes buggy wxWidgets crash miserably
if (ampCountOrig > 0)
if ((endsWith(original, "&") && !endsWith(original, "&&")) ||
(endsWith(translation, "&") && !endsWith(translation, "&&")))
- throw ParsingError(L"The & character to mark a menu item access key must not occur at the end of a string", scn.posRow(), scn.posCol());
+ throw ParsingError(L"The & character to mark a menu item access key must not occur at the end of a string", scn_.posRow(), scn_.posCol());
//if source ends with colon, so must translation (note: character seems to be universally used, even for asian and arabic languages)
if (endsWith(original, ":") &&
!endsWith(translation, ":") &&
!endsWith(translation, "\xef\xbc\x9a")) //chinese colon
- throw ParsingError(L"Source text ends with a colon character \":\", but translation does not", scn.posRow(), scn.posCol());
+ throw ParsingError(L"Source text ends with a colon character \":\", but translation does not", scn_.posRow(), scn_.posCol());
auto endsWithSingleDot = [](const std::string& s) { return endsWith(s, ".") && !endsWith(s, ".."); };
@@ -508,23 +508,23 @@ private: !endsWithSingleDot(translation) &&
!endsWith(translation, "\xe0\xa5\xa4") && //hindi period
!endsWith(translation, "\xe3\x80\x82")) //chinese period
- throw ParsingError(L"Source text ends with a punctuation mark character \".\", but translation does not", scn.posRow(), scn.posCol());
+ throw ParsingError(L"Source text ends with a punctuation mark character \".\", but translation does not", scn_.posRow(), scn_.posCol());
//if source ends with an ellipsis, so must translation (note: character seems to be universally used, even for asian and arabic languages)
if (endsWith(original, "...") &&
!endsWith(translation, "...") &&
!endsWith(translation, "\xe2\x80\xa6")) //narrow ellipsis (spanish?)
- throw ParsingError(L"Source text ends with an ellipsis \"...\", but translation does not", scn.posRow(), scn.posCol());
+ throw ParsingError(L"Source text ends with an ellipsis \"...\", but translation does not", scn_.posRow(), scn_.posCol());
//if source is a one-liner, so should be the translation
if (!contains(original, '\n') && contains(translation, '\n'))
- throw ParsingError(L"Source text is a one-liner, but translation consists of multiple lines", scn.posRow(), scn.posCol());
+ throw ParsingError(L"Source text is a one-liner, but translation consists of multiple lines", scn_.posRow(), scn_.posCol());
//check for correct FFS brand names
if (contains(original, "FreeFileSync") && !contains(translation, "FreeFileSync"))
- throw ParsingError(L"Misspelled \"FreeFileSync\" in translation", scn.posRow(), scn.posCol());
+ throw ParsingError(L"Misspelled \"FreeFileSync\" in translation", scn_.posRow(), scn_.posCol());
if (contains(original, "RealTimeSync") && !contains(translation, "RealTimeSync"))
- throw ParsingError(L"Misspelled \"RealTimeSync\" in translation", scn.posRow(), scn.posCol());
+ throw ParsingError(L"Misspelled \"RealTimeSync\" in translation", scn_.posRow(), scn_.posCol());
}
}
@@ -533,18 +533,18 @@ private: using namespace zen;
//check the primary placeholder is existing at least for the second english text
if (!contains(original.second, "%x"))
- throw ParsingError(L"Plural form source text does not contain %x placeholder", scn.posRow(), scn.posCol());
+ throw ParsingError(L"Plural form source text does not contain %x placeholder", scn_.posRow(), scn_.posCol());
- if (!isValidUtf8(original.first) || !isValidUtf8(original.second))
- throw ParsingError(L"Translation source text contains UTF-8 encoding error", scn.posRow(), scn.posCol());
- if (std::any_of(translation.begin(), translation.end(), [](const std::string& pform) { return !isValidUtf8(pform); }))
- throw ParsingError(L"Translation text contains UTF-8 encoding error", scn.posRow(), scn.posCol());
+ if (!isValidUtf(original.first) || !isValidUtf(original.second))
+ throw ParsingError(L"Translation source text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol());
+ if (std::any_of(translation.begin(), translation.end(), [](const std::string& pform) { return !isValidUtf(pform); }))
+ throw ParsingError(L"Translation text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol());
if (!translation.empty())
{
//check for invalid number of plural forms
if (pluralInfo.getCount() != static_cast<int>(translation.size()))
- throw ParsingError(replaceCpy(replaceCpy<std::wstring>(L"Invalid number of plural forms; actual: %x, expected: %y", L"%x", numberTo<std::wstring>(translation.size())), L"%y", numberTo<std::wstring>(pluralInfo.getCount())), scn.posRow(), scn.posCol());
+ throw ParsingError(replaceCpy(replaceCpy<std::wstring>(L"Invalid number of plural forms; actual: %x, expected: %y", L"%x", numberTo<std::wstring>(translation.size())), L"%y", numberTo<std::wstring>(pluralInfo.getCount())), scn_.posRow(), scn_.posCol());
//check for duplicate plural form translations (catch copy & paste errors for single-number form translations)
for (auto it = translation.begin(); it != translation.end(); ++it)
@@ -552,7 +552,7 @@ private: {
auto it2 = std::find(it + 1, translation.end(), *it);
if (it2 != translation.end())
- throw ParsingError(replaceCpy<std::wstring>(L"Duplicate plural form translation at index position %x", L"%x", numberTo<std::wstring>(it2 - translation.begin())), scn.posRow(), scn.posCol());
+ throw ParsingError(replaceCpy<std::wstring>(L"Duplicate plural form translation at index position %x", L"%x", numberTo<std::wstring>(it2 - translation.begin())), scn_.posRow(), scn_.posCol());
}
for (int pos = 0; pos < static_cast<int>(translation.size()); ++pos)
@@ -566,14 +566,14 @@ private: if (!(contains(translation[pos], "%x") ||
contains(translation[pos], numberTo<std::string>(firstNumber))))
throw ParsingError(replaceCpy<std::wstring>(replaceCpy<std::wstring>(L"Plural form translation at index position %y needs to use the decimal number %z or the %x placeholder",
- L"%y", numberTo<std::wstring>(pos)), L"%z", numberTo<std::wstring>(firstNumber)), scn.posRow(), scn.posCol());
+ L"%y", numberTo<std::wstring>(pos)), L"%z", numberTo<std::wstring>(firstNumber)), scn_.posRow(), scn_.posCol());
}
}
else
{
//ensure the placeholder is used when needed
if (!contains(translation[pos], "%x"))
- throw ParsingError(replaceCpy<std::wstring>(L"Plural form at index position %y is missing the %x placeholder", L"%y", numberTo<std::wstring>(pos)), scn.posRow(), scn.posCol());
+ throw ParsingError(replaceCpy<std::wstring>(L"Plural form at index position %y is missing the %x placeholder", L"%y", numberTo<std::wstring>(pos)), scn_.posRow(), scn_.posCol());
}
auto checkSecondaryPlaceholder = [&](const std::string& placeholder)
@@ -584,11 +584,11 @@ private: {
if (!zen::contains(original.first, placeholder) ||
!zen::contains(original.second, placeholder))
- throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form source", L"%x", zen::utfCvrtTo<std::wstring>(placeholder)), scn.posRow(), scn.posCol());
+ throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form source", L"%x", zen::utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol());
//secondary placeholder is required for all plural forms
if (std::any_of(translation.begin(), translation.end(), [&](const std::string& pform) { return !zen::contains(pform, placeholder); }))
- throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form translation", L"%x", zen::utfCvrtTo<std::wstring>(placeholder)), scn.posRow(), scn.posCol());
+ throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form translation", L"%x", zen::utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol());
}
};
checkSecondaryPlaceholder("%y");
@@ -597,12 +597,12 @@ private: //if source is a one-liner, so should be the translation
if (!contains(original.first, '\n') && !contains(original.second, '\n') &&
std::any_of(translation.begin(), translation.end(), [](const std::string& pform) { return contains(pform, '\n'); }))
- throw ParsingError(L"Source text is a one-liner, but at least one plural form translation consists of multiple lines", scn.posRow(), scn.posCol());
+ throw ParsingError(L"Source text is a one-liner, but at least one plural form translation consists of multiple lines", scn_.posRow(), scn_.posCol());
}
}
- void nextToken() { tk = scn.nextToken(); }
- const Token& token() const { return tk; }
+ void nextToken() { tk_ = scn_.nextToken(); }
+ const Token& token() const { return tk_; }
void consumeToken(Token::Type t) //throw ParsingError
{
@@ -613,11 +613,11 @@ private: void expectToken(Token::Type t) //throw ParsingError
{
if (token().type != t)
- throw ParsingError(L"Unexpected token", scn.posRow(), scn.posCol());
+ throw ParsingError(L"Unexpected token", scn_.posRow(), scn_.posCol());
}
- Scanner scn;
- Token tk;
+ Scanner scn_;
+ Token tk_;
};
diff --git a/FreeFileSync/Source/lib/parse_plural.h b/FreeFileSync/Source/lib/parse_plural.h index 9b28bc1a..81fd6009 100755 --- a/FreeFileSync/Source/lib/parse_plural.h +++ b/FreeFileSync/Source/lib/parse_plural.h @@ -272,8 +272,8 @@ class Parser {
public:
Parser(const std::string& stream, int64_t& n) :
- scn(stream),
- tk(scn.nextToken()),
+ scn_(stream),
+ tk_(scn_.nextToken()),
n_(n) {}
std::shared_ptr<Expr<int64_t>> parse() //throw ParsingError; return value always bound!
@@ -419,8 +419,8 @@ private: throw ParsingError();
}
- void nextToken() { tk = scn.nextToken(); }
- const Token& token() const { return tk; }
+ void nextToken() { tk_ = scn_.nextToken(); }
+ const Token& token() const { return tk_; }
void expectToken(Token::Type t) //throw ParsingError
{
@@ -428,8 +428,8 @@ private: throw ParsingError();
}
- Scanner scn;
- Token tk;
+ Scanner scn_;
+ Token tk_;
int64_t& n_;
};
}
diff --git a/FreeFileSync/Source/lib/process_xml.cpp b/FreeFileSync/Source/lib/process_xml.cpp index 8a98f3fe..181f2db6 100755 --- a/FreeFileSync/Source/lib/process_xml.cpp +++ b/FreeFileSync/Source/lib/process_xml.cpp @@ -24,8 +24,8 @@ namespace {
//-------------------------------------------------------------------------------------------------------------------------------
const int XML_FORMAT_VER_GLOBAL = 4;
-const int XML_FORMAT_VER_FFS_GUI = 6;
-const int XML_FORMAT_VER_FFS_BATCH = 6;
+const int XML_FORMAT_VER_FFS_GUI = 7; //2017-02-16
+const int XML_FORMAT_VER_FFS_BATCH = 7; //
//-------------------------------------------------------------------------------------------------------------------------------
}
@@ -135,8 +135,8 @@ namespace std::vector<Zstring> splitFilterByLines(const Zstring& filterPhrase)
{
if (filterPhrase.empty())
- return std::vector<Zstring>();
- return split(filterPhrase, Zstr('\n'));
+ return {};
+ return split(filterPhrase, Zstr('\n'), SplitType::ALLOW_EMPTY);
}
Zstring mergeFilterLines(const std::vector<Zstring>& filterLines)
@@ -157,7 +157,7 @@ void writeText(const wxLanguage& value, std::string& output) //use description as unique wxLanguage identifier, see localization.cpp
//=> handle changes to wxLanguage enum between wxWidgets versions
if (const wxLanguageInfo* lngInfo = wxLocale::GetLanguageInfo(value))
- output = utfCvrtTo<std::string>(lngInfo->Description);
+ output = utfTo<std::string>(lngInfo->Description);
else
{
assert(false);
@@ -168,7 +168,7 @@ void writeText(const wxLanguage& value, std::string& output) template <> inline
bool readText(const std::string& input, wxLanguage& value)
{
- if (const wxLanguageInfo* lngInfo = wxLocale::FindLanguageInfo(utfCvrtTo<wxString>(input)))
+ if (const wxLanguageInfo* lngInfo = wxLocale::FindLanguageInfo(utfTo<wxString>(input)))
{
value = static_cast<wxLanguage>(lngInfo->Language);
return true;
@@ -785,7 +785,7 @@ namespace zen template <> inline
bool readText(const std::string& input, ConfigFileItem& value)
{
- value.filePath_ = resolveFreeFileSyncDriveMacro(utfCvrtTo<Zstring>(input));
+ value.filePath_ = resolveFreeFileSyncDriveMacro(utfTo<Zstring>(input));
return true;
}
@@ -793,7 +793,7 @@ bool readText(const std::string& input, ConfigFileItem& value) template <> inline
void writeText(const ConfigFileItem& value, std::string& output)
{
- output = utfCvrtTo<std::string>(substituteFreeFileSyncDriveLetter(value.filePath_));
+ output = utfTo<std::string>(substituteFreeFileSyncDriveLetter(value.filePath_));
}
}
@@ -847,15 +847,19 @@ void readConfig(const XmlIn& in, SyncConfig& syncCfg) }
-void readConfig(const XmlIn& in, FilterConfig& filter)
+void readConfig(const XmlIn& in, FilterConfig& filter, int formatVer)
{
- std::vector<Zstring> tmp = splitFilterByLines(filter.includeFilter); //save default value
- in["Include"](tmp);
- filter.includeFilter = mergeFilterLines(tmp);
+ std::vector<Zstring> tmpIn = splitFilterByLines(filter.includeFilter); //consider default value
+ in["Include"](tmpIn);
+ filter.includeFilter = mergeFilterLines(tmpIn);
- std::vector<Zstring> tmp2 = splitFilterByLines(filter.excludeFilter); //save default value
- in["Exclude"](tmp2);
- filter.excludeFilter = mergeFilterLines(tmp2);
+ std::vector<Zstring> tmpEx = splitFilterByLines(filter.excludeFilter); //consider default value
+ in["Exclude"](tmpEx);
+ filter.excludeFilter = mergeFilterLines(tmpEx);
+
+ //TODO: remove macro migration after some time! 2017-02-16
+ if (formatVer <= 6) replace(filter.includeFilter, Zstr(';'), Zstr('|'));
+ if (formatVer <= 6) replace(filter.excludeFilter, Zstr(';'), Zstr('|'));
in["TimeSpan"](filter.timeSpan);
in["TimeSpan"].attribute("Type", filter.unitTimeSpan);
@@ -927,7 +931,7 @@ void readConfig(const XmlIn& in, FolderPairEnh& enhPair, int formatVer) //###########################################################
//alternate filter configuration
if (XmlIn inLocFilter = in["LocalFilter"])
- readConfig(inLocFilter, enhPair.localFilter);
+ readConfig(inLocFilter, enhPair.localFilter, formatVer);
}
@@ -944,7 +948,7 @@ void readConfig(const XmlIn& in, MainConfiguration& mainCfg, int formatVer) //###########################################################
//read filter settings
- readConfig(inMain["GlobalFilter"], mainCfg.globalFilter);
+ readConfig(inMain["GlobalFilter"], mainCfg.globalFilter, formatVer);
//###########################################################
//read all folder pairs
@@ -1023,17 +1027,18 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config, int formatVer) inGeneral["NotificationSound" ].attribute("SyncFinished", config.soundFileSyncFinished);
XmlIn inOpt = inGeneral["OptionalDialogs"];
- inOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warningUnresolvedConflicts);
- inOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", config.optDialogs.warningNotEnoughDiskSpace);
- inOpt["WarnSignificantDifference" ].attribute("Enabled", config.optDialogs.warningSignificantDifference);
- inOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", config.optDialogs.warningRecyclerMissing);
- inOpt["WarnInputFieldEmpty" ].attribute("Enabled", config.optDialogs.warningInputFieldEmpty);
- inOpt["WarnDatabaseError" ].attribute("Enabled", config.optDialogs.warningDatabaseError);
- inOpt["WarnDependentFolders" ].attribute("Enabled", config.optDialogs.warningDependentFolders);
- inOpt["WarnFolderPairRaceCondition"].attribute("Enabled", config.optDialogs.warningFolderPairRaceCondition);
- inOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warningDirectoryLockFailed);
- inOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange);
- inOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart);
+ inOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warnUnresolvedConflicts);
+ inOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", config.optDialogs.warnNotEnoughDiskSpace);
+ inOpt["WarnSignificantDifference" ].attribute("Enabled", config.optDialogs.warnSignificantDifference);
+ inOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", config.optDialogs.warnRecyclerMissing);
+ inOpt["WarnInputFieldEmpty" ].attribute("Enabled", config.optDialogs.warnInputFieldEmpty);
+ inOpt["WarnDatabaseError" ].attribute("Enabled", config.optDialogs.warnDatabaseError);
+ inOpt["WarnDependentFolderPair" ].attribute("Enabled", config.optDialogs.warnDependentFolderPair);
+ inOpt["WarnDependentBaseFolders" ].attribute("Enabled", config.optDialogs.warnDependentBaseFolders);
+ inOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warnDirectoryLockFailed);
+ inOpt["WarnVersioningFolderPartOfSync" ].attribute("Enabled", config.optDialogs.warnVersioningFolderPartOfSync);
+ inOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange);
+ inOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart);
inOpt["ConfirmExternalCommandMassInvoke"].attribute("Enabled", config.optDialogs.confirmExternalCommandMassInvoke);
//gui specific global settings (optional)
@@ -1459,17 +1464,18 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) outGeneral["NotificationSound" ].attribute("SyncFinished", config.soundFileSyncFinished);
XmlOut outOpt = outGeneral["OptionalDialogs"];
- outOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warningUnresolvedConflicts);
- outOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", config.optDialogs.warningNotEnoughDiskSpace);
- outOpt["WarnSignificantDifference" ].attribute("Enabled", config.optDialogs.warningSignificantDifference);
- outOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", config.optDialogs.warningRecyclerMissing);
- outOpt["WarnInputFieldEmpty" ].attribute("Enabled", config.optDialogs.warningInputFieldEmpty);
- outOpt["WarnDatabaseError" ].attribute("Enabled", config.optDialogs.warningDatabaseError);
- outOpt["WarnDependentFolders" ].attribute("Enabled", config.optDialogs.warningDependentFolders);
- outOpt["WarnFolderPairRaceCondition"].attribute("Enabled", config.optDialogs.warningFolderPairRaceCondition);
- outOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warningDirectoryLockFailed);
- outOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange);
- outOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart);
+ outOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warnUnresolvedConflicts);
+ outOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", config.optDialogs.warnNotEnoughDiskSpace);
+ outOpt["WarnSignificantDifference" ].attribute("Enabled", config.optDialogs.warnSignificantDifference);
+ outOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", config.optDialogs.warnRecyclerMissing);
+ outOpt["WarnInputFieldEmpty" ].attribute("Enabled", config.optDialogs.warnInputFieldEmpty);
+ outOpt["WarnDatabaseError" ].attribute("Enabled", config.optDialogs.warnDatabaseError);
+ outOpt["WarnDependentFolderPair" ].attribute("Enabled", config.optDialogs.warnDependentFolderPair);
+ outOpt["WarnDependentBaseFolders" ].attribute("Enabled", config.optDialogs.warnDependentBaseFolders);
+ outOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warnDirectoryLockFailed);
+ outOpt["WarnVersioningFolderPartOfSync" ].attribute("Enabled", config.optDialogs.warnVersioningFolderPartOfSync);
+ outOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange);
+ outOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart);
outOpt["ConfirmExternalCommandMassInvoke"].attribute("Enabled", config.optDialogs.confirmExternalCommandMassInvoke);
//gui specific global settings (optional)
@@ -1592,5 +1598,5 @@ std::wstring xmlAccess::extractJobName(const Zstring& configFilename) {
const Zstring shortName = afterLast(configFilename, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
const Zstring jobName = beforeLast(shortName, Zstr('.'), IF_MISSING_RETURN_ALL);
- return utfCvrtTo<std::wstring>(jobName);
+ return utfTo<std::wstring>(jobName);
}
diff --git a/FreeFileSync/Source/lib/process_xml.h b/FreeFileSync/Source/lib/process_xml.h index ba22b36f..bdcb438c 100755 --- a/FreeFileSync/Source/lib/process_xml.h +++ b/FreeFileSync/Source/lib/process_xml.h @@ -76,15 +76,16 @@ struct XmlBatchConfig struct OptionalDialogs
{
- bool warningDependentFolders = true;
- bool warningFolderPairRaceCondition = true;
- bool warningSignificantDifference = true;
- bool warningNotEnoughDiskSpace = true;
- bool warningUnresolvedConflicts = true;
- bool warningDatabaseError = true;
- bool warningRecyclerMissing = true;
- bool warningInputFieldEmpty = true;
- bool warningDirectoryLockFailed = true;
+ bool warnDependentFolderPair = true;
+ bool warnDependentBaseFolders = true;
+ bool warnSignificantDifference = true;
+ bool warnNotEnoughDiskSpace = true;
+ bool warnUnresolvedConflicts = true;
+ bool warnDatabaseError = true;
+ bool warnRecyclerMissing = true;
+ bool warnInputFieldEmpty = true;
+ bool warnDirectoryLockFailed = true;
+ bool warnVersioningFolderPartOfSync = true;
bool popupOnConfigChange = true;
bool confirmSyncStart = true;
bool confirmExternalCommandMassInvoke = true;
diff --git a/FreeFileSync/Source/lib/resolve_path.cpp b/FreeFileSync/Source/lib/resolve_path.cpp index b9b9c9f0..037701c1 100755 --- a/FreeFileSync/Source/lib/resolve_path.cpp +++ b/FreeFileSync/Source/lib/resolve_path.cpp @@ -80,24 +80,23 @@ Zstring resolveRelativePath(const Zstring& relativePath) -
-Opt<Zstring> resolveMacro(const Zstring& macro, //macro without %-characters
- const std::vector<std::pair<Zstring, Zstring>>& ext) //return nullptr if not resolved
+//returns value if resolved
+Opt<Zstring> tryResolveMacro(const Zstring& macro) //macro without %-characters
{
//there exist environment variables named %TIME%, %DATE% so check for our internal macros first!
- if (ciEqual(macro, Zstr("time")))
+ if (strEqual(macro, Zstr("time"), CmpAsciiNoCase()))
return formatTime<Zstring>(Zstr("%H%M%S"));
- if (ciEqual(macro, Zstr("date")))
+ if (strEqual(macro, Zstr("date"), CmpAsciiNoCase()))
return formatTime<Zstring>(FORMAT_ISO_DATE);
- if (ciEqual(macro, Zstr("timestamp")))
+ if (strEqual(macro, Zstr("timestamp"), CmpAsciiNoCase()))
return formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S")); //e.g. "2012-05-15 131513"
Zstring timeStr;
auto resolveTimePhrase = [&](const Zchar* phrase, const Zchar* format) -> bool
{
- if (!ciEqual(macro, phrase))
+ if (!strEqual(macro, phrase, CmpAsciiNoCase()))
return false;
timeStr = formatTime<Zstring>(format);
@@ -113,13 +112,6 @@ Opt<Zstring> resolveMacro(const Zstring& macro, //macro without %-characters if (resolveTimePhrase(Zstr("min" ), Zstr("%M"))) return timeStr;
if (resolveTimePhrase(Zstr("sec" ), Zstr("%S"))) return timeStr;
- //check domain-specific extensions
- {
- auto it = std::find_if(ext.begin(), ext.end(), [&](const std::pair<Zstring, Zstring>& p) { return ciEqual(macro, p.first); });
- if (it != ext.end())
- return it->second;
- }
-
//try to resolve as environment variable
if (Opt<Zstring> value = getEnvironmentVar(macro))
return *value;
@@ -129,9 +121,10 @@ Opt<Zstring> resolveMacro(const Zstring& macro, //macro without %-characters }
const Zchar MACRO_SEP = Zstr('%');
+}
//returns expanded or original string
-Zstring expandMacros(const Zstring& text, const std::vector<std::pair<Zstring, Zstring>>& ext)
+Zstring zen::expandMacros(const Zstring& text)
{
if (contains(text, MACRO_SEP))
{
@@ -142,18 +135,14 @@ Zstring expandMacros(const Zstring& text, const std::vector<std::pair<Zstring, Z Zstring potentialMacro = beforeFirst(rest, MACRO_SEP, IF_MISSING_RETURN_NONE);
Zstring postfix = afterFirst (rest, MACRO_SEP, IF_MISSING_RETURN_NONE); //text == prefix + MACRO_SEP + potentialMacro + MACRO_SEP + postfix
- if (Opt<Zstring> value = resolveMacro(potentialMacro, ext))
- return prefix + *value + expandMacros(postfix, ext);
+ if (Opt<Zstring> value = tryResolveMacro(potentialMacro))
+ return prefix + *value + expandMacros(postfix);
else
- return prefix + MACRO_SEP + potentialMacro + expandMacros(MACRO_SEP + postfix, ext);
+ return prefix + MACRO_SEP + potentialMacro + expandMacros(MACRO_SEP + postfix);
}
}
return text;
}
-}
-
-
-Zstring zen::expandMacros(const Zstring& text) { return ::expandMacros(text, std::vector<std::pair<Zstring, Zstring>>()); }
namespace
@@ -270,7 +259,7 @@ Zstring zen::getResolvedFilePath(const Zstring& pathPhrase) //noexcept path = resolveRelativePath(path);
//remove trailing slash, unless volume root:
- if (Opt<PathComponents> pc = getPathComponents(path))
+ if (Opt<PathComponents> pc = parsePathComponents(path))
{
//keep this brace for GCC: -Wparentheses
if (pc->relPath.empty())
diff --git a/FreeFileSync/Source/lib/versioning.cpp b/FreeFileSync/Source/lib/versioning.cpp index 40382fb7..fba7c539 100755 --- a/FreeFileSync/Source/lib/versioning.cpp +++ b/FreeFileSync/Source/lib/versioning.cpp @@ -107,8 +107,8 @@ void moveExistingItemToVersioning(const AbstractPath& sourcePath, const Abstract auto fixedTargetPathIssues = [&] //throw FileError
{
- Opt<AFS::PathDetails> pd;
- try { pd = AFS::getPathDetails(targetPath); /*throw FileError*/ }
+ Opt<AFS::PathStatus> pd;
+ try { pd = AFS::getPathStatus(targetPath); /*throw FileError*/ }
catch (FileError&) {} //previous exception is more relevant
if (pd)
@@ -233,7 +233,7 @@ void FileVersioner::revisionFolder(const AbstractPath& folderPath, const Zstring {
if (Opt<AFS::ItemType> type = AFS::getItemTypeIfExists(folderPath)) //throw FileError
{
- if (*type == AFS::ItemType::SYMLINK) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well!
+ if (*type == AFS::ItemType::SYMLINK) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well!
{
const AbstractPath targetPath = generateVersionedPath(relativePath);
if (onBeforeFileMove)
diff --git a/FreeFileSync/Source/lib/versioning.h b/FreeFileSync/Source/lib/versioning.h index 26222bbb..037834a2 100755 --- a/FreeFileSync/Source/lib/versioning.h +++ b/FreeFileSync/Source/lib/versioning.h @@ -42,7 +42,7 @@ public: throw std::logic_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 10,000!
- throw FileError(_("Unable to create time stamp for versioning:") + L" \"" + utfCvrtTo<std::wstring>(timeStamp_) + L"\"");
+ throw FileError(_("Unable to create time stamp for versioning:") + L" \"" + utfTo<std::wstring>(timeStamp_) + L"\"");
}
bool revisionFile(const AbstractPath& filePath, //throw FileError; return "false" if file is not existing
diff --git a/FreeFileSync/Source/structures.cpp b/FreeFileSync/Source/structures.cpp index b3e5b7ef..bd64ab7c 100755 --- a/FreeFileSync/Source/structures.cpp +++ b/FreeFileSync/Source/structures.cpp @@ -21,7 +21,7 @@ std::vector<unsigned int> zen::fromTimeShiftPhrase(const std::wstring& timeShift replace(tmp, L'-', L""); //there is no negative shift => treat as positive!
std::set<unsigned int> minutes;
- for (const std::wstring& part : split(tmp, L','))
+ for (const std::wstring& part : split(tmp, L',', SplitType::SKIP_EMPTY))
{
if (contains(part, L':'))
minutes.insert(stringTo<unsigned int>(beforeFirst(part, L':', IF_MISSING_RETURN_NONE)) * 60 +
diff --git a/FreeFileSync/Source/synchronization.cpp b/FreeFileSync/Source/synchronization.cpp index 1f99f523..37c391d1 100755 --- a/FreeFileSync/Source/synchronization.cpp +++ b/FreeFileSync/Source/synchronization.cpp @@ -5,8 +5,10 @@ // *****************************************************************************
#include "synchronization.h"
+#include <tuple>
#include <zen/process_priority.h>
#include <zen/perf.h>
+#include "algorithm.h"
#include "lib/db_file.h"
#include "lib/dir_exist_async.h"
#include "lib/status_handler_impl.h"
@@ -39,7 +41,7 @@ SyncStatistics::SyncStatistics(const FolderComparison& folderCmp) }
-SyncStatistics::SyncStatistics(const HierarchyObject& hierObj)
+SyncStatistics::SyncStatistics(const ContainerObject& hierObj)
{
recurse(hierObj);
}
@@ -53,7 +55,7 @@ SyncStatistics::SyncStatistics(const FilePair& file) inline
-void SyncStatistics::recurse(const HierarchyObject& hierObj)
+void SyncStatistics::recurse(const ContainerObject& hierObj)
{
for (const FilePair& file : hierObj.refSubFiles())
processFile(file);
@@ -591,7 +593,7 @@ public: }
private:
- void recurse(const HierarchyObject& hierObj)
+ void recurse(const ContainerObject& hierObj)
{
//don't process directories
@@ -696,9 +698,9 @@ private: template <SelectedSide side>
void manageFileMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError
- void runZeroPass(HierarchyObject& hierObj);
+ void runZeroPass(ContainerObject& hierObj);
template <PassNo pass>
- void runPass(HierarchyObject& hierObj); //throw X
+ void runPass(ContainerObject& hierObj); //throw X
void synchronizeFile(FilePair& file);
template <SelectedSide side> void synchronizeFileInt(FilePair& file, SyncOperation syncOp);
@@ -833,7 +835,7 @@ void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj, sourceObj.isFollowedSymlink<side>());
FilePair& tempFile = sourceObj.base().addSubFile<side>(afterLast(sourceRelPathTmp, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL), descrSource);
- static_assert(IsSameType<FixedList<FilePair>, HierarchyObject::FileList>::value,
+ static_assert(IsSameType<FixedList<FilePair>, ContainerObject::FileList>::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"!
//note: this new item is *not* considered at the end of 0th pass because "!sourceWillBeDeleted && !haveNameClash"
@@ -931,7 +933,7 @@ void SynchronizeFolderPair::manageFileMove(FilePair& sourceFile, //search for file move-operations
-void SynchronizeFolderPair::runZeroPass(HierarchyObject& hierObj)
+void SynchronizeFolderPair::runZeroPass(ContainerObject& hierObj)
{
for (FilePair& file : hierObj.refSubFiles())
{
@@ -1107,7 +1109,7 @@ SynchronizeFolderPair::PassNo SynchronizeFolderPair::getPass(const FolderPair& f template <SynchronizeFolderPair::PassNo pass>
-void SynchronizeFolderPair::runPass(HierarchyObject& hierObj) //throw X
+void SynchronizeFolderPair::runPass(ContainerObject& hierObj) //throw X
{
//synchronize files:
for (FilePair& file : hierObj.refSubFiles())
@@ -1161,7 +1163,7 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn return; //if parent directory creation failed, there's no reason to show more errors!
//can't use "getAbstractPath<sideTrg>()" as file name is not available!
- const AbstractPath targetPath = AFS::appendRelPath(file.base().getAbstractPath<sideTrg>(), file.getRelativePath<sideSrc>());
+ const AbstractPath targetPath = file.getAbstractPath<sideTrg>();
reportInfo(txtCreatingFile, AFS::getDisplayPath(targetPath));
StatisticsReporter statReporter(1, file.getFileSize<sideSrc>(), procCallback_);
@@ -1222,7 +1224,7 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn (moveSource->getSyncOperation() == SO_MOVE_RIGHT_SOURCE && moveTarget->getSyncOperation() == SO_MOVE_RIGHT_TARGET && sideTrg == RIGHT_SIDE));
const AbstractPath oldPath = moveSource->getAbstractPath<sideTrg>();
- const AbstractPath newPath = AFS::appendRelPath(moveTarget->base().getAbstractPath<sideTrg>(), moveTarget->getRelativePath<sideSrc>());
+ const AbstractPath newPath = moveTarget->getAbstractPath<sideTrg>();
reportInfo(txtMovingFile, AFS::getDisplayPath(oldPath), AFS::getDisplayPath(newPath));
@@ -1252,7 +1254,7 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn case SO_OVERWRITE_RIGHT:
{
//respect differences in case of source object:
- const AbstractPath targetPathLogical = AFS::appendRelPath(file.base().getAbstractPath<sideTrg>(), file.getRelativePath<sideSrc>());
+ const AbstractPath targetPathLogical = AFS::appendRelPath(file.parent().getAbstractPath<sideTrg>(), file.getItemName<sideSrc>());
AbstractPath targetPathResolvedOld = file.getAbstractPath<sideTrg>(); //support change in case when syncing to case-sensitive SFTP on Windows!
AbstractPath targetPathResolvedNew = targetPathLogical;
@@ -1271,7 +1273,7 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn auto onDeleteTargetFile = [&] //delete target at appropriate time
{
- reportStatus(this->getDelHandling<sideTrg>().getTxtRemovingFile(), AFS::getDisplayPath(targetPathResolvedOld));
+ //reportStatus(this->getDelHandling<sideTrg>().getTxtRemovingFile(), AFS::getDisplayPath(targetPathResolvedOld)); -> superfluous/confuses user
this->getDelHandling<sideTrg>().removeFileWithCallback(targetPathResolvedOld, file.getPairRelativePath(), [] {}, notifyUnbufferedIO); //throw FileError;
//no (logical) item count update desired - but total byte count may change, e.g. move(copy) deleted file to versioning dir
@@ -1280,8 +1282,7 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn //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 (!failSafeFileCopy_)
- reportStatus(txtOverwritingFile, AFS::getDisplayPath(targetPathResolvedOld)); //restore status text copy file
+ //=> if failSafeFileCopy_ : don't run callbacks that could throw
};
const AFS::FileAttribAfterCopy newAttr = copyFileWithCallback(file.getAbstractPath<sideSrc>(),
@@ -1311,7 +1312,7 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn assert(file.getItemName<sideTrg>() != file.getItemName<sideSrc>());
if (file.getItemName<sideTrg>() != file.getItemName<sideSrc>()) //have difference in case?
AFS::renameItem(file.getAbstractPath<sideTrg>(), //throw FileError, (ErrorDifferentVolume)
- AFS::appendRelPath(file.base().getAbstractPath<sideTrg>(), file.getRelativePath<sideSrc>()));
+ AFS::appendRelPath(file.parent().getAbstractPath<sideTrg>(), file.getItemName<sideSrc>()));
#if 0 //changing file time without copying content is not justified after CompareVariant::SIZE finds "equal" files! similar issue with CompareVariant::TIME_SIZE and FileTimeTolerance == -1
//Bonus: some devices don't support setting (precise) file times anyway, e.g. FAT or MTP!
@@ -1320,7 +1321,6 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn //- do NOT read *current* source file time, but use buffered value which corresponds to time of comparison!
AFS::setModTime(file.getAbstractPath<sideTrg>(), file.getLastWriteTime<sideSrc>()); //throw FileError
#endif
-
statReporter.reportDelta(1, 0);
//-> both sides *should* be completely equal now...
@@ -1377,7 +1377,7 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& symlink, SyncOperati if (parentFolder->isEmpty<sideTrg>()) //BaseFolderPair 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 AbstractPath targetPath = AFS::appendRelPath(symlink.base().getAbstractPath<sideTrg>(), symlink.getRelativePath<sideSrc>());
+ const AbstractPath targetPath = symlink.getAbstractPath<sideTrg>();
reportInfo(txtCreatingLink, AFS::getDisplayPath(targetPath));
StatisticsReporter statReporter(1, 0, procCallback_);
@@ -1436,7 +1436,7 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& symlink, SyncOperati //reportStatus(txtOverwritingLink, AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>())); //restore status text
AFS::copySymlink(symlink.getAbstractPath<sideSrc>(),
- AFS::appendRelPath(symlink.base().getAbstractPath<sideTrg>(), symlink.getRelativePath<sideSrc>()), //respect differences in case of source object
+ AFS::appendRelPath(symlink.parent().getAbstractPath<sideTrg>(), symlink.getItemName<sideSrc>()), //respect differences in case of source object
copyFilePermissions_); //throw FileError
statReporter.reportDelta(1, 0); //we model "delete + copy" as ONE logical operation
@@ -1456,7 +1456,7 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& symlink, SyncOperati if (symlink.getItemName<sideTrg>() != symlink.getItemName<sideSrc>()) //have difference in case?
AFS::renameItem(symlink.getAbstractPath<sideTrg>(), //throw FileError, (ErrorDifferentVolume)
- AFS::appendRelPath(symlink.base().getAbstractPath<sideTrg>(), symlink.getRelativePath<sideSrc>()));
+ AFS::appendRelPath(symlink.parent().getAbstractPath<sideTrg>(), symlink.getItemName<sideSrc>()));
//if (symlink.getLastWriteTime<sideTrg>() != symlink.getLastWriteTime<sideSrc>())
// //- no need to call sameFileTime() or respect 2 second FAT/FAT32 precision in this comparison
@@ -1516,7 +1516,7 @@ void SynchronizeFolderPair::synchronizeFolderInt(FolderPair& folder, SyncOperati if (parentFolder->isEmpty<sideTrg>()) //BaseFolderPair 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 AbstractPath targetPath = AFS::appendRelPath(folder.base().getAbstractPath<sideTrg>(), folder.getRelativePath<sideSrc>());
+ const AbstractPath targetPath = folder.getAbstractPath<sideTrg>();
reportInfo(txtCreatingFolder, AFS::getDisplayPath(targetPath));
//shallow-"copying" a folder might not fail if source is missing, so we need to check this first:
@@ -1587,7 +1587,7 @@ void SynchronizeFolderPair::synchronizeFolderInt(FolderPair& folder, SyncOperati assert(folder.getItemName<sideTrg>() != folder.getItemName<sideSrc>());
if (folder.getItemName<sideTrg>() != folder.getItemName<sideSrc>()) //have difference in case?
AFS::renameItem(folder.getAbstractPath<sideTrg>(), //throw FileError, (ErrorDifferentVolume)
- AFS::appendRelPath(folder.base().getAbstractPath<sideTrg>(), folder.getRelativePath<sideSrc>()));
+ AFS::appendRelPath(folder.parent().getAbstractPath<sideTrg>(), folder.getItemName<sideSrc>()));
//copyFileTimes -> useless: modification time changes with each child-object creation/deletion
statReporter.reportDelta(1, 0);
@@ -1744,13 +1744,6 @@ bool createBaseFolder(BaseFolderPair& baseFolder, int folderAccessTimeout, Proce }
-struct ReadWriteCount
-{
- size_t reads = 0;
- size_t writes = 0;
-};
-
-
enum class FolderPairJobType
{
PROCESS,
@@ -1829,33 +1822,7 @@ void zen::synchronize(const TimeComp& timeStamp, std::vector<SyncStatistics::ConflictInfo> unresolvedConflicts;
- //aggregate information
- std::map<AbstractPath, ReadWriteCount, AFS::LessAbstractPath> dirReadWriteCount; //count read/write accesses
- std::for_each(begin(folderCmp), end(folderCmp),
- [&](const BaseFolderPair& baseFolder)
- {
- //create all entries first! otherwise counting accesses would be too complex during later inserts!
-
- if (!AFS::isNullPath(baseFolder.getAbstractPath<LEFT_SIDE>())) //empty folder is always dependent => exclude!
- dirReadWriteCount[baseFolder.getAbstractPath<LEFT_SIDE>()];
- if (!AFS::isNullPath(baseFolder.getAbstractPath<RIGHT_SIDE>()))
- dirReadWriteCount[baseFolder.getAbstractPath<RIGHT_SIDE>()];
- });
-
- auto incReadCount = [&](const AbstractPath& baseFolderPath)
- {
- if (!AFS::isNullPath(baseFolderPath))
- for (auto& item : dirReadWriteCount)
- if (AFS::havePathDependency(baseFolderPath, item.first))
- ++item.second.reads;
- };
- auto incWriteCount = [&](const AbstractPath& baseFolderPath)
- {
- if (!AFS::isNullPath(baseFolderPath))
- for (auto& item : dirReadWriteCount)
- if (AFS::havePathDependency(baseFolderPath, item.first))
- ++item.second.writes;
- };
+ std::vector<std::tuple<AbstractPath, const HardFilter*, bool /*write access*/>> readWriteCheckBaseFolders;
std::vector<std::pair<AbstractPath, AbstractPath>> significantDiffPairs;
@@ -1864,6 +1831,9 @@ void zen::synchronize(const TimeComp& timeStamp, //status of base directories which are set to DeletionPolicy::RECYCLER (and contain actual items to be deleted)
std::map<AbstractPath, bool, AFS::LessAbstractPath> recyclerSupported; //expensive to determine on Win XP => buffer + check recycle bin existence only once per base folder!
+ std::set<AbstractPath, AFS::LessAbstractPath> verCheckVersioningPaths;
+ std::vector<std::pair<AbstractPath, const HardFilter*>> verCheckBaseFolderPaths; //hard filter creates new logical hierarchies for otherwise equal AbstractPath...
+
//start checking folder pairs
for (auto itBase = begin(folderCmp); itBase != end(folderCmp); ++itBase)
{
@@ -1899,26 +1869,6 @@ void zen::synchronize(const TimeComp& timeStamp, folderPairStat.updateCount<RIGHT_SIDE>() +
folderPairStat.deleteCount<RIGHT_SIDE>() > 0;
- //aggregate information of folders used by multiple pairs in read/write access
- if (!AFS::havePathDependency(baseFolder.getAbstractPath<LEFT_SIDE >(),
- baseFolder.getAbstractPath<RIGHT_SIDE>()))
- {
- if (writeLeft)
- incWriteCount(baseFolder.getAbstractPath<LEFT_SIDE>());
- else if (writeRight)
- incReadCount (baseFolder.getAbstractPath<LEFT_SIDE>());
-
- if (writeRight)
- incWriteCount(baseFolder.getAbstractPath<RIGHT_SIDE>());
- else if (writeLeft)
- incReadCount (baseFolder.getAbstractPath<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(baseFolder.getAbstractPath<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 ((AFS::isNullPath(baseFolder.getAbstractPath< LEFT_SIDE>()) && (writeLeft || folderPairCfg.saveSyncDB_)) ||
(AFS::isNullPath(baseFolder.getAbstractPath<RIGHT_SIDE>()) && (writeRight || folderPairCfg.saveSyncDB_)))
@@ -1960,18 +1910,32 @@ void zen::synchronize(const TimeComp& timeStamp, continue;
}
- //check if user-defined directory for deletion was specified
if (folderPairCfg.handleDeletion == DeletionPolicy::VERSIONING)
{
- if (trimCpy(folderPairCfg.versioningFolderPhrase).empty())
+ const AbstractPath versioningFolderPath = createAbstractPath(folderPairCfg.versioningFolderPhrase);
+
+ //check if user-defined directory for deletion was specified
+ if (AFS::isNullPath(versioningFolderPath))
{
//should never arrive here: already checked in SyncCfgDialog
callback.reportFatalError(_("Please enter a target folder for versioning."));
jobType[folderIndex] = FolderPairJobType::SKIP;
continue;
}
+ //===============================================================================================
+ //================ end of checks that may skip folder pairs => begin of warnings ================
+ //===============================================================================================
+
+ //prepare: check if versioning path itself will be synchronized (and was not excluded via filter)
+ verCheckVersioningPaths.insert(versioningFolderPath);;
+ verCheckBaseFolderPaths.emplace_back(baseFolder.getAbstractPath<LEFT_SIDE >(), &baseFolder.getFilter());
+ verCheckBaseFolderPaths.emplace_back(baseFolder.getAbstractPath<RIGHT_SIDE>(), &baseFolder.getFilter());
}
+ //prepare: check if folders are used by multiple pairs in read/write access
+ readWriteCheckBaseFolders.emplace_back(baseFolder.getAbstractPath<LEFT_SIDE >(), &baseFolder.getFilter(), writeLeft);
+ readWriteCheckBaseFolders.emplace_back(baseFolder.getAbstractPath<RIGHT_SIDE>(), &baseFolder.getFilter(), writeRight);
+
//check if more than 50% of total number of files/dirs are to be created/overwritten/deleted
if (!AFS::isNullPath(baseFolder.getAbstractPath< LEFT_SIDE>()) &&
!AFS::isNullPath(baseFolder.getAbstractPath<RIGHT_SIDE>()))
@@ -2015,7 +1979,6 @@ void zen::synchronize(const TimeComp& timeStamp, recyclerSupported.emplace(baseFolderPath, recSupported);
}
};
-
if (folderPairCfg.handleDeletion == DeletionPolicy::RECYCLER)
{
if (folderPairStat.expectPhysicalDeletion<LEFT_SIDE>())
@@ -2034,7 +1997,7 @@ void zen::synchronize(const TimeComp& timeStamp, for (const SyncStatistics::ConflictInfo& item : unresolvedConflicts) //show *all* conflicts in warning message
msg += L"\n\n" + fmtPath(item.relPath) + L": " + item.msg;
- callback.reportWarning(msg, warnings.warningUnresolvedConflicts);
+ callback.reportWarning(msg, warnings.warnUnresolvedConflicts);
}
//check if user accidentally selected wrong directories for sync
@@ -2047,7 +2010,7 @@ void zen::synchronize(const TimeComp& timeStamp, AFS::getDisplayPath(item.first) + L" <-> " + L"\n" +
AFS::getDisplayPath(item.second);
- callback.reportWarning(msg, warnings.warningSignificantDifference);
+ callback.reportWarning(msg, warnings.warnSignificantDifference);
}
//check for sufficient free diskspace
@@ -2060,36 +2023,72 @@ void zen::synchronize(const TimeComp& timeStamp, _("Required:") + L" " + filesizeToShortString(item.second.first) + L"\n" +
_("Available:") + L" " + filesizeToShortString(item.second.second);
- callback.reportWarning(msg, warnings.warningNotEnoughDiskSpace);
+ callback.reportWarning(msg, warnings.warnNotEnoughDiskSpace);
}
//windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong
{
- std::wstring dirListMissingRecycler;
+ std::wstring msg;
for (const auto& item : recyclerSupported)
if (!item.second)
- dirListMissingRecycler += L"\n" + AFS::getDisplayPath(item.first);
+ msg += L"\n" + AFS::getDisplayPath(item.first);
- 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);
+ if (!msg.empty())
+ callback.reportWarning(_("The recycle bin is not available for the following folders. Files will be deleted permanently instead:") + L"\n" + msg,
+ warnings.warnRecyclerMissing);
}
//check if folders are used by multiple pairs in read/write access
{
- std::vector<AbstractPath> 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
- conflictFolders.push_back(item.first);
+ std::set<AbstractPath, AFS::LessAbstractPath> dependentFolders;
+
+ //race condition := multiple accesses of which at least one is a write
+ for (auto it = readWriteCheckBaseFolders.begin(); it != readWriteCheckBaseFolders.end(); ++it)
+ if (std::get<bool>(*it)) //write access
+ for (auto it2 = readWriteCheckBaseFolders.begin(); it2 != readWriteCheckBaseFolders.end(); ++it2)
+ if (!std::get<bool>(*it2) || it < it2) //avoid duplicate comparisons
+ if (Opt<PathDependency> pd = getPathDependency(std::get<AbstractPath>(*it), *std::get<const HardFilter*>(*it),
+ std::get<AbstractPath>(*it2), *std::get<const HardFilter*>(*it2)))
+ {
+ dependentFolders.insert(pd->basePathParent);
+ dependentFolders.insert(pd->basePathChild);
+ }
+
+ if (!dependentFolders.empty())
+ {
+ std::wstring msg = _("Some files will be synchronized as part of more than one base folder.") + L"\n" +
+ _("To avoid conflicts, set up exclude filters so that each updated file is considered by only one base folder.") + L"\n";
- if (!conflictFolders.empty())
+ for (const AbstractPath& baseFolderPath : dependentFolders)
+ msg += L"\n" + AFS::getDisplayPath(baseFolderPath);
+
+ callback.reportWarning(msg, warnings.warnDependentBaseFolders);
+ }
+ }
+
+ //check if versioning path itself will be synchronized (and was not excluded via filter)
+ {
+ std::wstring msg;
+ for (const AbstractPath& versioningFolderPath : verCheckVersioningPaths)
{
- std::wstring msg = _("Multiple folder pairs write to a common subfolder. Please review your configuration.") + L"\n";
- for (const AbstractPath& folderPath : conflictFolders)
- msg += L"\n" + AFS::getDisplayPath(folderPath);
+ std::map<AbstractPath, std::wstring, AFS::LessAbstractPath> uniqueMsgs; //=> at most one msg per base folder (*and* per versioningFolderPath)
+
+ for (const auto& item : verCheckBaseFolderPaths) //may contain duplicate paths, but with *different* hard filter!
+ if (Opt<PathDependency> pd = getPathDependency(versioningFolderPath, NullFilter(), item.first, *item.second))
+ {
+ std::wstring line = L"\n\n" + _("Versioning folder:") + L" \t" + AFS::getDisplayPath(versioningFolderPath) +
+ L"\n" + _("Base folder:") + L" \t" + AFS::getDisplayPath(item.first);
+ if (AFS::equalAbstractPath(pd->basePathParent, item.first) && !pd->relPath.empty())
+ line += L"\n" + _("Exclude:") + L" \t" + utfTo<std::wstring>(FILE_NAME_SEPARATOR + pd->relPath + FILE_NAME_SEPARATOR);
- callback.reportWarning(msg, warnings.warningFolderPairRaceCondition);
+ uniqueMsgs[item.first] = line;
+ }
+ for (const auto& item : uniqueMsgs)
+ msg += item.second;
}
+ if (!msg.empty())
+ callback.reportWarning(_("The versioning folder will be synchronized as part of a base folder.") + L"\n" +
+ _("You may want to exclude it from synchronization via filter.") + msg, warnings.warnVersioningFolderPartOfSync);
}
//-------------------end of basic checks------------------------------------------
@@ -2208,7 +2207,7 @@ void zen::synchronize(const TimeComp& timeStamp, }
catch (const std::exception& e)
{
- callback.reportFatalError(utfCvrtTo<std::wstring>(e.what()));
+ callback.reportFatalError(utfTo<std::wstring>(e.what()));
callback.abortProcessNow(); //throw X
throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
}
diff --git a/FreeFileSync/Source/synchronization.h b/FreeFileSync/Source/synchronization.h index 3ed21964..b9e8041c 100755 --- a/FreeFileSync/Source/synchronization.h +++ b/FreeFileSync/Source/synchronization.h @@ -20,7 +20,7 @@ class SyncStatistics //this class counts *logical* operations, (create, update, //-> note the fundamental difference compared to counting disk accesses!
public:
SyncStatistics(const FolderComparison& folderCmp);
- SyncStatistics(const HierarchyObject& hierObj);
+ SyncStatistics(const ContainerObject& hierObj);
SyncStatistics(const FilePair& file);
template <SelectedSide side>
@@ -51,7 +51,7 @@ public: const std::vector<ConflictInfo>& getConflicts() const { return conflictMsgs_; }
private:
- void recurse(const HierarchyObject& hierObj);
+ void recurse(const ContainerObject& hierObj);
void processFile (const FilePair& file);
void processLink (const SymlinkPair& link);
diff --git a/FreeFileSync/Source/ui/batch_config.cpp b/FreeFileSync/Source/ui/batch_config.cpp index 73bafffc..62edd620 100755 --- a/FreeFileSync/Source/ui/batch_config.cpp +++ b/FreeFileSync/Source/ui/batch_config.cpp @@ -137,7 +137,7 @@ XmlBatchConfig BatchDialog::getConfig() const //load structure with batch settings "batchCfg"
batchCfg.runMinimized = m_checkBoxRunMinimized->GetValue();
- batchCfg.logFolderPathPhrase = utfCvrtTo<Zstring>(logfileDir->getPath());
+ batchCfg.logFolderPathPhrase = utfTo<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 c2a6057a..52567793 100755 --- a/FreeFileSync/Source/ui/batch_status_handler.cpp +++ b/FreeFileSync/Source/ui/batch_status_handler.cpp @@ -40,9 +40,9 @@ std::unique_ptr<AFS::OutputStream> prepareNewLogfile(const AbstractPath& logFold //=> too many issues, most notably cmd.exe is not Unicode-awere: http://www.freefilesync.org/forum/viewtopic.php?t=1679
//assemble logfile name
- Zstring body = utfCvrtTo<Zstring>(jobName) + Zstr(" ") + formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp);
+ Zstring body = utfTo<Zstring>(jobName) + Zstr(" ") + formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp);
if (!status.empty())
- body += utfCvrtTo<Zstring>(L" [" + status + L"]");
+ body += utfTo<Zstring>(L" [" + status + L"]");
//ensure uniqueness; avoid file system race-condition!
Zstring logFileName = body + Zstr(".log");
@@ -72,7 +72,7 @@ struct LogTraverserCallback: public AFS::TraverserCallback void onFile(const FileInfo& fi) override
{
- if (ciStartsWith(fi.itemName, prefix_) && endsWith(fi.itemName, Zstr(".log")))
+ if (startsWith(fi.itemName, prefix_, CmpFilePath() /*even on Linux!*/) && endsWith(fi.itemName, Zstr(".log"), CmpFilePath()))
logFileNames_.push_back(fi.itemName);
if (onUpdateStatus_) onUpdateStatus_();
@@ -102,7 +102,7 @@ private: void limitLogfileCount(const AbstractPath& logFolderPath, const std::wstring& jobname, size_t maxCount, const std::function<void()>& onUpdateStatus) //throw FileError
{
- const Zstring prefix = utfCvrtTo<Zstring>(jobname);
+ const Zstring prefix = utfTo<Zstring>(jobname);
LogTraverserCallback lt(prefix, onUpdateStatus); //traverse source directory one level deep
AFS::traverseFolder(logFolderPath, lt);
@@ -165,7 +165,7 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress, //...
//if (logFile)
- // ::wxSetEnv(L"logfile", utfCvrtTo<wxString>(logFile->getFilename()));
+ // ::wxSetEnv(L"logfile", utfTo<wxString>(logFile->getFilename()));
}
@@ -287,7 +287,7 @@ BatchStatusHandler::~BatchStatusHandler() //----------------- write results into LastSyncs.log------------------------
try
{
- saveToLastSyncsLog(summary, errorLog_, lastSyncsLogFileSizeMax_, OnUpdateLogfileStatusNoThrow(*this, utfCvrtTo<std::wstring>(getLastSyncsLogfilePath()))); //throw FileError
+ saveToLastSyncsLog(summary, errorLog_, lastSyncsLogFileSizeMax_, OnUpdateLogfileStatusNoThrow(*this, utfTo<std::wstring>(getLastSyncsLogfilePath()))); //throw FileError
}
catch (FileError&) { assert(false); }
diff --git a/FreeFileSync/Source/ui/column_attr.h b/FreeFileSync/Source/ui/column_attr.h index 9c4bd2f2..40fe8f47 100755 --- a/FreeFileSync/Source/ui/column_attr.h +++ b/FreeFileSync/Source/ui/column_attr.h @@ -37,7 +37,7 @@ std::vector<ColumnAttributeRim> getDefaultColumnAttributesLeft() {
{ ColumnTypeRim::ITEM_PATH, -80, 1, true }, //stretch to full width and substract sum of fixed-size widths!
{ ColumnTypeRim::EXTENSION, 60, 0, false },
- { ColumnTypeRim::DATE, 112, 0, false },
+ { ColumnTypeRim::DATE, 140, 0, false },
{ ColumnTypeRim::SIZE, 80, 0, true },
};
}
@@ -45,13 +45,7 @@ std::vector<ColumnAttributeRim> getDefaultColumnAttributesLeft() inline
std::vector<ColumnAttributeRim> getDefaultColumnAttributesRight()
{
- return
- {
- { ColumnTypeRim::ITEM_PATH, -80, 1, true },
- { ColumnTypeRim::EXTENSION, 60, 0, false },
- { ColumnTypeRim::DATE, 112, 0, false },
- { ColumnTypeRim::SIZE, 80, 0, true },
- };
+ return getDefaultColumnAttributesLeft(); //*currently* same default
}
enum class ItemPathFormat
diff --git a/FreeFileSync/Source/ui/custom_grid.cpp b/FreeFileSync/Source/ui/custom_grid.cpp index ca1282a5..25300cf7 100755 --- a/FreeFileSync/Source/ui/custom_grid.cpp +++ b/FreeFileSync/Source/ui/custom_grid.cpp @@ -14,7 +14,6 @@ #include <zen/format_unit.h>
#include <zen/scope_guard.h>
#include <wx+/tooltip.h>
-#include <wx+/string_conv.h>
#include <wx+/rtl.h>
#include <wx+/image_tools.h>
#include <wx+/image_resources.h>
@@ -362,21 +361,24 @@ private: {
value = [&]
{
+ if (folder.isEmpty<side>())
+ return std::wstring();
+
switch (colTypeRim)
{
case ColumnTypeRim::ITEM_PATH:
switch (itemPathFormat)
{
case ItemPathFormat::FULL_PATH:
- return folder.isEmpty<side>() ? std::wstring() : AFS::getDisplayPath(folder.getAbstractPath<side>());
+ return AFS::getDisplayPath(folder.getAbstractPath<side>());
case ItemPathFormat::RELATIVE_PATH:
- return utfCvrtTo<std::wstring>(folder.getRelativePath<side>());
+ return utfTo<std::wstring>(folder.getRelativePath<side>());
case ItemPathFormat::ITEM_NAME:
- return utfCvrtTo<std::wstring>(folder.getItemName<side>());
+ return utfTo<std::wstring>(folder.getItemName<side>());
}
break;
case ColumnTypeRim::SIZE:
- return folder.isEmpty<side>() ? std::wstring() : L"<" + _("Folder") + L">";
+ return L"<" + _("Folder") + L">";
case ColumnTypeRim::DATE:
return std::wstring();
case ColumnTypeRim::EXTENSION:
@@ -391,26 +393,29 @@ private: {
value = [&]
{
+ if (file.isEmpty<side>())
+ return std::wstring();
+
switch (colTypeRim)
{
case ColumnTypeRim::ITEM_PATH:
switch (itemPathFormat)
{
case ItemPathFormat::FULL_PATH:
- return file.isEmpty<side>() ? std::wstring() : AFS::getDisplayPath(file.getAbstractPath<side>());
+ return AFS::getDisplayPath(file.getAbstractPath<side>());
case ItemPathFormat::RELATIVE_PATH:
- return utfCvrtTo<std::wstring>(file.getRelativePath<side>());
+ return utfTo<std::wstring>(file.getRelativePath<side>());
case ItemPathFormat::ITEM_NAME:
- return utfCvrtTo<std::wstring>(file.getItemName<side>());
+ return utfTo<std::wstring>(file.getItemName<side>());
}
break;
case ColumnTypeRim::SIZE:
- //return file.isEmpty<side>() ? std::wstring() : utfCvrtTo<std::wstring>(file.getFileId<side>()); // -> test file id
- return file.isEmpty<side>() ? std::wstring() : toGuiString(file.getFileSize<side>());
+ //return utfTo<std::wstring>(file.getFileId<side>()); // -> test file id
+ return toGuiString(file.getFileSize<side>());
case ColumnTypeRim::DATE:
- return file.isEmpty<side>() ? std::wstring() : utcToLocalTimeString(file.getLastWriteTime<side>());
+ return utcToLocalTimeString(file.getLastWriteTime<side>());
case ColumnTypeRim::EXTENSION:
- return utfCvrtTo<std::wstring>(getFileExtension(file.getItemName<side>()));
+ return utfTo<std::wstring>(getFileExtension(file.getItemName<side>()));
}
assert(false);
return std::wstring();
@@ -421,25 +426,28 @@ private: {
value = [&]
{
+ if (symlink.isEmpty<side>())
+ return std::wstring();
+
switch (colTypeRim)
{
case ColumnTypeRim::ITEM_PATH:
switch (itemPathFormat)
{
case ItemPathFormat::FULL_PATH:
- return symlink.isEmpty<side>() ? std::wstring() : AFS::getDisplayPath(symlink.getAbstractPath<side>());
+ return AFS::getDisplayPath(symlink.getAbstractPath<side>());
case ItemPathFormat::RELATIVE_PATH:
- return utfCvrtTo<std::wstring>(symlink.getRelativePath<side>());
+ return utfTo<std::wstring>(symlink.getRelativePath<side>());
case ItemPathFormat::ITEM_NAME:
- return utfCvrtTo<std::wstring>(symlink.getItemName<side>());
+ return utfTo<std::wstring>(symlink.getItemName<side>());
}
break;
case ColumnTypeRim::SIZE:
- return symlink.isEmpty<side>() ? std::wstring() : L"<" + _("Symlink") + L">";
+ return L"<" + _("Symlink") + L">";
case ColumnTypeRim::DATE:
- return symlink.isEmpty<side>() ? std::wstring() : utcToLocalTimeString(symlink.getLastWriteTime<side>());
+ return utcToLocalTimeString(symlink.getLastWriteTime<side>());
case ColumnTypeRim::EXTENSION:
- return utfCvrtTo<std::wstring>(getFileExtension(symlink.getItemName<side>()));
+ return utfTo<std::wstring>(getFileExtension(symlink.getItemName<side>()));
}
assert(false);
return std::wstring();
@@ -734,7 +742,7 @@ private: {
toolTip = getGridDataView() && getGridDataView()->getFolderPairCount() > 1 ?
AFS::getDisplayPath(fsObj->getAbstractPath<side>()) :
- utfCvrtTo<std::wstring>(fsObj->getRelativePath<side>());
+ utfTo<std::wstring>(fsObj->getRelativePath<side>());
visitFSObject(*fsObj, [](const FolderPair& folder) {},
[&](const FilePair& file)
@@ -767,7 +775,7 @@ public: GridDataLeft(const std::shared_ptr<const zen::GridView>& gridDataView, Grid& grid) : GridDataRim<LEFT_SIDE>(gridDataView, grid) {}
void setNavigationMarker(std::unordered_set<const FileSystemObject*>&& markedFilesAndLinks,
- std::unordered_set<const HierarchyObject*>&& markedContainer)
+ std::unordered_set<const ContainerObject*>&& markedContainer)
{
markedFilesAndLinks_.swap(markedFilesAndLinks);
markedContainer_ .swap(markedContainer);
@@ -790,12 +798,12 @@ private: if (auto folder = dynamic_cast<const FolderPair*>(fsObj))
{
- if (markedContainer_.find(folder) != markedContainer_.end()) //mark directories which *are* the given HierarchyObject*
+ if (markedContainer_.find(folder) != markedContainer_.end()) //mark directories which *are* the given ContainerObject*
return true;
}
- //mark all objects which have the HierarchyObject as *any* matching ancestor
- const HierarchyObject* parent = &(fsObj->parent());
+ //mark all objects which have the ContainerObject as *any* matching ancestor
+ const ContainerObject* parent = &(fsObj->parent());
for (;;)
{
if (markedContainer_.find(parent) != markedContainer_.end())
@@ -820,7 +828,7 @@ private: }
std::unordered_set<const FileSystemObject*> markedFilesAndLinks_; //mark files/symlinks directly within a container
- std::unordered_set<const HierarchyObject*> markedContainer_; //mark full container including all child-objects
+ std::unordered_set<const ContainerObject*> markedContainer_; //mark full container including all child-objects
//DO NOT DEREFERENCE!!!! NOT GUARANTEED TO BE VALID!!!
};
@@ -1748,7 +1756,7 @@ void gridview::setScrollMaster(Grid& grid) void gridview::setNavigationMarker(Grid& gridLeft,
std::unordered_set<const FileSystemObject*>&& markedFilesAndLinks,
- std::unordered_set<const HierarchyObject*>&& markedContainer)
+ std::unordered_set<const ContainerObject*>&& markedContainer)
{
if (auto provLeft = dynamic_cast<GridDataLeft*>(gridLeft.getDataProvider()))
provLeft->setNavigationMarker(std::move(markedFilesAndLinks), std::move(markedContainer));
diff --git a/FreeFileSync/Source/ui/custom_grid.h b/FreeFileSync/Source/ui/custom_grid.h index 1e1abf39..f1cc5491 100755 --- a/FreeFileSync/Source/ui/custom_grid.h +++ b/FreeFileSync/Source/ui/custom_grid.h @@ -36,7 +36,7 @@ void setScrollMaster(Grid& grid); //mark rows selected in navigation/compressed tree and navigate to leading object
void setNavigationMarker(Grid& gridLeft,
std::unordered_set<const FileSystemObject*>&& markedFilesAndLinks,//mark files/symlinks directly within a container
- std::unordered_set<const HierarchyObject*>&& markedContainer); //mark full container including child-objects
+ std::unordered_set<const ContainerObject*>&& markedContainer); //mark full container including child-objects
}
wxBitmap getSyncOpImage(SyncOperation syncOp);
diff --git a/FreeFileSync/Source/ui/folder_history_box.cpp b/FreeFileSync/Source/ui/folder_history_box.cpp index 7a5a9f11..6bf7033f 100755 --- a/FreeFileSync/Source/ui/folder_history_box.cpp +++ b/FreeFileSync/Source/ui/folder_history_box.cpp @@ -7,7 +7,6 @@ #include "folder_history_box.h"
#include <list>
#include <zen/scope_guard.h>
-#include <wx+/string_conv.h>
#include "../lib/resolve_path.h"
#include <gtk/gtk.h>
@@ -62,18 +61,18 @@ void FolderHistoryBox::setValueAndUpdateList(const wxString& folderPathPhrase) std::vector<wxString> dirList;
{
//add some aliases to allow user changing to volume name and back, if possible
- std::vector<Zstring> aliases = getDirectoryAliases(toZ(folderPathPhrase)); //may block when resolving [<volume name>]
- std::transform(aliases.begin(), aliases.end(), std::back_inserter(dirList), [](const Zstring& str) { return utfCvrtTo<wxString>(str); });
+ std::vector<Zstring> aliases = getDirectoryAliases(utfTo<Zstring>(folderPathPhrase)); //may block when resolving [<volume name>]
+ std::transform(aliases.begin(), aliases.end(), std::back_inserter(dirList), [](const Zstring& str) { return utfTo<wxString>(str); });
}
if (sharedHistory_.get())
{
std::vector<Zstring> tmp = sharedHistory_->getList();
- std::sort(tmp.begin(), tmp.end(), LessNoCase() /*even on Linux*/);
+ std::sort(tmp.begin(), tmp.end(), LessNaturalSort() /*even on Linux*/);
if (!dirList.empty() && !tmp.empty())
dirList.push_back(FolderHistory::separationLine());
- std::transform(tmp.begin(), tmp.end(), std::back_inserter(dirList), [](const Zstring& str) { return utfCvrtTo<wxString>(str); });
+ std::transform(tmp.begin(), tmp.end(), std::back_inserter(dirList), [](const Zstring& str) { return utfTo<wxString>(str); });
}
//###########################################################################################
@@ -114,7 +113,7 @@ void FolderHistoryBox::OnKeyEvent(wxKeyEvent& event) //delete selected row
if (sharedHistory_.get())
- sharedHistory_->delItem(toZ(GetString(pos)));
+ sharedHistory_->delItem(utfTo<Zstring>(GetString(pos)));
SetString(pos, wxString()); //in contrast to "Delete(pos)", this one does not kill the drop-down list and gives a nice visual feedback!
this->SetValue(currentVal);
diff --git a/FreeFileSync/Source/ui/folder_history_box.h b/FreeFileSync/Source/ui/folder_history_box.h index 29c488b5..05c152de 100755 --- a/FreeFileSync/Source/ui/folder_history_box.h +++ b/FreeFileSync/Source/ui/folder_history_box.h @@ -35,7 +35,7 @@ public: void addItem(const Zstring& folderPathPhrase)
{
- if (folderPathPhrase.empty() || folderPathPhrase == zen::utfCvrtTo<Zstring>(separationLine()))
+ if (folderPathPhrase.empty() || folderPathPhrase == zen::utfTo<Zstring>(separationLine()))
return;
const Zstring nameTmp = zen::trimCpy(folderPathPhrase);
diff --git a/FreeFileSync/Source/ui/folder_selector.cpp b/FreeFileSync/Source/ui/folder_selector.cpp index 2234b3f6..9170e5a1 100755 --- a/FreeFileSync/Source/ui/folder_selector.cpp +++ b/FreeFileSync/Source/ui/folder_selector.cpp @@ -9,7 +9,6 @@ #include <zen/file_access.h>
#include <wx/dirdlg.h>
#include <wx/scrolwin.h>
-#include <wx+/string_conv.h>
#include <wx+/popup_dlg.h>
#include <wx+/context_menu.h>
#include <wx+/image_resources.h>
@@ -30,19 +29,19 @@ namespace void setFolderPathPhrase(const Zstring& folderPathPhrase, FolderHistoryBox* comboBox, wxWindow& tooltipWnd, wxStaticText* staticText) //pointers are optional
{
if (comboBox)
- comboBox->setValue(toWx(folderPathPhrase));
+ comboBox->setValue(utfTo<wxString>(folderPathPhrase));
const Zstring folderPathPhraseFmt = AFS::getInitPathPhrase(createAbstractPath(folderPathPhrase)); //noexcept
//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(folderPathPhraseFmt)); //who knows when the real bugfix reaches mere mortals via an official release...
+ tooltipWnd.SetToolTip(utfTo<wxString>(folderPathPhraseFmt)); //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(folderPathPhrase)), appendSeparator(folderPathPhraseFmt)) ?
- wxString(_("Drag && drop")) : toWx(folderPathPhraseFmt));
+ wxString(_("Drag && drop")) : utfTo<wxString>(folderPathPhraseFmt));
}
}
@@ -158,7 +157,7 @@ void FolderSelector::onFilesDropped(FileDropEvent& event) void FolderSelector::onEditFolderPath(wxCommandEvent& event)
{
- setFolderPathPhrase(toZ(event.GetString()), nullptr, folderComboBox_, staticText_);
+ setFolderPathPhrase(utfTo<Zstring>(event.GetString()), nullptr, folderComboBox_, staticText_);
wxCommandEvent dummy(EVENT_ON_FOLDER_MANUAL_EDIT);
ProcessEvent(dummy);
@@ -195,14 +194,14 @@ void FolderSelector::onSelectFolder(wxCommandEvent& event) }
//wxDirDialog internally uses lame-looking SHBrowseForFolder(); we better use IFileDialog() instead! (remembers size and position!)
- wxDirDialog dirPicker(&selectFolderButton_, _("Select a folder"), toWx(defaultFolderPath)); //put modal wxWidgets dialogs on stack: creating on freestore leads to memleak!
+ wxDirDialog dirPicker(&selectFolderButton_, _("Select a folder"), utfTo<wxString>(defaultFolderPath)); //put modal wxWidgets dialogs on stack: creating on freestore leads to memleak!
//-> following doesn't seem to do anything at all! still "Show hidden" is available as a context menu option:
//::gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dirPicker.m_widget), true /*show_hidden*/);
if (dirPicker.ShowModal() != wxID_OK)
return;
- const Zstring newFolderPathPhrase = toZ(dirPicker.GetPath());
+ const Zstring newFolderPathPhrase = utfTo<Zstring>(dirPicker.GetPath());
setFolderPathPhrase(newFolderPathPhrase, &folderComboBox_, folderComboBox_, staticText_);
@@ -219,7 +218,7 @@ void FolderSelector::onSelectAltFolder(wxCommandEvent& event) Zstring FolderSelector::getPath() const
{
- Zstring path = utfCvrtTo<Zstring>(folderComboBox_.GetValue());
+ Zstring path = utfTo<Zstring>(folderComboBox_.GetValue());
return path;
}
diff --git a/FreeFileSync/Source/ui/grid_view.cpp b/FreeFileSync/Source/ui/grid_view.cpp index 86873c52..5985eb96 100755 --- a/FreeFileSync/Source/ui/grid_view.cpp +++ b/FreeFileSync/Source/ui/grid_view.cpp @@ -8,6 +8,7 @@ #include "sorting.h"
#include "../synchronization.h"
#include <zen/stl_tools.h>
+#include <zen/perf.h>
using namespace zen;
@@ -52,24 +53,24 @@ void addNumbers(const FileSystemObject& fsObj, StatusResult& result) template <class Predicate>
void GridView::updateView(Predicate pred)
{
- viewRef.clear();
- rowPositions.clear();
- rowPositionsFirstChild.clear();
+ viewRef_.clear();
+ rowPositions_.clear();
+ rowPositionsFirstChild_.clear();
- for (const RefIndex& ref : sortedRef)
+ for (const RefIndex& ref : sortedRef_)
{
if (const FileSystemObject* fsObj = FileSystemObject::retrieve(ref.objId))
if (pred(*fsObj))
{
//save row position for direct random access to FilePair or FolderPair
- this->rowPositions.emplace(ref.objId, viewRef.size()); //costs: 0.28 µs per call - MSVC based on std::set
+ this->rowPositions_.emplace(ref.objId, viewRef_.size()); //costs: 0.28 µs per call - MSVC based on std::set
//"this->" required by two-pass lookup as enforced by GCC 4.7
//save row position to identify first child *on sorted subview* of FolderPair or BaseFolderPair in case latter are filtered out
- const HierarchyObject* parent = &fsObj->parent();
+ const ContainerObject* parent = &fsObj->parent();
for (;;) //map all yet unassociated parents to this row
{
- const auto rv = this->rowPositionsFirstChild.emplace(parent, viewRef.size());
+ const auto rv = this->rowPositionsFirstChild_.emplace(parent, viewRef_.size());
if (!rv.second)
break;
@@ -80,7 +81,7 @@ void GridView::updateView(Predicate pred) }
//build subview
- this->viewRef.push_back(ref.objId);
+ this->viewRef_.push_back(ref.objId);
}
}
}
@@ -88,14 +89,14 @@ void GridView::updateView(Predicate pred) ptrdiff_t GridView::findRowDirect(FileSystemObject::ObjectIdConst objId) const
{
- auto it = rowPositions.find(objId);
- return it != rowPositions.end() ? it->second : -1;
+ auto it = rowPositions_.find(objId);
+ return it != rowPositions_.end() ? it->second : -1;
}
-ptrdiff_t GridView::findRowFirstChild(const HierarchyObject* hierObj) const
+ptrdiff_t GridView::findRowFirstChild(const ContainerObject* hierObj) const
{
- auto it = rowPositionsFirstChild.find(hierObj);
- return it != rowPositionsFirstChild.end() ? it->second : -1;
+ auto it = rowPositionsFirstChild_.find(hierObj);
+ return it != rowPositionsFirstChild_.end() ? it->second : -1;
}
@@ -239,13 +240,13 @@ GridView::StatusSyncPreview GridView::updateSyncPreview(bool showExcluded, //map std::vector<FileSystemObject*> GridView::getAllFileRef(const std::vector<size_t>& rows)
{
- const size_t viewSize = viewRef.size();
+ const size_t viewSize = viewRef_.size();
std::vector<FileSystemObject*> output;
for (size_t pos : rows)
if (pos < viewSize)
- if (FileSystemObject* fsObj = FileSystemObject::retrieve(viewRef[pos]))
+ if (FileSystemObject* fsObj = FileSystemObject::retrieve(viewRef_[pos]))
output.push_back(fsObj);
return output;
@@ -254,59 +255,82 @@ std::vector<FileSystemObject*> GridView::getAllFileRef(const std::vector<size_t> void GridView::removeInvalidRows()
{
- viewRef.clear();
- rowPositions.clear();
- rowPositionsFirstChild.clear();
+ viewRef_.clear();
+ rowPositions_.clear();
+ rowPositionsFirstChild_.clear();
//remove rows that have been deleted meanwhile
- erase_if(sortedRef, [&](const RefIndex& refIdx) { return !FileSystemObject::retrieve(refIdx.objId); });
+ erase_if(sortedRef_, [&](const RefIndex& refIdx) { return !FileSystemObject::retrieve(refIdx.objId); });
}
class GridView::SerializeHierarchy
{
public:
- static void execute(HierarchyObject& hierObj, std::vector<GridView::RefIndex>& sortedRef, size_t index) { SerializeHierarchy(sortedRef, index).recurse(hierObj); }
+ static void execute(ContainerObject& hierObj, std::vector<GridView::RefIndex>& sortedRef, size_t index) { SerializeHierarchy(sortedRef, index).recurse(hierObj); }
private:
SerializeHierarchy(std::vector<GridView::RefIndex>& sortedRef, size_t index) :
index_(index),
- sortedRef_(sortedRef) {}
+ output_(sortedRef) {}
+ /*
+ Spend additional CPU cycles to sort the standard file list?
+
+ Test case: 690.000 item pairs, Windows 7 x64 (C:\ vs I:\)
+ ----------------------
+ CmpNaturalSort: 850 ms
+ CmpFilePath: 233 ms
+ CmpAsciiNoCase: 189 ms
+ No sorting: 30 ms
+ */
+#if 0
+ template <class ItemPair>
+ static std::vector<ItemPair*> getItemsSorted(FixedList<ItemPair>& itemList)
+ {
+ std::vector<ItemPair*> output;
+ for (ItemPair& item : itemList)
+ output.push_back(&item);
- void recurse(HierarchyObject& hierObj)
+ std::sort(output.begin(), output.end(), [](const ItemPair* lhs, const ItemPair* rhs) { return LessNaturalSort()(lhs->getPairItemName(), rhs->getPairItemName()); });
+ return output;
+ }
+#endif
+ void recurse(ContainerObject& hierObj)
{
for (FilePair& file : hierObj.refSubFiles())
- sortedRef_.emplace_back(index_, file.getId());
+ output_.emplace_back(index_, file.getId());
+
for (SymlinkPair& symlink : hierObj.refSubLinks())
- sortedRef_.emplace_back(index_, symlink.getId());
+ output_.emplace_back(index_, symlink.getId());
+
for (FolderPair& folder : hierObj.refSubFolders())
{
- sortedRef_.emplace_back(index_, folder.getId());
+ output_.emplace_back(index_, folder.getId());
recurse(folder); //add recursion here to list sub-objects directly below parent!
}
}
- size_t index_;
- std::vector<GridView::RefIndex>& sortedRef_;
+ const size_t index_;
+ std::vector<GridView::RefIndex>& output_;
};
void GridView::setData(FolderComparison& folderCmp)
{
//clear everything
- std::vector<FileSystemObject::ObjectId>().swap(viewRef); //free mem
- std::vector<RefIndex>().swap(sortedRef); //
- currentSort = NoValue();
+ std::vector<FileSystemObject::ObjectId>().swap(viewRef_); //free mem
+ std::vector<RefIndex>().swap(sortedRef_); //
+ currentSort_ = NoValue();
- folderPairCount = std::count_if(begin(folderCmp), end(folderCmp),
- [](const BaseFolderPair& baseObj) //count non-empty pairs to distinguish single/multiple folder pair cases
+ folderPairCount_ = std::count_if(begin(folderCmp), end(folderCmp),
+ [](const BaseFolderPair& baseObj) //count non-empty pairs to distinguish single/multiple folder pair cases
{
return !AFS::isNullPath(baseObj.getAbstractPath< LEFT_SIDE>()) ||
!AFS::isNullPath(baseObj.getAbstractPath<RIGHT_SIDE>());
});
for (auto it = begin(folderCmp); it != end(folderCmp); ++it)
- SerializeHierarchy::execute(*it, sortedRef, it - begin(folderCmp));
+ SerializeHierarchy::execute(*it, sortedRef_, it - begin(folderCmp));
}
@@ -472,10 +496,10 @@ bool GridView::getDefaultSortDirection(ColumnTypeRim type) //true: ascending; fa void GridView::sortView(ColumnTypeRim type, ItemPathFormat pathFmt, bool onLeft, bool ascending)
{
- viewRef.clear();
- rowPositions.clear();
- rowPositionsFirstChild.clear();
- currentSort = SortInfo(type, onLeft, ascending);
+ viewRef_.clear();
+ rowPositions_.clear();
+ rowPositionsFirstChild_.clear();
+ currentSort_ = SortInfo(type, onLeft, ascending);
switch (type)
{
@@ -483,43 +507,43 @@ void GridView::sortView(ColumnTypeRim type, ItemPathFormat pathFmt, bool onLeft, switch (pathFmt)
{
case ItemPathFormat::FULL_PATH:
- if ( ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFullPath<true, LEFT_SIDE >());
- else if ( ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFullPath<true, RIGHT_SIDE>());
- else if (!ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFullPath<false, LEFT_SIDE >());
- else if (!ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFullPath<false, RIGHT_SIDE>());
+ if ( ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<true, LEFT_SIDE >());
+ else if ( ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<true, RIGHT_SIDE>());
+ else if (!ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<false, LEFT_SIDE >());
+ else if (!ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<false, RIGHT_SIDE>());
break;
case ItemPathFormat::RELATIVE_PATH:
- if ( ascending) std::sort(sortedRef.begin(), sortedRef.end(), LessRelativeFolder<true>());
- else if (!ascending) std::sort(sortedRef.begin(), sortedRef.end(), LessRelativeFolder<false>());
+ if ( ascending) std::sort(sortedRef_.begin(), sortedRef_.end(), LessRelativeFolder<true>());
+ else if (!ascending) std::sort(sortedRef_.begin(), sortedRef_.end(), LessRelativeFolder<false>());
break;
case ItemPathFormat::ITEM_NAME:
- if ( ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessShortFileName<true, LEFT_SIDE >());
- else if ( ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessShortFileName<true, RIGHT_SIDE>());
- else if (!ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessShortFileName<false, LEFT_SIDE >());
- else if (!ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessShortFileName<false, RIGHT_SIDE>());
+ if ( ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessShortFileName<true, LEFT_SIDE >());
+ else if ( ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessShortFileName<true, RIGHT_SIDE>());
+ else if (!ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessShortFileName<false, LEFT_SIDE >());
+ else if (!ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessShortFileName<false, RIGHT_SIDE>());
break;
}
break;
case ColumnTypeRim::SIZE:
- if ( ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFilesize<true, LEFT_SIDE >());
- else if ( ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFilesize<true, RIGHT_SIDE>());
- else if (!ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFilesize<false, LEFT_SIDE >());
- else if (!ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFilesize<false, RIGHT_SIDE>());
+ if ( ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFilesize<true, LEFT_SIDE >());
+ else if ( ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFilesize<true, RIGHT_SIDE>());
+ else if (!ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFilesize<false, LEFT_SIDE >());
+ else if (!ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFilesize<false, RIGHT_SIDE>());
break;
case ColumnTypeRim::DATE:
- if ( ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFiletime<true, LEFT_SIDE >());
- else if ( ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFiletime<true, RIGHT_SIDE>());
- else if (!ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFiletime<false, LEFT_SIDE >());
- else if (!ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFiletime<false, RIGHT_SIDE>());
+ if ( ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFiletime<true, LEFT_SIDE >());
+ else if ( ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFiletime<true, RIGHT_SIDE>());
+ else if (!ascending && onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFiletime<false, LEFT_SIDE >());
+ else if (!ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFiletime<false, RIGHT_SIDE>());
break;
case ColumnTypeRim::EXTENSION:
- if ( ascending && onLeft) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessExtension<true, LEFT_SIDE >());
- else if ( ascending && !onLeft) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessExtension<true, RIGHT_SIDE>());
- else if (!ascending && onLeft) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessExtension<false, LEFT_SIDE >());
- else if (!ascending && !onLeft) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessExtension<false, RIGHT_SIDE>());
+ if ( ascending && onLeft) std::stable_sort(sortedRef_.begin(), sortedRef_.end(), LessExtension<true, LEFT_SIDE >());
+ else if ( ascending && !onLeft) std::stable_sort(sortedRef_.begin(), sortedRef_.end(), LessExtension<true, RIGHT_SIDE>());
+ else if (!ascending && onLeft) std::stable_sort(sortedRef_.begin(), sortedRef_.end(), LessExtension<false, LEFT_SIDE >());
+ else if (!ascending && !onLeft) std::stable_sort(sortedRef_.begin(), sortedRef_.end(), LessExtension<false, RIGHT_SIDE>());
break;
}
}
diff --git a/FreeFileSync/Source/ui/grid_view.h b/FreeFileSync/Source/ui/grid_view.h index d37b9990..03da427a 100755 --- a/FreeFileSync/Source/ui/grid_view.h +++ b/FreeFileSync/Source/ui/grid_view.h @@ -25,8 +25,8 @@ public: const FileSystemObject* getObject(size_t row) const; //returns nullptr if object is not found; complexity: constant!
/**/
FileSystemObject* getObject(size_t row); //
- size_t rowsOnView() const { return viewRef .size(); } //only visible elements
- size_t rowsTotal () const { return sortedRef.size(); } //total rows available
+ size_t rowsOnView() const { return viewRef_ .size(); } //only visible elements
+ size_t rowsTotal () const { return sortedRef_.size(); } //total rows available
//get references to FileSystemObject: no nullptr-check needed! Everything's bound.
std::vector<FileSystemObject*> getAllFileRef(const std::vector<size_t>& rows);
@@ -112,13 +112,13 @@ public: bool onLeft_;
bool ascending_;
};
- const SortInfo* getSortInfo() const { return currentSort.get(); } //return nullptr if currently not sorted
+ const SortInfo* getSortInfo() const { return currentSort_.get(); } //return nullptr if currently not sorted
ptrdiff_t findRowDirect(FileSystemObject::ObjectIdConst objId) const; // find an object's row position on view list directly, return < 0 if not found
- ptrdiff_t findRowFirstChild(const HierarchyObject* hierObj) const; // find first child of FolderPair or BaseFolderPair *on sorted sub view*
+ ptrdiff_t findRowFirstChild(const ContainerObject* hierObj) const; // find first child of FolderPair or BaseFolderPair *on sorted sub view*
//"hierObj" may be invalid, it is NOT dereferenced, return < 0 if not found
- size_t getFolderPairCount() const { return folderPairCount; } //count non-empty pairs to distinguish single/multiple folder pair cases
+ size_t getFolderPairCount() const { return folderPairCount_; } //count non-empty pairs to distinguish single/multiple folder pair cases
private:
GridView (const GridView&) = delete;
@@ -136,20 +136,20 @@ private: template <class Predicate> void updateView(Predicate pred);
- std::unordered_map<FileSystemObject::ObjectIdConst, size_t> rowPositions; //find row positions on sortedRef directly
- std::unordered_map<const void*, size_t> rowPositionsFirstChild; //find first child on sortedRef of a hierarchy object
- //void* instead of HierarchyObject*: these are weak pointers and should *never be dereferenced*!
+ std::unordered_map<FileSystemObject::ObjectIdConst, size_t> rowPositions_; //find row positions on sortedRef directly
+ std::unordered_map<const void*, size_t> rowPositionsFirstChild_; //find first child on sortedRef of a hierarchy object
+ //void* instead of ContainerObject*: these are weak pointers and should *never be dereferenced*!
- std::vector<FileSystemObject::ObjectId> viewRef; //partial view on sortedRef
+ std::vector<FileSystemObject::ObjectId> viewRef_; //partial view on sortedRef
/* /|\
| (update...)
| */
- std::vector<RefIndex> sortedRef; //flat view of weak pointers on folderCmp; may be sorted
+ std::vector<RefIndex> sortedRef_; //flat view of weak pointers on folderCmp; may be sorted
/* /|\
| (setData...)
| */
//std::shared_ptr<FolderComparison> folderCmp; //actual comparison data: owned by GridView!
- size_t folderPairCount = 0; //number of non-empty folder pairs
+ size_t folderPairCount_ = 0; //number of non-empty folder pairs
class SerializeHierarchy;
@@ -179,7 +179,7 @@ private: template <bool ascending>
struct LessSyncDirection;
- Opt<SortInfo> currentSort;
+ Opt<SortInfo> currentSort_;
};
@@ -193,8 +193,8 @@ private: inline
const FileSystemObject* GridView::getObject(size_t row) const
{
- return row < viewRef.size() ?
- FileSystemObject::retrieve(viewRef[row]) : nullptr;
+ return row < viewRef_.size() ?
+ FileSystemObject::retrieve(viewRef_[row]) : nullptr;
}
inline
diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp index f3d91464..cf320877 100755 --- a/FreeFileSync/Source/ui/gui_generated.cpp +++ b/FreeFileSync/Source/ui/gui_generated.cpp @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Jun 17 2015)
+// C++ code generated with wxFormBuilder (version Dec 21 2016)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
@@ -127,7 +127,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizerTopButtons->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 );
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->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
m_buttonCancel->Enable( false );
m_buttonCancel->Hide();
@@ -135,7 +135,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const 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->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
m_buttonCompare->SetToolTip( _("dummy") );
bSizerTopButtons->Add( m_buttonCompare, 0, wxEXPAND, 5 );
@@ -205,7 +205,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizerTopButtons->Add( 4, 0, 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->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
m_buttonSync->SetToolTip( _("dummy") );
bSizerTopButtons->Add( m_buttonSync, 0, wxEXPAND, 5 );
@@ -550,7 +550,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const 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 ) );
+ m_staticTextFullStatus->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer451->Add( m_staticTextFullStatus, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
@@ -580,7 +580,6 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer1713->Add( m_staticText101, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_textCtrlSearchTxt = new wxTextCtrl( m_panelSearch, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220, -1 ), wxTE_PROCESS_ENTER|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 );
@@ -1118,7 +1117,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w fgSizer16->Add( m_bitmapByTimeSize, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
m_toggleBtnByTimeSize = new wxToggleButton( m_panelComparisonSettings, wxID_ANY, _("File time and size"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
- m_toggleBtnByTimeSize->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+ m_toggleBtnByTimeSize->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
fgSizer16->Add( m_toggleBtnByTimeSize, 0, wxEXPAND, 5 );
@@ -1126,7 +1125,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w fgSizer16->Add( m_bitmapByContent, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
m_toggleBtnByContent = new wxToggleButton( m_panelComparisonSettings, wxID_ANY, _("File content"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
- m_toggleBtnByContent->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+ m_toggleBtnByContent->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
fgSizer16->Add( m_toggleBtnByContent, 0, wxEXPAND, 5 );
@@ -1135,7 +1134,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w m_toggleBtnBySize = new wxToggleButton( m_panelComparisonSettings, wxID_ANY, _("File size"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_toggleBtnBySize->SetValue( true );
- m_toggleBtnBySize->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+ m_toggleBtnBySize->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
fgSizer16->Add( m_toggleBtnBySize, 0, wxEXPAND, 5 );
@@ -1513,22 +1512,22 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w 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 ) );
+ m_toggleBtnTwoWay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer236->Add( m_toggleBtnTwoWay, 0, 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 ) );
+ m_toggleBtnMirror->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer236->Add( m_toggleBtnMirror, 0, 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 ) );
+ m_toggleBtnUpdate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer236->Add( m_toggleBtnUpdate, 0, 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 ) );
+ m_toggleBtnCustom->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer236->Add( m_toggleBtnCustom, 0, wxEXPAND, 5 );
@@ -1782,7 +1781,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w 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->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
m_staticTextNamingCvtPart2Bold->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
bSizer192->Add( m_staticTextNamingCvtPart2Bold, 0, wxALIGN_CENTER_VERTICAL, 5 );
@@ -1870,7 +1869,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w 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 ) );
+ m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
@@ -2116,7 +2115,7 @@ SftpSetupDlgGenerated::SftpSetupDlgGenerated( wxWindow* parent, wxWindowID id, c bSizer181->Add( m_staticText1382, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
- m_staticText138 = new wxStaticText( m_panel41, wxID_ANY, _("123.123.123"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText138 = new wxStaticText( m_panel41, wxID_ANY, _("66.198.240.22"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText138->Wrap( -1 );
m_staticText138->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
@@ -2347,7 +2346,7 @@ SftpSetupDlgGenerated::SftpSetupDlgGenerated( wxWindow* parent, wxWindowID id, c 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 ) );
+ m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizerStdButtons->Add( m_buttonOkay, 0, wxALL, 5 );
@@ -2427,7 +2426,7 @@ SftpFolderPickerGenerated::SftpFolderPickerGenerated( wxWindow* parent, wxWindow m_buttonOkay = new wxButton( this, wxID_OK, _("Select Folder"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_buttonOkay->SetDefault();
- m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+ m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizerStdButtons->Add( m_buttonOkay, 0, wxALL, 5 );
@@ -2509,7 +2508,7 @@ SyncConfirmationDlgGenerated::SyncConfirmationDlgGenerated( wxWindow* parent, wx 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 ) );
+ m_staticTextVariant->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer182->Add( m_staticTextVariant, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
@@ -2648,7 +2647,7 @@ SyncConfirmationDlgGenerated::SyncConfirmationDlgGenerated( wxWindow* parent, wx 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 ) );
+ m_buttonStartSync->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizerStdButtons->Add( m_buttonStartSync, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
@@ -2705,7 +2704,7 @@ CompareProgressDlgGenerated::CompareProgressDlgGenerated( wxWindow* parent, wxWi 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 ) );
+ m_staticTextItemsFound->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
fgSizer7->Add( m_staticTextItemsFound, 0, wxALIGN_BOTTOM, 5 );
@@ -2717,7 +2716,7 @@ CompareProgressDlgGenerated::CompareProgressDlgGenerated( wxWindow* parent, wxWi 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 ) );
+ m_staticTextItemsRemaining->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizerItemsRemaining->Add( m_staticTextItemsRemaining, 0, wxALIGN_BOTTOM, 5 );
@@ -2734,7 +2733,7 @@ CompareProgressDlgGenerated::CompareProgressDlgGenerated( wxWindow* parent, wxWi 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 ) );
+ m_staticTextTimeRemaining->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
fgSizer7->Add( m_staticTextTimeRemaining, 0, wxALIGN_BOTTOM, 5 );
@@ -2745,7 +2744,7 @@ CompareProgressDlgGenerated::CompareProgressDlgGenerated( wxWindow* parent, wxWi 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 ) );
+ m_staticTextTimeElapsed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
fgSizer7->Add( m_staticTextTimeElapsed, 0, wxALIGN_BOTTOM, 5 );
@@ -2831,7 +2830,7 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind 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 ) );
+ m_staticTextPhase->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer42->Add( m_staticTextPhase, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 );
@@ -2898,7 +2897,7 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind m_staticTextItemsProcessed = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_staticTextItemsProcessed->Wrap( -1 );
- m_staticTextItemsProcessed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+ m_staticTextItemsProcessed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer169->Add( m_staticTextItemsProcessed, 0, wxALIGN_BOTTOM, 5 );
@@ -2937,7 +2936,7 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind m_staticTextItemsRemaining = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_staticTextItemsRemaining->Wrap( -1 );
- m_staticTextItemsRemaining->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+ m_staticTextItemsRemaining->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer170->Add( m_staticTextItemsRemaining, 0, wxALIGN_BOTTOM, 5 );
@@ -2973,7 +2972,7 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind m_staticTextTimeRemaining = new wxStaticText( m_panelTimeRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextTimeRemaining->Wrap( -1 );
- m_staticTextTimeRemaining->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+ m_staticTextTimeRemaining->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer167->Add( m_staticTextTimeRemaining, 0, wxRIGHT|wxLEFT, 5 );
@@ -3003,7 +3002,7 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind 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 ) );
+ m_staticTextTimeElapsed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer168->Add( m_staticTextTimeElapsed, 0, wxRIGHT|wxLEFT, 5 );
@@ -3330,7 +3329,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS 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 ) );
+ m_buttonSaveAs->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizerStdButtons->Add( m_buttonSaveAs, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
@@ -3418,7 +3417,7 @@ DeleteDlgGenerated::DeleteDlgGenerated( wxWindow* parent, wxWindowID id, const w 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 ) );
+ m_buttonOK->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizerStdButtons->Add( m_buttonOK, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 );
@@ -3531,7 +3530,7 @@ CopyToDlgGenerated::CopyToDlgGenerated( wxWindow* parent, wxWindowID id, const w m_buttonOK = new wxButton( this, wxID_OK, _("Copy"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_buttonOK->SetDefault();
- m_buttonOK->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+ m_buttonOK->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizerStdButtons->Add( m_buttonOK, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 );
@@ -3787,7 +3786,7 @@ OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const 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 ) );
+ m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
@@ -3877,7 +3876,7 @@ SelectTimespanDlgGenerated::SelectTimespanDlgGenerated( wxWindow* parent, wxWind 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 ) );
+ m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
@@ -4037,13 +4036,13 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_staticTextThanks = new wxStaticText( m_panel391, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE );
m_staticTextThanks->Wrap( -1 );
- m_staticTextThanks->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) );
+ m_staticTextThanks->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
m_staticTextThanks->SetForegroundColour( wxColour( 0, 0, 0 ) );
bSizer1781->Add( m_staticTextThanks, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );
m_buttonShowDonationDetails = new wxButton( m_panel391, wxID_ANY, _("Donation details"), wxDefaultPosition, wxDefaultSize, 0 );
- m_buttonShowDonationDetails->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) );
+ m_buttonShowDonationDetails->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
bSizer1781->Add( m_buttonShowDonationDetails, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 );
@@ -4088,13 +4087,13 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_staticTextDonate = new wxStaticText( m_panel39, wxID_ANY, _("If you like FreeFileSync:"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE );
m_staticTextDonate->Wrap( -1 );
- m_staticTextDonate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) );
+ m_staticTextDonate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
m_staticTextDonate->SetForegroundColour( wxColour( 0, 0, 0 ) );
bSizer178->Add( m_staticTextDonate, 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->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
m_buttonDonate->SetToolTip( _("http://www.freefilesync.org/donate.php") );
bSizer178->Add( m_buttonDonate, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 );
@@ -4136,7 +4135,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer166->Add( m_bitmapHomepage, 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->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, true, wxEmptyString ) );
m_hyperlink1->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
m_hyperlink1->SetToolTip( _("http://www.freefilesync.org") );
@@ -4151,7 +4150,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer166->Add( m_bitmapEmail, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
m_hyperlink2 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("zenju@freefilesync.org"), wxT("mailto:zenju@freefilesync.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- m_hyperlink2->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, true, wxEmptyString ) );
+ m_hyperlink2->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, true, wxEmptyString ) );
m_hyperlink2->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
m_hyperlink2->SetToolTip( _("mailto:zenju@freefilesync.org") );
@@ -4204,7 +4203,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS 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 ) );
+ m_staticText54->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer177->Add( m_staticText54, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
@@ -4402,7 +4401,7 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id, m_buttonActivateOnline = new wxButton( m_panel3511, wxID_ANY, _("Activate online"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_buttonActivateOnline->SetDefault();
- m_buttonActivateOnline->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+ m_buttonActivateOnline->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer234->Add( m_buttonActivateOnline, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
@@ -4439,7 +4438,7 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id, m_buttonCopyUrl = new wxButton( m_panel351, wxID_ANY, _("&Copy to clipboard"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_buttonCopyUrl->SetDefault();
- m_buttonCopyUrl->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) );
+ m_buttonCopyUrl->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
bSizer236->Add( m_buttonCopyUrl, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
@@ -4447,7 +4446,6 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id, bSizer237->Add( bSizer236, 0, wxEXPAND, 5 );
m_textCtrlManualActivationUrl = new wxTextCtrl( m_panel351, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220, -1 ), wxTE_MULTILINE|wxTE_READONLY|wxWANTS_CHARS );
- m_textCtrlManualActivationUrl->SetMaxLength( 0 );
bSizer237->Add( m_textCtrlManualActivationUrl, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
wxBoxSizer* bSizer235;
@@ -4458,11 +4456,10 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id, bSizer235->Add( m_staticText13611, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
m_textCtrlOfflineActivationKey = new wxTextCtrl( m_panel351, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220, -1 ), wxTE_PROCESS_ENTER|wxWANTS_CHARS );
- m_textCtrlOfflineActivationKey->SetMaxLength( 0 );
bSizer235->Add( m_textCtrlOfflineActivationKey, 1, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 );
m_buttonActivateOffline = new wxButton( m_panel351, wxID_ANY, _("Activate offline"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
- m_buttonActivateOffline->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+ m_buttonActivateOffline->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer235->Add( m_buttonActivateOffline, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h index a9e15154..438b6e2c 100755 --- a/FreeFileSync/Source/ui/gui_generated.h +++ b/FreeFileSync/Source/ui/gui_generated.h @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Jun 17 2015)
+// C++ code generated with wxFormBuilder (version Dec 21 2016)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp index 28b14545..2031d2f4 100755 --- a/FreeFileSync/Source/ui/gui_status_handler.cpp +++ b/FreeFileSync/Source/ui/gui_status_handler.cpp @@ -332,7 +332,7 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog() //----------------- write results into LastSyncs.log------------------------
try
{
- saveToLastSyncsLog(summary, errorLog_, lastSyncsLogFileSizeMax_, OnUpdateLogfileStatusNoThrow(*this, utfCvrtTo<std::wstring>(getLastSyncsLogfilePath()))); //throw FileError
+ saveToLastSyncsLog(summary, errorLog_, lastSyncsLogFileSizeMax_, OnUpdateLogfileStatusNoThrow(*this, utfTo<std::wstring>(getLastSyncsLogfilePath()))); //throw FileError
}
catch (FileError&) { assert(false); }
diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp index ace65175..ebe41013 100755 --- a/FreeFileSync/Source/ui/main_dlg.cpp +++ b/FreeFileSync/Source/ui/main_dlg.cpp @@ -16,7 +16,6 @@ #include <wx/filedlg.h>
#include <wx/display.h>
#include <wx+/context_menu.h>
-#include <wx+/string_conv.h>
#include <wx+/bitmap_button.h>
#include <wx+/app_main.h>
#include <wx+/toggle_button.h>
@@ -93,8 +92,8 @@ bool acceptDialogFileDrop(const std::vector<Zstring>& shellItemPaths) return std::any_of(shellItemPaths.begin(), shellItemPaths.end(), [](const Zstring& shellItemPath)
{
const Zstring ext = getFileExtension(shellItemPath);
- return ciEqual(ext, Zstr("ffs_gui")) ||
- ciEqual(ext, Zstr("ffs_batch"));
+ return strEqual(ext, Zstr("ffs_gui"), CmpFilePath()) ||
+ strEqual(ext, Zstr("ffs_batch"), CmpFilePath());
});
}
}
@@ -1073,7 +1072,7 @@ void MainDialog::copySelectionToClipboard(const std::vector<const Grid*>& gridRe }
catch (const std::bad_alloc& e)
{
- showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(_("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what())));
+ showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(_("Out of memory.") + L" " + utfTo<std::wstring>(e.what())));
}
}
@@ -1203,7 +1202,7 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec folderCmp,
extractDirectionCfg(getConfig().mainCfg),
globalCfg.gui.mainDlg.manualDeletionUseRecycler,
- globalCfg.optDialogs.warningRecyclerMissing,
+ globalCfg.optDialogs.warnRecyclerMissing,
statusHandler);
m_gridMainL->clearSelection(ALLOW_GRID_EVENT);
@@ -1285,13 +1284,11 @@ void invokeCommandLine(const Zstring& commandLinePhrase, //throw FileError const AbstractPath basePath = fsObj->base().getAbstractPath<side >();
const AbstractPath basePath2 = fsObj->base().getAbstractPath<side2>();
- const Zstring& relPath = fsObj->getPairRelativePath();
- const Zstring& relPathParent = beforeLast(relPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
//full path, even if item is not (yet) existing:
- const Zstring itemPath = !AFS::isNullPath(basePath ) ? toZ(AFS::getDisplayPath(AFS::appendRelPath(basePath, relPath ))) : Zstr("");
- const Zstring itemPath2 = !AFS::isNullPath(basePath2) ? toZ(AFS::getDisplayPath(AFS::appendRelPath(basePath2, relPath ))) : Zstr("");
- const Zstring folderPath = !AFS::isNullPath(basePath ) ? toZ(AFS::getDisplayPath(AFS::appendRelPath(basePath, relPathParent))) : Zstr("");
- const Zstring folderPath2 = !AFS::isNullPath(basePath2) ? toZ(AFS::getDisplayPath(AFS::appendRelPath(basePath2, relPathParent))) : Zstr("");
+ const Zstring itemPath = AFS::isNullPath(basePath ) ? Zstr("") : utfTo<Zstring>(AFS::getDisplayPath(fsObj->getAbstractPath<side >()));
+ const Zstring itemPath2 = AFS::isNullPath(basePath2) ? Zstr("") : utfTo<Zstring>(AFS::getDisplayPath(fsObj->getAbstractPath<side2>()));
+ const Zstring folderPath = AFS::isNullPath(basePath ) ? Zstr("") : utfTo<Zstring>(AFS::getDisplayPath(fsObj->parent().getAbstractPath<side >()));
+ const Zstring folderPath2 = AFS::isNullPath(basePath2) ? Zstr("") : utfTo<Zstring>(AFS::getDisplayPath(fsObj->parent().getAbstractPath<side2>()));
Zstring localPath;
Zstring localPath2;
@@ -1306,8 +1303,8 @@ void invokeCommandLine(const Zstring& commandLinePhrase, //throw FileError else
extractFileDetails<side2>(*fsObj, [&](const TempFileBuffer::FileDetails& details) { localPath2 = tempFileBuf.getTempPath(details); });
- if (localPath .empty()) localPath = replaceCpy(toZ(L"<" + _("Local path not available for %x.") + L">"), Zstr("%x"), itemPath );
- if (localPath2.empty()) localPath2 = replaceCpy(toZ(L"<" + _("Local path not available for %x.") + L">"), Zstr("%x"), itemPath2);
+ if (localPath .empty()) localPath = replaceCpy(utfTo<Zstring>(L"<" + _("Local path not available for %x.") + L">"), Zstr("%x"), itemPath );
+ if (localPath2.empty()) localPath2 = replaceCpy(utfTo<Zstring>(L"<" + _("Local path not available for %x.") + L">"), Zstr("%x"), itemPath2);
Zstring command = commandLinePhrase;
replace(command, Zstr("%item_path%"), itemPath);
@@ -1347,7 +1344,7 @@ void MainDialog::openExternalApplication(const Zstring& commandLinePhrase, bool {
try
{
- shellExecute("xdg-open \"" + toZ(AFS::getDisplayPath(folderPath)) + "\"", EXEC_TYPE_ASYNC); //
+ shellExecute("xdg-open \"" + utfTo<Zstring>(AFS::getDisplayPath(folderPath)) + "\"", EXEC_TYPE_ASYNC); //
}
catch (const FileError& e) { showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); }
};
@@ -1981,7 +1978,7 @@ void MainDialog::onNaviSelection(GridRangeSelectEvent& event) //get selection on navigation tree and set corresponding markers on main grid
std::unordered_set<const FileSystemObject*> markedFilesAndLinks; //mark files/symlinks directly
- std::unordered_set<const HierarchyObject*> markedContainer; //mark full container including child-objects
+ std::unordered_set<const ContainerObject*> markedContainer; //mark full container including child-objects
for (size_t row : m_gridNavi->getSelectedRows())
if (std::unique_ptr<TreeView::Node> node = treeDataView->getLine(row))
@@ -2045,13 +2042,13 @@ void MainDialog::onNaviGridContext(GridClickEvent& event) Zstring labelShort = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getPairItemName();
if (isFolder)
labelShort += FILE_NAME_SEPARATOR;
- submenu.addItem(utfCvrtTo<wxString>(labelShort), [this, &selection, include] { filterShortname(*selection[0], include); });
+ submenu.addItem(utfTo<wxString>(labelShort), [this, &selection, include] { filterShortname(*selection[0], include); });
//by relative path
Zstring labelRel = FILE_NAME_SEPARATOR + selection[0]->getPairRelativePath();
if (isFolder)
labelRel += FILE_NAME_SEPARATOR;
- submenu.addItem(utfCvrtTo<wxString>(labelRel), [this, &selection, include] { filterItems(selection, include); });
+ submenu.addItem(utfTo<wxString>(labelRel), [this, &selection, include] { filterItems(selection, include); });
menu.addSubmenu(label, submenu, &getResourceImage(iconName));
}
@@ -2170,9 +2167,9 @@ void MainDialog::onMainGridContextRim(bool leftSide) //by extension
if (!isFolder)
{
- const Zstring extension = getFileExtension(selection[0]->getPairRelativePath());
+ const Zstring extension = getFileExtension(selection[0]->getPairItemName());
if (!extension.empty())
- submenu.addItem(L"*." + utfCvrtTo<wxString>(extension),
+ submenu.addItem(L"*." + utfTo<wxString>(extension),
[this, extension, include] { filterExtension(extension, include); });
}
@@ -2180,13 +2177,13 @@ void MainDialog::onMainGridContextRim(bool leftSide) Zstring labelShort = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getPairItemName();
if (isFolder)
labelShort += FILE_NAME_SEPARATOR;
- submenu.addItem(utfCvrtTo<wxString>(labelShort), [this, &selection, include] { filterShortname(*selection[0], include); });
+ submenu.addItem(utfTo<wxString>(labelShort), [this, &selection, include] { filterShortname(*selection[0], include); });
//by relative path
Zstring labelRel = FILE_NAME_SEPARATOR + selection[0]->getPairRelativePath();
if (isFolder)
labelRel += FILE_NAME_SEPARATOR;
- submenu.addItem(utfCvrtTo<wxString>(labelRel), [this, &selection, include] { filterItems(selection, include); });
+ submenu.addItem(utfTo<wxString>(labelRel), [this, &selection, include] { filterItems(selection, include); });
menu.addSubmenu(label, submenu, &getResourceImage(iconName));
}
@@ -2264,7 +2261,7 @@ void MainDialog::onMainGridContextRim(bool leftSide) -void MainDialog::filterPhrase(const Zstring& phrase, bool include, bool addNewLine)
+void MainDialog::addFilterPhrase(const Zstring& phrase, bool include, bool requireNewLine)
{
Zstring& filterString = [&]() -> Zstring&
{
@@ -2279,17 +2276,19 @@ void MainDialog::filterPhrase(const Zstring& phrase, bool include, bool addNewLi return currentCfg.mainCfg.globalFilter.excludeFilter;
}();
- if (addNewLine)
+ if (requireNewLine)
{
- if (!filterString.empty() && !endsWith(filterString, Zstr("\n")))
+ trim(filterString, false, true, [](Zchar c) { return c == FILTER_ITEM_SEPARATOR || c == Zstr('\n'); });
+ if (!filterString.empty())
filterString += Zstr("\n");
filterString += phrase;
}
else
{
- if (!filterString.empty() && !endsWith(filterString, Zstr("\n")) && !endsWith(filterString, Zstr(";")))
+ trim(filterString, false, true, [](Zchar c) { return c == Zstr('\n'); });
+ if (!filterString.empty() && !endsWith(filterString, FILTER_ITEM_SEPARATOR))
filterString += Zstr("\n");
- filterString += phrase + Zstr(";"); //';' is appended to 'mark' that next exclude extension entry won't write to new line
+ filterString += phrase + FILTER_ITEM_SEPARATOR; //append FILTER_ITEM_SEPARATOR to 'mark' that next extension exclude should write to same line
}
updateGlobalFilterButton();
@@ -2306,7 +2305,7 @@ void MainDialog::filterPhrase(const Zstring& phrase, bool include, bool addNewLi void MainDialog::filterExtension(const Zstring& extension, bool include)
{
assert(!extension.empty());
- filterPhrase(Zstr("*.") + extension, include, false);
+ addFilterPhrase(Zstr("*.") + extension, include, false);
}
@@ -2317,7 +2316,7 @@ void MainDialog::filterShortname(const FileSystemObject& fsObj, bool include) if (isFolder)
phrase += FILE_NAME_SEPARATOR;
- filterPhrase(phrase, include, true);
+ addFilterPhrase(phrase, include, true);
}
@@ -2340,7 +2339,7 @@ void MainDialog::filterItems(const std::vector<FileSystemObject*>& selection, bo if (isFolder)
phrase += FILE_NAME_SEPARATOR;
}
- filterPhrase(phrase, include, true);
+ addFilterPhrase(phrase, include, true);
}
}
@@ -2594,12 +2593,12 @@ void MainDialog::onDirManualCorrection(wxCommandEvent& event) }
-wxString getFormattedHistoryElement(const Zstring& filepath)
+Zstring getFormattedHistoryElement(const Zstring& filepath)
{
Zstring output = afterLast(filepath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
if (endsWith(output, Zstr(".ffs_gui")))
output = beforeLast(output, Zstr('.'), IF_MISSING_RETURN_NONE);
- return utfCvrtTo<wxString>(output);
+ return output;
}
@@ -2641,28 +2640,31 @@ void MainDialog::addFileToCfgHistory(const std::vector<Zstring>& filePaths) {
const wxString lastSessionLabel = L"<" + _("Last session") + L">";
- wxString label;
+ wxString newLabel;
unsigned int newPos = 0;
if (equalFilePath(filePath, lastRunConfigPath_))
- label = lastSessionLabel;
+ newLabel = lastSessionLabel;
else
{
//workaround wxWidgets 2.9 bug on GTK screwing up the client data if the list box is sorted:
- label = getFormattedHistoryElement(filePath);
+ const Zstring labelFmt = getFormattedHistoryElement(filePath);
+ newLabel = utfTo<wxString>(labelFmt);
//"linear-time insertion sort":
for (; newPos < m_listBoxHistory->GetCount(); ++newPos)
{
- const wxString& itemLabel = m_listBoxHistory->GetString(newPos);
- if (itemLabel != lastSessionLabel) //last session label should always be at top position!
- if (label.CmpNoCase(itemLabel) < 0)
- break;
+ if (auto histData = dynamic_cast<wxClientHistoryData*>(m_listBoxHistory->GetClientObject(newPos)))
+ if (equalFilePath(histData->cfgFile_, lastRunConfigPath_))
+ continue; //last session label should always be at top position!
+
+ if (LessNaturalSort()(labelFmt, utfTo<Zstring>(m_listBoxHistory->GetString(newPos))))
+ break;
}
}
assert(!m_listBoxHistory->IsSorted());
- m_listBoxHistory->Insert(label, newPos, new wxClientHistoryData(filePath, ++lastUseIndexMax));
+ m_listBoxHistory->Insert(newLabel, newPos, new wxClientHistoryData(filePath, ++lastUseIndexMax));
selections.insert(selections.begin() + newPos, true);
}
@@ -2748,7 +2750,7 @@ void MainDialog::updateUnsavedCfgStatus() title += L'*';
if (!activeCfgFilename.empty())
- title += toWx(activeCfgFilename);
+ title += utfTo<wxString>(activeCfgFilename);
else if (activeConfigFiles.size() > 1)
{
const wchar_t* EM_DASH = L" \u2014 ";
@@ -2828,13 +2830,13 @@ bool MainDialog::trySaveConfig(const Zstring* guiFilename) //return true if save wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak!
wxString(),
//OS X really needs dir/file separated like this:
- utfCvrtTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir
- utfCvrtTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file
+ utfTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir
+ utfTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file
wxString(L"FreeFileSync (*.ffs_gui)|*.ffs_gui") + L"|" +_("All files") + L" (*.*)|*",
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (filePicker.ShowModal() != wxID_OK)
return false;
- targetFilename = toZ(filePicker.GetPath());
+ targetFilename = utfTo<Zstring>(filePicker.GetPath());
}
const xmlAccess::XmlGuiConfig guiCfg = getConfig();
@@ -2917,13 +2919,13 @@ bool MainDialog::trySaveBatchConfig(const Zstring* batchFileToUpdate) wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak!
wxString(),
//OS X really needs dir/file separated like this:
- utfCvrtTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir
- utfCvrtTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file
+ utfTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir
+ utfTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file
_("FreeFileSync batch") + L" (*.ffs_batch)|*.ffs_batch" + L"|" +_("All files") + L" (*.*)|*",
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (filePicker.ShowModal() != wxID_OK)
return false;
- targetFilename = toZ(filePicker.GetPath());
+ targetFilename = utfTo<Zstring>(filePicker.GetPath());
}
try
@@ -2955,7 +2957,7 @@ bool MainDialog::saveOldConfig() //return false on user abort {
bool neverSaveChanges = false;
switch (showConfirmationDialog3(this, DialogInfoType::INFO, PopupDialogCfg3().
- setTitle(toWx(activeCfgFilename)).
+ setTitle(utfTo<wxString>(activeCfgFilename)).
setMainInstructions(replaceCpy(_("Do you want to save changes to %x?"), L"%x",
fmtPath(afterLast(activeCfgFilename, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)))).
setCheckBox(neverSaveChanges, _("Never save &changes"), ConfirmationButton3::DO_IT),
@@ -3010,7 +3012,7 @@ void MainDialog::OnConfigLoad(wxCommandEvent& event) wxFileDialog filePicker(this,
wxString(),
- utfCvrtTo<wxString>(beforeLast(activeCfgFilename, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir
+ utfTo<wxString>(beforeLast(activeCfgFilename, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir
wxString(), //default file
wxString(L"FreeFileSync (*.ffs_gui; *.ffs_batch)|*.ffs_gui;*.ffs_batch") + L"|" +_("All files") + L" (*.*)|*",
wxFD_OPEN | wxFD_MULTIPLE);
@@ -3018,9 +3020,12 @@ void MainDialog::OnConfigLoad(wxCommandEvent& event) {
wxArrayString tmp;
filePicker.GetPaths(tmp);
- std::vector<wxString> filepaths(tmp.begin(), tmp.end());
- loadConfiguration(toZ(filepaths));
+ std::vector<Zstring> filePaths;
+ for (const wxString& path : tmp)
+ filePaths.push_back(utfTo<Zstring>(path));
+
+ loadConfiguration(filePaths);
}
}
@@ -3306,8 +3311,8 @@ void MainDialog::showConfigDialog(SyncConfigPanel panelToShow, int localPairInde auto addPairCfg = [&](const FolderPairEnh& fp)
{
LocalPairConfig fpCfg;
- fpCfg.folderPairName = getShortDisplayNameForFolderPair(AFS::getDisplayPath(createAbstractPath(fp.folderPathPhraseLeft_ )),
- AFS::getDisplayPath(createAbstractPath(fp.folderPathPhraseRight_)));
+ fpCfg.folderPairName = getShortDisplayNameForFolderPair(createAbstractPath(fp.folderPathPhraseLeft_ ),
+ createAbstractPath(fp.folderPathPhraseRight_));
fpCfg.altCmpConfig = fp.altCmpConfig;
fpCfg.altSyncConfig = fp.altSyncConfig;
fpCfg.localFilter = fp.localFilter;
@@ -3493,14 +3498,14 @@ inline wxBitmap buttonPressed(const std::string& name)
{
wxBitmap background = getResourceImage(L"buttonPressed");
- return mirrorIfRtl(layOver(background, getResourceImage(utfCvrtTo<wxString>(name))));
+ return mirrorIfRtl(layOver(background, getResourceImage(utfTo<wxString>(name))));
}
inline
wxBitmap buttonReleased(const std::string& name)
{
- wxImage output = getResourceImage(utfCvrtTo<wxString>(name)).ConvertToImage().ConvertToGreyscale(1.0/3, 1.0/3, 1.0/3); //treat all channels equally!
+ wxImage output = getResourceImage(utfTo<wxString>(name)).ConvertToImage().ConvertToGreyscale(1.0/3, 1.0/3, 1.0/3); //treat all channels equally!
//zen::moveImage(output, 1, 0); //move image right one pixel
brighten(output, 80);
@@ -3681,12 +3686,12 @@ void MainDialog::OnCompare(wxCommandEvent& event) {
const Zstring soundFilePath = getResourceDirPf() + globalCfg.soundFileCompareFinished;
if (fileAvailable(soundFilePath))
- wxSound::Play(utfCvrtTo<wxString>(soundFilePath), wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as batch!
+ wxSound::Play(utfTo<wxString>(soundFilePath), wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as batch!
}
//add to folder history after successful comparison only
- folderHistoryLeft ->addItem(toZ(m_folderPathLeft ->GetValue()));
- folderHistoryRight->addItem(toZ(m_folderPathRight->GetValue()));
+ folderHistoryLeft ->addItem(utfTo<Zstring>(m_folderPathLeft ->GetValue()));
+ folderHistoryRight->addItem(utfTo<Zstring>(m_folderPathRight->GetValue()));
if (oldFocus == m_buttonCompare)
oldFocus = m_buttonSync;
@@ -3862,7 +3867,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event) if (Opt<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<RIGHT_SIDE>()))
availableDirPaths.insert(*nativeFolderPath);
}
- dirLocks = std::make_unique<LockHolder>(availableDirPaths, globalCfg.optDialogs.warningDirectoryLockFailed, statusHandler);
+ dirLocks = std::make_unique<LockHolder>(availableDirPaths, globalCfg.optDialogs.warnDirectoryLockFailed, statusHandler);
}
//START SYNCHRONIZATION
@@ -4167,7 +4172,7 @@ void MainDialog::applySyncConfig() zen::redetermineSyncDirection(getConfig().mainCfg, folderCmp,
[&](const std::wstring& warning)
{
- bool& warningActive = globalCfg.optDialogs.warningDatabaseError;
+ bool& warningActive = globalCfg.optDialogs.warnDatabaseError;
if (warningActive)
{
bool dontWarnAgain = false;
@@ -4247,7 +4252,7 @@ void MainDialog::hideFindPanel() void MainDialog::startFindNext(bool searchAscending) //F3 or ENTER in m_textCtrlSearchTxt
{
- Zstring searchString = utfCvrtTo<Zstring>(trimCpy(m_textCtrlSearchTxt->GetValue()));
+ Zstring searchString = utfTo<Zstring>(trimCpy(m_textCtrlSearchTxt->GetValue()));
if (searchString.empty())
@@ -4262,7 +4267,7 @@ void MainDialog::startFindNext(bool searchAscending) //F3 or ENTER in m_textCtrl std::swap(grid1, grid2); //select side to start search at grid cursor position
wxBeginBusyCursor(wxHOURGLASS_CURSOR);
- const std::pair<const Grid*, ptrdiff_t> result = findGridMatch(*grid1, *grid2, utfCvrtTo<std::wstring>(searchString),
+ const std::pair<const Grid*, ptrdiff_t> result = findGridMatch(*grid1, *grid2, utfTo<std::wstring>(searchString),
m_checkBoxMatchCase->GetValue(), searchAscending); //parameter owned by GUI, *not* globalCfg structure! => we should better implement a getGlocalCfg()!
wxEndBusyCursor();
@@ -4628,7 +4633,7 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) wxBusyCursor dummy;
- const Zstring filePath = utfCvrtTo<Zstring>(filePicker.GetPath());
+ const Zstring filePath = utfTo<Zstring>(filePicker.GetPath());
//http://en.wikipedia.org/wiki/Comma-separated_values
const lconv* localInfo = ::localeconv(); //always bound according to doc
@@ -4638,7 +4643,7 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) auto fmtValue = [&](const wxString& val) -> std::string
{
- std::string&& tmp = utfCvrtTo<std::string>(val);
+ std::string&& tmp = utfTo<std::string>(val);
if (contains(tmp, CSV_SEP))
return '\"' + tmp + '\"';
@@ -4651,11 +4656,10 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) //base folders
header += fmtValue(_("Folder Pairs")) + LINE_BREAK;
- std::for_each(begin(folderCmp), end(folderCmp),
- [&](BaseFolderPair& baseFolder)
+ std::for_each(begin(folderCmp), end(folderCmp), [&](BaseFolderPair& baseFolder)
{
- header += utfCvrtTo<std::string>(AFS::getDisplayPath(baseFolder.getAbstractPath< LEFT_SIDE>())) + CSV_SEP;
- header += utfCvrtTo<std::string>(AFS::getDisplayPath(baseFolder.getAbstractPath<RIGHT_SIDE>())) + LINE_BREAK;
+ header += utfTo<std::string>(AFS::getDisplayPath(baseFolder.getAbstractPath< LEFT_SIDE>())) + CSV_SEP;
+ header += utfTo<std::string>(AFS::getDisplayPath(baseFolder.getAbstractPath<RIGHT_SIDE>())) + LINE_BREAK;
});
header += LINE_BREAK;
diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h index 927b7e35..109940c0 100755 --- a/FreeFileSync/Source/ui/main_dlg.h +++ b/FreeFileSync/Source/ui/main_dlg.h @@ -225,7 +225,7 @@ private: void filterExtension(const Zstring& extension, bool include);
void filterShortname(const zen::FileSystemObject& fsObj, bool include);
void filterItems(const std::vector<zen::FileSystemObject*>& selection, bool include);
- void filterPhrase(const Zstring& phrase, bool include, bool addNewLine);
+ void addFilterPhrase(const Zstring& phrase, bool include, bool requireNewLine);
void OnTopFolderPairAdd (wxCommandEvent& event) override;
void OnTopFolderPairRemove(wxCommandEvent& event) override;
diff --git a/FreeFileSync/Source/ui/on_completion_box.cpp b/FreeFileSync/Source/ui/on_completion_box.cpp index 113fc433..a9afc8f5 100755 --- a/FreeFileSync/Source/ui/on_completion_box.cpp +++ b/FreeFileSync/Source/ui/on_completion_box.cpp @@ -44,7 +44,7 @@ const wxEventType wxEVT_VALIDATE_USER_SELECTION = wxNewEventType(); bool isCloseProgressDlgCommand(const Zstring& value)
{
- return trimCpy(utfCvrtTo<std::wstring>(value)) == getCmdTxtCloseProgressDlg();
+ return trimCpy(utfTo<std::wstring>(value)) == getCmdTxtCloseProgressDlg();
}
@@ -59,7 +59,7 @@ OnCompletionBox::OnCompletionBox(wxWindow* parent, const wxValidator& validator,
const wxString& name) :
wxComboBox(parent, id, value, pos, size, n, choices, style, validator, name),
- defaultCommands(getDefaultCommands())
+ defaultCommands_(getDefaultCommands())
{
//#####################################
/*##*/ SetMinSize(wxSize(150, -1)); //## workaround yet another wxWidgets bug: default minimum size is much too large for a wxComboBox
@@ -78,14 +78,14 @@ void OnCompletionBox::addItemHistory() {
const Zstring command = trimCpy(getValue());
- if (command == utfCvrtTo<Zstring>(getSeparationLine()) || //do not add sep. line
- command == utfCvrtTo<Zstring>(getCmdTxtCloseProgressDlg()) || //do not add special command
+ if (command == utfTo<Zstring>(getSeparationLine()) || //do not add sep. line
+ command == utfTo<Zstring>(getCmdTxtCloseProgressDlg()) || //do not add special command
command.empty())
return;
//do not add built-in commands to history
- for (const auto& item : defaultCommands)
- if (command == utfCvrtTo<Zstring>(item.first) ||
+ for (const auto& item : defaultCommands_)
+ if (command == utfTo<Zstring>(item.first) ||
equalFilePath(command, item.second))
return;
@@ -105,13 +105,13 @@ Zstring OnCompletionBox::getValue() const if (value == implementation::translate(getCmdTxtCloseProgressDlg())) //undo translation for config file storage
value = getCmdTxtCloseProgressDlg();
- return utfCvrtTo<Zstring>(value);
+ return utfTo<Zstring>(value);
}
void OnCompletionBox::setValue(const Zstring& value)
{
- auto tmp = trimCpy(utfCvrtTo<std::wstring>(value));
+ auto tmp = trimCpy(utfTo<std::wstring>(value));
if (tmp == getCmdTxtCloseProgressDlg())
tmp = implementation::translate(getCmdTxtCloseProgressDlg()); //have this symbolic constant translated properly
@@ -131,16 +131,18 @@ void OnCompletionBox::setValueAndUpdateList(const std::wstring& value) items.push_back(implementation::translate(getCmdTxtCloseProgressDlg()));
//2. built in commands
- for (const auto& item : defaultCommands)
+ for (const auto& item : defaultCommands_)
items.push_back(item.first);
//3. history elements
if (!history_.empty())
{
+ auto histSorted = history_;
+ std::sort(histSorted.begin(), histSorted.end(), LessNaturalSort() /*even on Linux*/);
+
items.push_back(getSeparationLine());
- for (const Zstring& hist : history_)
- items.push_back(utfCvrtTo<std::wstring>(hist));
- std::sort(items.end() - history_.size(), items.end());
+ for (const Zstring& hist : histSorted)
+ items.push_back(utfTo<std::wstring>(hist));
}
//attention: if the target value is not part of the dropdown list, SetValue() will look for a string that *starts with* this value:
@@ -180,9 +182,9 @@ void OnCompletionBox::OnValidateSelection(wxCommandEvent& event) if (value == getSeparationLine())
return setValueAndUpdateList(std::wstring());
- for (const auto& item : defaultCommands)
+ for (const auto& item : defaultCommands_)
if (item.first == value)
- return setValueAndUpdateList(utfCvrtTo<std::wstring>(item.second)); //replace GUI name by actual command string
+ return setValueAndUpdateList(utfTo<std::wstring>(item.second)); //replace GUI name by actual command string
}
@@ -209,7 +211,7 @@ void OnCompletionBox::OnKeyEvent(wxKeyEvent& event) (GetValue() != GetString(pos) || //avoid problems when a character shall be deleted instead of list item
GetValue().empty())) //exception: always allow removing empty entry
{
- const auto selValue = utfCvrtTo<Zstring>(GetString(pos));
+ const auto selValue = utfTo<Zstring>(GetString(pos));
if (std::find(history_.begin(), history_.end(), selValue) != history_.end()) //only history elements may be deleted
{
diff --git a/FreeFileSync/Source/ui/on_completion_box.h b/FreeFileSync/Source/ui/on_completion_box.h index ebfc054f..4d866c92 100755 --- a/FreeFileSync/Source/ui/on_completion_box.h +++ b/FreeFileSync/Source/ui/on_completion_box.h @@ -55,7 +55,7 @@ private: std::vector<Zstring> history_;
size_t historyMax_ = 0;
- const std::vector<std::pair<std::wstring, Zstring>> defaultCommands;
+ const std::vector<std::pair<std::wstring, Zstring>> defaultCommands_;
};
diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp index 3b8caacf..bbe4acbd 100755 --- a/FreeFileSync/Source/ui/progress_indicator.cpp +++ b/FreeFileSync/Source/ui/progress_indicator.cpp @@ -491,7 +491,7 @@ inline wxBitmap getImageButtonReleased(const wchar_t* name)
{
return greyScale(getResourceImage(name)).ConvertToImage();
- //getResourceImage(utfCvrtTo<wxString>(name)).ConvertToImage().ConvertToGreyscale(1.0/3, 1.0/3, 1.0/3); //treat all channels equally!
+ //getResourceImage(utfTo<wxString>(name)).ConvertToImage().ConvertToGreyscale(1.0/3, 1.0/3, 1.0/3); //treat all channels equally!
//brighten(output, 30);
//zen::moveImage(output, 1, 0); //move image right one pixel
@@ -505,7 +505,7 @@ class MessageView public:
MessageView(const ErrorLog& log) : log_(log) {}
- size_t rowsOnView() const { return viewRef.size(); }
+ size_t rowsOnView() const { return viewRef_.size(); }
struct LogEntryView
{
@@ -517,9 +517,9 @@ public: Opt<LogEntryView> getEntry(size_t row) const
{
- if (row < viewRef.size())
+ if (row < viewRef_.size())
{
- const Line& line = viewRef[row];
+ const Line& line = viewRef_[row];
LogEntryView output;
output.time = line.logIt_->time;
@@ -533,7 +533,7 @@ public: void updateView(int includedTypes) //TYPE_INFO | TYPE_WARNING, ect. see error_log.h
{
- viewRef.clear();
+ viewRef_.clear();
for (auto it = log_.begin(); it != log_.end(); ++it)
if (it->type & includedTypes)
@@ -547,7 +547,7 @@ public: if (c == L'\n')
{
if (!lastCharNewline) //do not reference empty lines!
- viewRef.emplace_back(it, rowNumber);
+ viewRef_.emplace_back(it, rowNumber);
++rowNumber;
lastCharNewline = true;
}
@@ -555,7 +555,7 @@ public: lastCharNewline = false;
if (!lastCharNewline)
- viewRef.emplace_back(it, rowNumber);
+ viewRef_.emplace_back(it, rowNumber);
}
}
@@ -588,7 +588,7 @@ private: size_t rowNumber_; //LogEntry::message may span multiple rows
};
- std::vector<Line> viewRef; //partial view on log_
+ std::vector<Line> viewRef_; //partial view on log_
/* /|\
| updateView()
| */
@@ -984,7 +984,7 @@ private: }
catch (const std::bad_alloc& e)
{
- showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(_("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what())));
+ showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(_("Out of memory.") + L" " + utfTo<std::wstring>(e.what())));
}
}
@@ -1984,7 +1984,7 @@ void SyncProgressDialogImpl<TopLevelDialog>::processHasFinished(SyncResult resul {
const Zstring soundFilePath = getResourceDirPf() + soundFileSyncComplete_;
if (fileAvailable(soundFilePath))
- wxSound::Play(utfCvrtTo<wxString>(soundFilePath), wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as batch!
+ wxSound::Play(utfTo<wxString>(soundFilePath), wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as batch!
}
break;
}
diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp index c7cd9051..0bb20dd0 100755 --- a/FreeFileSync/Source/ui/small_dlgs.cpp +++ b/FreeFileSync/Source/ui/small_dlgs.cpp @@ -665,7 +665,7 @@ void OptionsDlg::setExtApp(const xmlAccess::ExternalApps& extApp) descriptionTransToEng[description] = it->first;
m_gridCustomCommand->SetCellValue(row, 0, description);
- m_gridCustomCommand->SetCellValue(row, 1, utfCvrtTo<wxString>(it->second)); //commandline
+ m_gridCustomCommand->SetCellValue(row, 1, utfTo<wxString>(it->second)); //commandline
}
}
@@ -676,7 +676,7 @@ xmlAccess::ExternalApps OptionsDlg::getExtApp() const for (int i = 0; i < m_gridCustomCommand->GetNumberRows(); ++i)
{
auto description = copyStringTo<std::wstring>(m_gridCustomCommand->GetCellValue(i, 0));
- auto commandline = utfCvrtTo<Zstring> (m_gridCustomCommand->GetCellValue(i, 1));
+ auto commandline = utfTo<Zstring> (m_gridCustomCommand->GetCellValue(i, 1));
//try to undo translation of description for GlobalSettings.xml
auto it = descriptionTransToEng.find(description);
@@ -952,7 +952,7 @@ DownloadProgressWindow::Impl::Impl(wxWindow* parent, const Zstring& filePath, ui m_gaugeProgress->SetRange(GAUGE_FULL_RANGE);
- m_staticTextDetails->SetLabel(utfCvrtTo<std::wstring>(filePath));
+ m_staticTextDetails->SetLabel(utfTo<std::wstring>(filePath));
updateGui();
diff --git a/FreeFileSync/Source/ui/sorting.h b/FreeFileSync/Source/ui/sorting.h index 61063783..33c262e4 100755 --- a/FreeFileSync/Source/ui/sorting.h +++ b/FreeFileSync/Source/ui/sorting.h @@ -52,7 +52,7 @@ bool lessShortFileName(const FileSystemObject& a, const FileSystemObject& b) return true;
//sort directories and files/symlinks by short name
- return makeSortDirection(LessNoCase() /*even on Linux*/, Int2Type<ascending>())(a.getItemName<side>(), b.getItemName<side>());
+ return makeSortDirection(LessNaturalSort() /*even on Linux*/, Int2Type<ascending>())(a.getItemName<side>(), b.getItemName<side>());
}
@@ -65,8 +65,9 @@ bool lessFullPath(const FileSystemObject& a, const FileSystemObject& b) else if (b.isEmpty<side>())
return true;
- return makeSortDirection(LessNoCase() /*even on Linux*/, Int2Type<ascending>())(AFS::getDisplayPath(a.getAbstractPath<side>()),
- AFS::getDisplayPath(b.getAbstractPath<side>()));
+ return makeSortDirection(LessNaturalSort() /*even on Linux*/, Int2Type<ascending>())(
+ utfTo<Zstring>(AFS::getDisplayPath(a.getAbstractPath<side>())),
+ utfTo<Zstring>(AFS::getDisplayPath(b.getAbstractPath<side>())));
}
@@ -75,27 +76,27 @@ bool lessRelativeFolder(const FileSystemObject& a, const FileSystemObject& b) {
const bool isDirectoryA = isDirectoryPair(a);
const Zstring& relFolderA = isDirectoryA ?
- a.getPairRelativePath() : //directory
- beforeLast(a.getPairRelativePath(), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
+ a.getPairRelativePath() :
+ a.parent().getPairRelativePath();
const bool isDirectoryB = isDirectoryPair(b);
const Zstring& relFolderB = isDirectoryB ?
- b.getPairRelativePath() : //directory
- beforeLast(b.getPairRelativePath(), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
+ b.getPairRelativePath() :
+ b.parent().getPairRelativePath();
//compare relative names without filepaths first
- const int rv = cmpFilePath(relFolderA.c_str(), relFolderA.size(),
- relFolderB.c_str(), relFolderB.size());
+ const int rv = CmpNaturalSort()(relFolderA.c_str(), relFolderA.size(),
+ relFolderB.c_str(), relFolderB.size());
if (rv != 0)
return makeSortDirection(std::less<int>(), Int2Type<ascending>())(rv, 0);
- //compare the filepaths
- if (isDirectoryB) //directories shall appear before files
+ //make directories always appear before contained files
+ if (isDirectoryB)
return false;
else if (isDirectoryA)
return true;
- return LessNoCase() /*even on Linux*/(a.getPairItemName(), b.getPairItemName());
+ return makeSortDirection(LessNaturalSort(), Int2Type<ascending>())(a.getPairItemName(), b.getPairItemName());
}
@@ -173,7 +174,7 @@ bool lessExtension(const FileSystemObject& a, const FileSystemObject& b) return afterLast(fsObj.getItemName<side>(), Zchar('.'), zen::IF_MISSING_RETURN_NONE);
};
- return makeSortDirection(LessNoCase() /*even on Linux*/, Int2Type<ascending>())(getExtension(a), getExtension(b));
+ return makeSortDirection(LessNaturalSort() /*even on Linux*/, Int2Type<ascending>())(getExtension(a), getExtension(b));
}
diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp index 1d47c4e6..ffe4de38 100755 --- a/FreeFileSync/Source/ui/sync_cfg.cpp +++ b/FreeFileSync/Source/ui/sync_cfg.cpp @@ -323,8 +323,10 @@ ConfigDialog::ConfigDialog(wxWindow* parent, m_listBoxFolderPair->Append(_("Main config"));
for (const LocalPairConfig& cfg : folderPairConfig)
{
- const bool pairNameEmpty = trimCpy(cfg.folderPairName).empty();
- m_listBoxFolderPair->Append(L" " + (pairNameEmpty ? L"<" + _("empty") + L">" : cfg.folderPairName));
+ auto fpName = utfTo<std::wstring>(cfg.folderPairName);
+ if (trimCpy(fpName).empty())
+ fpName = L"<" + _("empty") + L">";
+ m_listBoxFolderPair->Append(L" " + fpName);
}
if (folderPairConfig.empty())
@@ -584,8 +586,8 @@ void ConfigDialog::onFilterKeyEvent(wxKeyEvent& event) FilterConfig ConfigDialog::getFilterConfig() const
{
- Zstring includeFilter = utfCvrtTo<Zstring>(m_textCtrlInclude->GetValue());
- Zstring exludeFilter = utfCvrtTo<Zstring>(m_textCtrlExclude->GetValue());
+ Zstring includeFilter = utfTo<Zstring>(m_textCtrlInclude->GetValue());
+ Zstring exludeFilter = utfTo<Zstring>(m_textCtrlExclude->GetValue());
return FilterConfig(includeFilter, exludeFilter,
@@ -600,8 +602,8 @@ FilterConfig ConfigDialog::getFilterConfig() const void ConfigDialog::setFilterConfig(const FilterConfig& filter)
{
- m_textCtrlInclude->ChangeValue(utfCvrtTo<wxString>(filter.includeFilter));
- m_textCtrlExclude->ChangeValue(utfCvrtTo<wxString>(filter.excludeFilter));
+ m_textCtrlInclude->ChangeValue(utfTo<wxString>(filter.includeFilter));
+ m_textCtrlExclude->ChangeValue(utfTo<wxString>(filter.excludeFilter));
setEnumVal(enumTimeDescr_, *m_choiceUnitTimespan, filter.unitTimeSpan);
setEnumVal(enumSizeDescr_, *m_choiceUnitMinSize, filter.unitSizeMin);
@@ -987,7 +989,7 @@ void ConfigDialog::updateSyncGui() {
updateTooltipEnumVal(enumVersioningStyle_, *m_choiceVersioningStyle);
- const std::wstring pathSep = utfCvrtTo<std::wstring>(FILE_NAME_SEPARATOR);
+ const std::wstring pathSep = utfTo<std::wstring>(FILE_NAME_SEPARATOR);
switch (getEnumVal(enumVersioningStyle_, *m_choiceVersioningStyle))
{
case VersioningStyle::REPLACE:
diff --git a/FreeFileSync/Source/ui/sync_cfg.h b/FreeFileSync/Source/ui/sync_cfg.h index b5034ba6..ef8999a2 100755 --- a/FreeFileSync/Source/ui/sync_cfg.h +++ b/FreeFileSync/Source/ui/sync_cfg.h @@ -31,7 +31,7 @@ enum class SyncConfigPanel struct LocalPairConfig
{
- std::wstring folderPairName; //read-only!
+ Zstring folderPairName; //read-only!
std::shared_ptr<const CompConfig> altCmpConfig; //optional
std::shared_ptr<const SyncConfig> altSyncConfig; //
FilterConfig localFilter;
diff --git a/FreeFileSync/Source/ui/tree_view.cpp b/FreeFileSync/Source/ui/tree_view.cpp index 90ba9085..ec38b1dd 100755 --- a/FreeFileSync/Source/ui/tree_view.cpp +++ b/FreeFileSync/Source/ui/tree_view.cpp @@ -43,7 +43,7 @@ void TreeView::compressNode(Container& cont) //remove single-element sub-trees - template <class Function> //(const FileSystemObject&) -> bool
-void TreeView::extractVisibleSubtree(HierarchyObject& hierObj, //in
+void TreeView::extractVisibleSubtree(ContainerObject& hierObj, //in
TreeView::Container& cont, //out
Function pred)
{
@@ -155,63 +155,45 @@ void calcPercentage(std::vector<std::pair<uint64_t, int*>>& workList) }
-std::wstring zen::getShortDisplayNameForFolderPair(const std::wstring& displayPathLeft, const std::wstring& displayPathRight)
+Zstring zen::getShortDisplayNameForFolderPair(const AbstractPath& itemPathL, const AbstractPath& itemPathR)
{
- warn_static("refactor using AfsPath")
- warn_static("next: get rid of int cmpFilePath(const wchar_t*) on Linux/OS X!")
-
- const wchar_t sep = L'/';
- std::wstring fmtPathL = displayPathLeft;
- std::wstring fmtPathR = displayPathRight;
- if (!startsWith(fmtPathL, sep)) fmtPathL = sep + fmtPathL;
- if (!startsWith(fmtPathR, sep)) fmtPathR = sep + fmtPathR;
-
- auto itL = fmtPathL.end();
- auto itR = fmtPathR.end();
+ Zstring commonTrail;
+ AbstractPath tmpPathL = itemPathL;
+ AbstractPath tmpPathR = itemPathR;
for (;;)
{
- auto itLPrev = find_last(fmtPathL.begin(), itL, sep); //c:\file, d:\1file have no common postfix!
- auto itRPrev = find_last(fmtPathR.begin(), itR, sep);
-
- if (itLPrev == itL ||
- itRPrev == itR)
+ Opt<AbstractPath> parentPathL = AFS::getParentFolderPath(tmpPathL);
+ Opt<AbstractPath> parentPathR = AFS::getParentFolderPath(tmpPathR);
+ if (!parentPathL || !parentPathR)
break;
- if (cmpFilePath(&*itLPrev, itL - itLPrev,
- &*itRPrev, itR - itRPrev) != 0)
+ const Zstring itemNameL = AFS::getItemName(tmpPathL);
+ const Zstring itemNameR = AFS::getItemName(tmpPathR);
+ if (!strEqual(itemNameL, itemNameR, CmpNaturalSort())) //let's compare case-insensitively even on Linux!
break;
- itL = itLPrev;
- itR = itRPrev;
- }
- if (itL != fmtPathL.end() && *itL == sep)
- ++itL;
+ tmpPathL = *parentPathL;
+ tmpPathR = *parentPathR;
- const size_t postFixLen = fmtPathL.end() - itL;
- if (postFixLen > 0)
- return std::wstring(&*(displayPathLeft.end() - postFixLen), postFixLen);
+ commonTrail = AFS::appendPaths(itemNameL, commonTrail, FILE_NAME_SEPARATOR);
+ }
+ if (!commonTrail.empty())
+ return commonTrail;
- auto getLastComponent = [sep](const std::wstring& displayPath) -> std::wstring
+ auto getLastComponent = [](const AbstractPath& itemPath)
{
- const std::wstring fmtPath = displayPath;
- auto itEnd = fmtPath.end();
- if (endsWith(fmtPath, sep)) //preserve trailing separator, support "C:\"
- --itEnd;
-
- auto it = find_last(fmtPath.begin(), itEnd, sep);
- if (it == itEnd)
- it = fmtPath.begin();
- else ++it;
-
- return displayPath.c_str() + (it - fmtPath.begin());
+ if (!AFS::getParentFolderPath(itemPath)) //= device root
+ return utfTo<Zstring>(AFS::getDisplayPath(itemPath));
+ return AFS::getItemName(itemPath);
};
- if (displayPathLeft.empty())
- return getLastComponent(displayPathRight);
- else if (displayPathRight.empty())
- return getLastComponent(displayPathLeft);
+
+ if (AFS::isNullPath(itemPathL))
+ return getLastComponent(itemPathR);
+ else if (AFS::isNullPath(itemPathR))
+ return getLastComponent(itemPathL);
else
- return getLastComponent(displayPathLeft) + L" \u2212 " + //= unicode minus
- getLastComponent(displayPathRight);
+ return getLastComponent(itemPathL) + utfTo<Zstring>(L" \u2212 ") + //= unicode minus
+ getLastComponent(itemPathR);
}
@@ -232,8 +214,8 @@ struct TreeView::LessShortName switch (lhs.type_)
{
case TreeView::TYPE_ROOT:
- return makeSortDirection(LessNoCase() /*even on Linux*/, Int2Type<ascending>())(static_cast<const RootNodeImpl*>(lhs.node_)->displayName,
- static_cast<const RootNodeImpl*>(rhs.node_)->displayName);
+ return makeSortDirection(LessNaturalSort() /*even on Linux*/, Int2Type<ascending>())(static_cast<const RootNodeImpl*>(lhs.node_)->displayName,
+ static_cast<const RootNodeImpl*>(rhs.node_)->displayName);
case TreeView::TYPE_DIRECTORY:
{
@@ -245,7 +227,7 @@ struct TreeView::LessShortName else if (!folderR)
return true;
- return makeSortDirection(LessNoCase() /*even on Linux*/, Int2Type<ascending>())(folderL->getPairItemName(), folderR->getPairItemName());
+ return makeSortDirection(LessNaturalSort() /*even on Linux*/, Int2Type<ascending>())(folderL->getPairItemName(), folderR->getPairItemName());
}
case TreeView::TYPE_FILES:
@@ -336,7 +318,7 @@ void TreeView::getChildren(const Container& cont, unsigned int level, std::vecto void TreeView::applySubView(std::vector<RootNodeImpl>&& newView)
{
//preserve current node expansion status
- auto getHierAlias = [](const TreeView::TreeLine& tl) -> const HierarchyObject*
+ auto getHierAlias = [](const TreeView::TreeLine& tl) -> const ContainerObject*
{
switch (tl.type_)
{
@@ -354,7 +336,7 @@ void TreeView::applySubView(std::vector<RootNodeImpl>&& newView) return nullptr;
};
- std::unordered_set<const HierarchyObject*> expandedNodes;
+ std::unordered_set<const ContainerObject*> expandedNodes;
if (!flatTree.empty())
{
auto it = flatTree.begin();
@@ -434,8 +416,8 @@ void TreeView::updateView(Predicate pred) else
{
root.baseFolder = baseObj;
- root.displayName = getShortDisplayNameForFolderPair(AFS::getDisplayPath(baseObj->getAbstractPath< LEFT_SIDE>()),
- AFS::getDisplayPath(baseObj->getAbstractPath<RIGHT_SIDE>()));
+ root.displayName = getShortDisplayNameForFolderPair(baseObj->getAbstractPath< LEFT_SIDE>(),
+ baseObj->getAbstractPath<RIGHT_SIDE>());
this->compressNode(root); //"this->" required by two-pass lookup as enforced by GCC 4.7
}
@@ -706,7 +688,7 @@ std::unique_ptr<TreeView::Node> TreeView::getLine(size_t row) const if (auto firstFile = FileSystemObject::retrieve(parentDir->firstFileId))
{
std::vector<FileSystemObject*> filesAndLinks;
- HierarchyObject& parent = firstFile->parent();
+ ContainerObject& parent = firstFile->parent();
//lazy evaluation: recheck "lastViewFilterPred" again rather than buffer and bloat "lastViewFilterPred"
for (FileSystemObject& fsObj : parent.refSubFiles())
@@ -832,9 +814,9 @@ private: {
case ColumnTypeNavi::FOLDER_NAME:
if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get()))
- return root->displayName_;
+ return utfTo<std::wstring>(root->displayName_);
else if (const TreeView::DirNode* dir = dynamic_cast<const TreeView::DirNode*>(node.get()))
- return utfCvrtTo<std::wstring>(dir->folder_.getPairItemName());
+ return utfTo<std::wstring>(dir->folder_.getPairItemName());
else if (dynamic_cast<const TreeView::FilesNode*>(node.get()))
return _("Files");
break;
diff --git a/FreeFileSync/Source/ui/tree_view.h b/FreeFileSync/Source/ui/tree_view.h index 6d9f965a..e135c3f4 100755 --- a/FreeFileSync/Source/ui/tree_view.h +++ b/FreeFileSync/Source/ui/tree_view.h @@ -83,11 +83,11 @@ public: struct RootNode : public Node
{
- RootNode(int percent, uint64_t bytes, int itemCount, NodeStatus status, BaseFolderPair& baseFolder, const std::wstring& displayName) :
+ RootNode(int percent, uint64_t bytes, int itemCount, NodeStatus status, BaseFolderPair& baseFolder, const Zstring& displayName) :
Node(percent, bytes, itemCount, 0, status), baseFolder_(baseFolder), displayName_(displayName) {}
BaseFolderPair& baseFolder_;
- const std::wstring displayName_;
+ const Zstring displayName_;
};
std::unique_ptr<Node> getLine(size_t row) const; //return nullptr on error
@@ -115,7 +115,7 @@ private: std::vector<DirNodeImpl> subDirs;
FileSystemObject::ObjectId firstFileId = nullptr; //weak pointer to first FilePair or SymlinkPair
//- "compress" algorithm may hide file nodes for directories with a single included file, i.e. itemCountGross == itemCountNet == 1
- //- a HierarchyObject* would be a better fit, but we need weak pointer semantics!
+ //- a ContainerObject* would be a better fit, but we need weak pointer semantics!
//- a std::vector<FileSystemObject::ObjectId> would be a better design, but we don't want a second memory structure as large as custom grid!
};
@@ -127,7 +127,7 @@ private: struct RootNodeImpl : public Container
{
std::shared_ptr<BaseFolderPair> baseFolder;
- std::wstring displayName;
+ Zstring displayName;
};
enum NodeType
@@ -149,7 +149,7 @@ private: static void compressNode(Container& cont);
template <class Function>
- static void extractVisibleSubtree(HierarchyObject& hierObj, Container& cont, Function includeObject);
+ static void extractVisibleSubtree(ContainerObject& hierObj, Container& cont, Function includeObject);
void getChildren(const Container& cont, unsigned int level, std::vector<TreeLine>& output);
template <class Predicate> void updateView(Predicate pred);
void applySubView(std::vector<RootNodeImpl>&& newView);
@@ -173,7 +173,7 @@ private: };
-std::wstring getShortDisplayNameForFolderPair(const std::wstring& displayPathLeft, const std::wstring& displayPathRight);
+Zstring getShortDisplayNameForFolderPair(const AbstractPath& itemPathL, const AbstractPath& itemPathR);
namespace treeview
diff --git a/FreeFileSync/Source/ui/version_check.cpp b/FreeFileSync/Source/ui/version_check.cpp index a63257ed..f8f2e661 100755 --- a/FreeFileSync/Source/ui/version_check.cpp +++ b/FreeFileSync/Source/ui/version_check.cpp @@ -73,7 +73,7 @@ std::vector<std::pair<std::string, std::string>> geHttpPostParameters() const wxLinuxDistributionInfo distribInfo = wxGetLinuxDistributionInfo();
assert(contains(distribInfo.Release, L'.'));
- std::vector<wxString> digits = split<wxString>(distribInfo.Release, L'.'); //e.g. "15.04"
+ std::vector<wxString> digits = split<wxString>(distribInfo.Release, L'.', SplitType::ALLOW_EMPTY); //e.g. "15.04"
digits.resize(2);
//distribInfo.Id //e.g. "Ubuntu"
@@ -88,8 +88,8 @@ std::vector<std::pair<std::string, std::string>> geHttpPostParameters() params.emplace_back("os_arch", "64");
#endif
- params.emplace_back("language", utfCvrtTo<std::string>(getIso639Language()));
- params.emplace_back("country", utfCvrtTo<std::string>(getIso3166Country()));
+ params.emplace_back("language", utfTo<std::string>(getIso639Language()));
+ params.emplace_back("country", utfTo<std::string>(getIso3166Country()));
return params;
}
@@ -106,7 +106,7 @@ void showUpdateAvailableDialog(wxWindow* parent, const std::string& onlineVersio {
const std::string buf = sendHttpPost(L"http://www.freefilesync.org/get_latest_changes.php", ffsUpdateCheckUserAgent,
nullptr /*notifyUnbufferedIO*/, { { "since", zen::ffsVersion } }).readAll(); //throw SysError
- updateDetailsMsg = utfCvrtTo<std::wstring>(buf);
+ updateDetailsMsg = utfTo<std::wstring>(buf);
}
catch (const zen::SysError& e) { throw FileError(_("Failed to retrieve update information."), e.toString()); }
@@ -119,7 +119,7 @@ void showUpdateAvailableDialog(wxWindow* parent, const std::string& onlineVersio switch (showConfirmationDialog(parent, DialogInfoType::INFO, PopupDialogCfg().
setIcon(getResourceImage(L"update_available")).
setTitle(_("Check for Program Updates")).
- setMainInstructions(_("A new version of FreeFileSync is available:") + L" " + utfCvrtTo<std::wstring>(onlineVersion) + L"\n" + _("Download now?")).
+ setMainInstructions(_("A new version of FreeFileSync is available:") + L" " + utfTo<std::wstring>(onlineVersion) + L"\n" + _("Download now?")).
setDetailInstructions(updateDetailsMsg),
_("&Download")))
{
@@ -145,7 +145,7 @@ std::string getOnlineVersion(const std::vector<std::pair<std::string, std::strin std::vector<size_t> parseVersion(const std::string& version)
{
std::vector<size_t> output;
- for (const std::string& digit : split(version, FFS_VERSION_SEPARATOR))
+ for (const std::string& digit : split(version, FFS_VERSION_SEPARATOR, SplitType::ALLOW_EMPTY))
output.push_back(stringTo<size_t>(digit));
return output;
}
diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h index 76a584c7..1770e515 100755 --- a/FreeFileSync/Source/version/version.h +++ b/FreeFileSync/Source/version/version.h @@ -3,7 +3,7 @@ namespace zen
{
-const char ffsVersion[] = "8.9"; //internal linkage!
+const char ffsVersion[] = "8.10"; //internal linkage!
const char FFS_VERSION_SEPARATOR = '.';
}
diff --git a/wx+/app_main.h b/wx+/app_main.h index 2202ebc5..4b2d92a4 100755 --- a/wx+/app_main.h +++ b/wx+/app_main.h @@ -21,12 +21,6 @@ bool mainWindowWasSet(); -
-
-
-
-
-
//######################## implementation ########################
inline
bool& refMainWndStatus()
diff --git a/wx+/file_drop.cpp b/wx+/file_drop.cpp index cab848b9..f951ca3c 100755 --- a/wx+/file_drop.cpp +++ b/wx+/file_drop.cpp @@ -37,7 +37,7 @@ private: //wxPoint clientDropPos(x, y)
std::vector<Zstring> filePaths;
for (const wxString& file : fileArray)
- filePaths.push_back(utfCvrtTo<Zstring>(file));
+ filePaths.push_back(utfTo<Zstring>(file));
//create a custom event on drop window: execute event after file dropping is completed! (after mouse is released)
if (wxEvtHandler* handler = dropWindow_.GetEventHandler())
diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 75abcd2f..d714ecf5 100755 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -142,7 +142,7 @@ wxSize GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& wxSize extentTrunc = dc.GetTextExtent(textTrunc);
if (extentTrunc.GetWidth() > rect.width)
{
- //unlike Windows 7 Explorer, we truncate UTF-16 correctly: e.g. CJK-Ideogramm encodes to TWO wchar_t: utfCvrtTo<std::wstring>("\xf0\xa4\xbd\x9c");
+ //unlike Windows 7 Explorer, we truncate UTF-16 correctly: e.g. CJK-Ideogramm encodes to TWO wchar_t: utfTo<std::wstring>("\xf0\xa4\xbd\x9c");
size_t low = 0; //number of unicode chars!
size_t high = unicodeLength(text); //
if (high > 1)
@@ -159,7 +159,7 @@ wxSize GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& break;
}
- const std::wstring& candidate = std::wstring(strBegin(text), findUnicodePos(text, middle)) + ELLIPSIS;
+ const std::wstring& candidate = getUnicodeSubstring(text, 0, middle) + ELLIPSIS;
const wxSize extentCand = dc.GetTextExtent(candidate); //perf: most expensive call of this routine!
if (extentCand.GetWidth() <= rect.width)
diff --git a/wx+/http.cpp b/wx+/http.cpp index 851b3ed0..944c771a 100755 --- a/wx+/http.cpp +++ b/wx+/http.cpp @@ -33,9 +33,9 @@ public: {
ZEN_ON_SCOPE_FAIL( cleanup(); /*destructor call would lead to member double clean-up!!!*/ );
- assert(!startsWith(makeUpperCopy(url), L"HTTPS:")); //not supported by wxHTTP!
- const std::wstring urlFmt = startsWith(makeUpperCopy(url), L"HTTP://") ||
- startsWith(makeUpperCopy(url), L"HTTPS://") ? afterFirst(url, L"://", IF_MISSING_RETURN_NONE) : url;
+ assert(!startsWith(url, L"https:", CmpAsciiNoCase())); //not supported by wxHTTP!
+ const std::wstring urlFmt = startsWith(url, L"http://", CmpAsciiNoCase()) ||
+ startsWith(url, L"https://", CmpAsciiNoCase()) ? afterFirst(url, L"://", IF_MISSING_RETURN_NONE) : url;
const std::wstring server = beforeFirst(urlFmt, L'/', IF_MISSING_RETURN_ALL);
const std::wstring page = L'/' + afterFirst(urlFmt, L'/', IF_MISSING_RETURN_NONE);
@@ -49,7 +49,7 @@ public: throw SysError(L"wxHTTP::Connect");
if (postParams)
- if (!webAccess_.SetPostText(L"application/x-www-form-urlencoded", utfCvrtTo<wxString>(*postParams)))
+ if (!webAccess_.SetPostText(L"application/x-www-form-urlencoded", utfTo<wxString>(*postParams)))
throw SysError(L"wxHTTP::SetPostText");
httpStream_.reset(webAccess_.GetInputStream(page)); //pass ownership
@@ -87,7 +87,7 @@ public: if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesRead); //throw X
if (bytesRead == 0) //end of file
- bytesToRead = memBuf_.size();
+ bytesToRead = std::min(bytesToRead, memBuf_.size());
}
std::copy(memBuf_.begin(), memBuf_.begin() + bytesToRead, static_cast<char*>(buffer));
@@ -225,8 +225,7 @@ std::vector<std::pair<std::string, std::string>> zen::xWwwFormUrlDecode(const st {
std::vector<std::pair<std::string, std::string>> output;
- for (const std::string& nvPair : split(str, '&'))
- if (!nvPair.empty())
+ for (const std::string& nvPair : split(str, '&', SplitType::SKIP_EMPTY))
output.emplace_back(urldecode(beforeFirst(nvPair, '=', IF_MISSING_RETURN_ALL)),
urldecode(afterFirst (nvPair, '=', IF_MISSING_RETURN_NONE)));
return output;
diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp index 5cd9df0b..dd8299c2 100755 --- a/wx+/image_resources.cpp +++ b/wx+/image_resources.cpp @@ -76,7 +76,7 @@ void GlobalBitmaps::init(const Zstring& filepath) {
assert(bitmaps.empty() && anims.empty());
- wxFFileInputStream input(utfCvrtTo<wxString>(filepath));
+ wxFFileInputStream input(utfTo<wxString>(filepath));
if (input.IsOk()) //if not... we don't want to react too harsh here
{
//activate support for .png files
diff --git a/wx+/string_conv.h b/wx+/string_conv.h deleted file mode 100755 index 5db49ff2..00000000 --- a/wx+/string_conv.h +++ /dev/null @@ -1,28 +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 freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef STRING_CONV_H_893217450815743
-#define STRING_CONV_H_893217450815743
-
-#include <zen/utf.h>
-#include <wx/string.h>
-#include <zen/zstring.h>
-
-namespace zen
-{
-//conversion between Zstring and wxString
-inline wxString toWx(const Zstring& str) { return utfCvrtTo<wxString>(str); }
-inline Zstring toZ(const wxString& str) { return utfCvrtTo<Zstring>(str); }
-
-inline std::vector<Zstring> toZ(const std::vector<wxString>& strList)
-{
- std::vector<Zstring> tmp;
- std::transform(strList.begin(), strList.end(), std::back_inserter(tmp), [](const wxString& str) { return toZ(str); });
- return tmp;
-}
-}
-
-#endif //STRING_CONV_H_893217450815743
diff --git a/wx+/timespan.h b/wx+/timespan.h deleted file mode 100755 index b165351f..00000000 --- a/wx+/timespan.h +++ /dev/null @@ -1,166 +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 freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef TIMESPAN_H_254783756533456
-#define TIMESPAN_H_254783756533456
-
-#include <wx/textctrl.h>
-#include <wx/datetime.h>
-#include <wx/spinbutt.h>
-#include <wx/sizer.h>
-
-//user friendly time span control
-//- constructor is compatible with a wxTextControl
-//- emits change event: wxEVT_TIMESPAN_CHANGE
-
-namespace zen
-{
-inline
-wxEventType getEventType()
-{
- static wxEventType evt = wxNewEventType(); //external linkage!
- return evt;
-}
-const wxEventType wxEVT_TIMESPAN_CHANGE = getEventType();
-
-
-class TimeSpanCtrl : public wxPanel
-{
-public:
- TimeSpanCtrl(wxWindow* parent, wxWindowID id,
- const wxString& value = {},
- const wxPoint& pos = wxDefaultPosition,
- const wxSize& size = wxDefaultSize,
- long style = 0,
- const wxValidator& validator = wxDefaultValidator,
- const wxString& name = wxTextCtrlNameStr) :
- wxPanel(parent, id, pos, size, style, name),
- FORMAT_TIMESPAN(wxT("%H:%M:%S"))
- {
- wxBoxSizer* bSizer27 = new wxBoxSizer( wxHORIZONTAL );
-
- m_textCtrl = new wxTextCtrl(this, wxID_ANY, wxString(), wxDefaultPosition, wxDefaultSize, wxTE_CENTRE );
- bSizer27->Add(m_textCtrl, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND, 5 );
-
- m_spinBtn = new wxSpinButton(this, wxID_ANY, wxDefaultPosition, wxSize( 20, -1 ), wxSP_ARROW_KEYS );
- bSizer27->Add(m_spinBtn, 0, wxALIGN_CENTER_VERTICAL | wxEXPAND, 5 );
-
- SetSizer(bSizer27);
- Layout();
-
- //connect events
- m_spinBtn ->Connect(wxEVT_SCROLL_LINEUP, wxEventHandler (TimeSpanCtrl::OnSpinUp), nullptr, this);
- m_spinBtn ->Connect(wxEVT_SCROLL_LINEDOWN, wxEventHandler (TimeSpanCtrl::OnSpinDown), nullptr, this);
- m_textCtrl->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (TimeSpanCtrl::OnKeyPress), nullptr, this);
- m_textCtrl->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(TimeSpanCtrl::OnMouseAction), nullptr, this);
-
- setValue(0);
- }
-
- void setValue(int span) //unit: [s]
- {
- wxString newValue;
- if (span < 0)
- {
- newValue += wxT("- ");
- span = -span;
- }
- newValue += wxTimeSpan::Seconds(span).Format(FORMAT_TIMESPAN);
-
- long pos = m_textCtrl->GetInsertionPoint();
- pos += newValue.size() - m_textCtrl->GetValue().size();
-
- m_textCtrl->ChangeValue(newValue);
- m_textCtrl->SetInsertionPoint(pos);
-
- wxCommandEvent chgEvent(wxEVT_TIMESPAN_CHANGE);
- wxPostEvent(this, chgEvent);
- }
-
- int getValue() const
- {
- wxString textVal = m_textCtrl->GetValue();
- textVal.Trim(false);
-
- bool isNegative = false;
- if (textVal.StartsWith(wxT("-")))
- {
- isNegative = true;
- textVal = textVal.substr(1);
- }
- textVal.Trim(false);
-
- wxDateTime tmp(time_t(0));
- if (tmp.ParseFormat(textVal, FORMAT_TIMESPAN, wxDateTime(tmp)) == nullptr)
- return 0;
-
- return (isNegative ? -1 : 1) *
- (tmp.GetHour () * 3600 +
- tmp.GetMinute() * 60 +
- tmp.GetSecond());
- }
-
-private:
- void OnSpinUp (wxEvent& event) { spinValue(true); }
- void OnSpinDown(wxEvent& event) { spinValue(false); }
-
- void OnKeyPress(wxKeyEvent& event)
- {
- const int keyCode = event.GetKeyCode();
- switch (keyCode)
- {
- case WXK_UP:
- case WXK_NUMPAD_UP:
- return spinValue(true);
- case WXK_DOWN:
- case WXK_NUMPAD_DOWN:
- return spinValue(false);
- default:
- event.Skip();
- }
- }
-
- void OnMouseAction(wxMouseEvent& event)
- {
- int delta = event.GetWheelRotation();
- if (delta > 0)
- spinValue(true);
- else if (delta < 0)
- spinValue(false);
- else
- event.Skip();
- }
-
- void spinValue(bool up)
- {
- wxString textval = m_textCtrl->GetValue();
- long pos = m_textCtrl->GetInsertionPoint();
-
- int stepSize = 1;
- if (pos <= static_cast<long>(textval.size()))
- {
- int delimCount = std::count(textval.begin() + pos, textval.end(), wxT(':'));
- if (delimCount == 1)
- stepSize = 60; //minute
- else if (delimCount == 2)
- stepSize = 3600; //hour
- }
-
- if (!up)
- stepSize *= -1;
-
- setValue(getValue() + stepSize);
- }
-
- wxTextCtrl* m_textCtrl;
- wxSpinButton* m_spinBtn;
-
- const wxString FORMAT_TIMESPAN;
-};
-}
-
-
-#endif //TIMESPAN_H_254783756533456
diff --git a/zen/file_access.cpp b/zen/file_access.cpp index 61a003bb..71d00386 100755 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -27,7 +27,7 @@ using namespace zen;
-Opt<PathComponents> zen::getPathComponents(const Zstring& itemPath)
+Opt<PathComponents> zen::parsePathComponents(const Zstring& itemPath)
{
if (startsWith(itemPath, "/"))
{
@@ -44,7 +44,7 @@ Opt<PathComponents> zen::getPathComponents(const Zstring& itemPath) Opt<Zstring> zen::getParentFolderPath(const Zstring& itemPath)
{
- if (const Opt<PathComponents> comp = getPathComponents(itemPath))
+ if (const Opt<PathComponents> comp = parsePathComponents(itemPath))
{
if (comp->relPath.empty())
return NoValue();
@@ -73,7 +73,7 @@ ItemType zen::getItemType(const Zstring& itemPath) //throw FileError }
-PathDetails zen::getPathDetails(const Zstring& itemPath) //throw FileError
+PathStatus zen::getPathStatus(const Zstring& itemPath) //throw FileError
{
const Opt<Zstring> parentPath = getParentFolderPath(itemPath);
try
@@ -91,7 +91,7 @@ PathDetails zen::getPathDetails(const Zstring& itemPath) //throw FileError const Zstring itemName = afterLast(itemPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
assert(!itemName.empty());
- PathDetails pd = getPathDetails(*parentPath); //throw FileError
+ PathStatus pd = getPathStatus(*parentPath); //throw FileError
if (!pd.relPath.empty())
{
pd.relPath.push_back(itemName);
@@ -115,7 +115,7 @@ PathDetails zen::getPathDetails(const Zstring& itemPath) //throw FileError Opt<ItemType> zen::getItemTypeIfExists(const Zstring& itemPath) //throw FileError
{
- const PathDetails pd = getPathDetails(itemPath); //throw FileError
+ const PathStatus pd = getPathStatus(itemPath); //throw FileError
if (pd.relPath.empty())
return pd.existingType;
return NoValue();
@@ -502,8 +502,8 @@ void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw File }
catch (FileError&)
{
- Opt<PathDetails> pd;
- try { pd = getPathDetails(dirPath); /*throw FileError*/ }
+ Opt<PathStatus> pd;
+ try { pd = getPathStatus(dirPath); /*throw FileError*/ }
catch (FileError&) {} //previous exception is more relevant
if (pd && pd->existingType != ItemType::FILE)
diff --git a/zen/file_access.h b/zen/file_access.h index c3a52f8a..a6b221e5 100755 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -22,7 +22,7 @@ struct PathComponents Zstring rootPath; //itemPath = rootPath + (FILE_NAME_SEPARATOR?) + relPath
Zstring relPath; //
};
-Opt<PathComponents> getPathComponents(const Zstring& itemPath); //no value on failure
+Opt<PathComponents> parsePathComponents(const Zstring& itemPath); //no value on failure
Opt<Zstring> getParentFolderPath(const Zstring& itemPath);
@@ -43,13 +43,13 @@ ItemType getItemType (const Zstring& itemPath); //throw FileError //execute potentially SLOW folder traversal but distinguish error/not existing
Opt<ItemType> getItemTypeIfExists(const Zstring& itemPath); //throw FileError
-struct PathDetails
+struct PathStatus
{
ItemType existingType;
Zstring existingPath; //itemPath =: existingPath + relPath
std::vector<Zstring> relPath; //
};
-PathDetails getPathDetails(const Zstring& itemPath); //throw FileError
+PathStatus getPathStatus(const Zstring& itemPath); //throw FileError
enum class ProcSymlink
{
diff --git a/zen/file_error.h b/zen/file_error.h index 87f9525b..949c644f 100755 --- a/zen/file_error.h +++ b/zen/file_error.h @@ -46,7 +46,7 @@ DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume); //----------- facilitate usage of std::wstring for error messages --------------------
inline std::wstring fmtPath(const std::wstring& displayPath) { return L'\"' + displayPath + L'\"'; }
-inline std::wstring fmtPath(const Zstring& displayPath) { return fmtPath(utfCvrtTo<std::wstring>(displayPath)); }
+inline std::wstring fmtPath(const Zstring& displayPath) { return fmtPath(utfTo<std::wstring>(displayPath)); }
inline std::wstring fmtPath(const wchar_t* displayPath) { return fmtPath(std::wstring(displayPath)); } //resolve overload ambiguity
}
diff --git a/zen/file_io.cpp b/zen/file_io.cpp index b4affd37..0c5ff490 100755 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -140,7 +140,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, X; r if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesRead); //throw X
if (bytesRead == 0) //end of file
- bytesToRead = memBuf_.size();
+ bytesToRead = std::min(bytesToRead, memBuf_.size());
}
std::copy(memBuf_.begin(), memBuf_.begin() + bytesToRead, static_cast<char*>(buffer));
@@ -185,9 +185,10 @@ FileOutput::FileOutput(const Zstring& filePath, AccessFlag access, const IOCallb FileOutput::~FileOutput()
{
+ notifyUnbufferedIO_ = nullptr; //no call-backs during destruction!!!
try
{
- flushBuffers(); //throw FileError, X
+ flushBuffers(); //throw FileError, (X)
}
catch (...) { assert(false); }
}
diff --git a/zen/file_io.h b/zen/file_io.h index 8a5e0f7f..827abd9e 100755 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -90,7 +90,7 @@ private: size_t tryWrite(const void* buffer, size_t bytesToWrite); //throw FileError; may return short! CONTRACT: bytesToWrite > 0
std::vector<char> memBuf_;
- const IOCallback notifyUnbufferedIO_; //throw X
+ IOCallback notifyUnbufferedIO_; //throw X
};
//-----------------------------------------------------------------------------------------------
diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index cf17c8d4..a2208b3e 100755 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -5,7 +5,7 @@ // *****************************************************************************
#include "format_unit.h"
-#include <cwchar> //swprintf
+//#include <cwchar> //swprintf
#include <ctime>
#include <cstdio>
#include "basic_math.h"
@@ -168,7 +168,7 @@ std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number) //::setlocale (LC_ALL, ""); -> implicitly called by wxLocale
const lconv* localInfo = ::localeconv(); //always bound according to doc
- const std::wstring& thousandSep = utfCvrtTo<std::wstring>(localInfo->thousands_sep);
+ const std::wstring& thousandSep = utfTo<std::wstring>(localInfo->thousands_sep);
// THOUSANDS_SEPARATOR = std::use_facet<std::numpunct<wchar_t>>(std::locale("")).thousands_sep(); - why not working?
// DECIMAL_POINT = std::use_facet<std::numpunct<wchar_t>>(std::locale("")).decimal_point();
diff --git a/zen/globals.h b/zen/globals.h index a1fd2764..b6c5dd28 100755 --- a/zen/globals.h +++ b/zen/globals.h @@ -18,7 +18,11 @@ template <class T> class Global
{
public:
- Global() { static_assert(std::is_trivially_destructible<Pod>::value, "this memory needs to live forever"); }
+ Global()
+ {
+ static_assert(std::is_trivially_destructible<Pod>::value, "this memory needs to live forever");
+ assert(!pod.inst && !pod.spinLock); //we depend on static zero-initialization!
+ }
explicit Global(std::unique_ptr<T>&& newInst) { set(std::move(newInst)); }
~Global() { set(nullptr); }
@@ -50,9 +54,9 @@ private: //=> use trivially-destructible POD only!!!
struct Pod
{
- std::shared_ptr<T>* inst = nullptr;
+ std::shared_ptr<T>* inst; // = nullptr;
+ std::atomic<bool> spinLock; // { false }; rely entirely on static zero-initialization! => avoid potential contention with worker thread during Global<> construction!
//serialize access; can't use std::mutex: has non-trival destructor
- std::atomic<bool> spinLock { false };
} pod;
};
diff --git a/zen/recycler.cpp b/zen/recycler.cpp index 02ea026a..0c71bf3b 100755 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -45,7 +45,7 @@ bool zen::recycleOrDeleteIfExists(const Zstring& itemPath) //throw FileError return true;
}
- throw FileError(errorMsg, replaceCpy<std::wstring>(L"Glib Error Code %x:", L"%x", numberTo<std::wstring>(error->code)) + L" " + utfCvrtTo<std::wstring>(error->message));
+ throw FileError(errorMsg, formatSystemError(L"g_file_trash", L"Glib Error Code " + numberTo<std::wstring>(error->code), utfTo<std::wstring>(error->message)));
//g_quark_to_string(error->domain)
}
return true;
diff --git a/zen/scope_guard.h b/zen/scope_guard.h index 09a7fbdb..62552f7b 100755 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -13,7 +13,7 @@ //std::uncaught_exceptions() currently unsupported on GCC and Clang => clean up ASAP
- static_assert(__GNUC__ < 6 || (__GNUC__ == 6 && (__GNUC_MINOR__ < 2 || (__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support");
+ static_assert(__GNUC__ < 6 || (__GNUC__ == 6 && (__GNUC_MINOR__ < 3 || (__GNUC_MINOR__ == 3 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support");
namespace __cxxabiv1
{
diff --git a/zen/serialize.h b/zen/serialize.h index bb2f7a45..c8dfb96d 100755 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -241,6 +241,7 @@ template <class BufferedInputStream> inline void readArray(BufferedInputStream& stream, void* buffer, size_t len) //throw UnexpectedEndOfStreamError
{
const size_t bytesRead = stream.read(buffer, len);
+ assert(bytesRead <= len); //buffer overflow otherwise not always detected!
if (bytesRead < len)
throw UnexpectedEndOfStreamError();
}
diff --git a/zen/shell_execute.h b/zen/shell_execute.h index 9ba0aef0..5e4ddf1a 100755 --- a/zen/shell_execute.h +++ b/zen/shell_execute.h @@ -41,7 +41,7 @@ void shellExecute(const Zstring& command, ExecutionType type) //throw FileError //Posix::system - execute a shell command
int rv = ::system(command.c_str()); //do NOT use std::system as its documentation says nothing about "WEXITSTATUS(rv)", ect...
if (rv == -1 || WEXITSTATUS(rv) == 127) //http://linux.die.net/man/3/system "In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127)"
- throw FileError(_("Incorrect command line:") + L"\n" + utfCvrtTo<std::wstring>(command));
+ throw FileError(_("Incorrect command line:") + L"\n" + utfTo<std::wstring>(command));
}
else
runAsync([=] { int rv = ::system(command.c_str()); (void)rv; });
diff --git a/zen/string_base.h b/zen/string_base.h index 3afa66c6..b5e45c0e 100755 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -264,8 +264,8 @@ public: void push_back(Char val) { operator+=(val); } //STL access
void pop_back();
- Zbase& operator=(const Zbase& str);
Zbase& operator=(Zbase&& tmp) noexcept;
+ Zbase& operator=(const Zbase& str);
Zbase& operator=(const Char* str) { return assign(str, strLength(str)); }
Zbase& operator=(Char ch) { return assign(&ch, 1); }
Zbase& operator+=(const Zbase& str) { return append(str.c_str(), str.length()); }
@@ -573,11 +573,14 @@ template <class InputIterator> inline Zbase<Char, SP>& Zbase<Char, SP>::append(InputIterator first, InputIterator last)
{
const size_t len = std::distance(first, last);
- const size_t thisLen = length();
- reserve(thisLen + len); //make unshared and check capacity
-
- *std::copy(first, last, rawStr_ + thisLen) = 0;
- this->setLength(rawStr_, thisLen + len);
+ if (len > 0) //avoid making this string unshared for no reason
+ {
+ const size_t thisLen = length();
+ reserve(thisLen + len); //make unshared and check capacity
+
+ *std::copy(first, last, rawStr_ + thisLen) = 0;
+ this->setLength(rawStr_, thisLen + len);
+ }
return *this;
}
diff --git a/zen/string_tools.h b/zen/string_tools.h index 5a82e0ed..236f8df6 100755 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -25,11 +25,31 @@ namespace zen template <class Char> bool isWhiteSpace(Char ch);
template <class Char> bool isDigit (Char ch); //not exactly the same as "std::isdigit" -> we consider '0'-'9' only!
template <class Char> bool isHexDigit (Char ch);
-template <class Char> bool isAlpha (Char ch);
+template <class Char> bool isAsciiAlpha(Char ch);
-template <class S, class T> bool startsWith(const S& str, const T& prefix); //
-template <class S, class T> bool endsWith (const S& str, const T& postfix); //both S and T can be strings or char/wchar_t arrays or simple char/wchar_t
-template <class S, class T> bool contains (const S& str, const T& term); //
+//case-sensitive comparison (compile-time correctness: use different number of arguments as STL comparison predicates!)
+struct CmpBinary { template <class Char> int operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const; };
+
+//basic case-insensitive comparison (considering A-Z only!)
+struct CmpAsciiNoCase { template <class Char> int operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const; };
+
+struct LessAsciiNoCase
+{
+ template <class S> //don't support heterogenous input! => use as container predicate only!
+ bool operator()(const S& lhs, const S& rhs) const { return CmpAsciiNoCase()(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; }
+};
+
+//both S and T can be strings or char/wchar_t arrays or simple char/wchar_t
+template <class S, class T> bool contains(const S& str, const T& term);
+
+template <class S, class T> bool startsWith(const S& str, const T& prefix);
+template <class S, class T, class Function> bool startsWith(const S& str, const T& prefix, Function cmpStringFun);
+
+template <class S, class T> bool endsWith (const S& str, const T& postfix);
+template <class S, class T, class Function> bool endsWith (const S& str, const T& postfix, Function cmpStringFun);
+
+template <class S, class T> bool strEqual(const S& lhs, const T& rhs);
+template <class S, class T, class Function> bool strEqual(const S& lhs, const T& rhs, Function cmpStringFun);
enum FailureReturnVal
{
@@ -42,16 +62,23 @@ template <class S, class T> S beforeLast (const S& str, const T& term, FailureRe template <class S, class T> S afterFirst (const S& str, const T& term, FailureReturnVal rv);
template <class S, class T> S beforeFirst(const S& str, const T& term, FailureReturnVal rv);
-template <class S, class T> std::vector<S> split(const S& str, const T& delimiter);
-template <class S> S trimCpy(S str, bool fromLeft = true, bool fromRight = true);
-template <class S> void trim (S& str, bool fromLeft = true, bool fromRight = true);
+enum class SplitType
+{
+ ALLOW_EMPTY,
+ SKIP_EMPTY
+};
+template <class S, class T> std::vector<S> split(const S& str, const T& delimiter, SplitType st);
+
+template <class S> S trimCpy(S str, bool fromLeft = true, bool fromRight = true);
+template <class S> void trim (S& str, bool fromLeft = true, bool fromRight = true);
template <class S, class Function> void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar);
+
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);
//high-performance conversion between numbers and strings
template <class S, class Num> S numberTo(const Num& number);
-template <class Num, class S > Num stringTo(const S& str);
+template <class Num, class S> Num stringTo(const S& str);
std::pair<char, char> hexify (unsigned char c, bool upperCase = true);
char unhexify(char high, char low);
@@ -61,9 +88,6 @@ template <class S, class T, class Num> S printNumber(const T& format, const Num& //string to string conversion: converts string-like type into char-compatible target string class
template <class T, class S> T copyStringTo(S&& str);
-//case-sensitive comparison
-template <class S, class T> int cmpString(const S& lhs, const T& rhs);
-
@@ -99,7 +123,7 @@ bool isWhiteSpace(wchar_t ch) template <class Char> inline
-bool isDigit(Char ch) //similar to implmenetation of std::::isdigit()!
+bool isDigit(Char ch) //similar to implmenetation of std::isdigit()!
{
static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, "");
return static_cast<Char>('0') <= ch && ch <= static_cast<Char>('9');
@@ -116,40 +140,52 @@ bool isHexDigit(Char c) }
-template <> bool isAlpha(char ch) = delete; //probably not a good idea with UTF-8 anyway...
-
-template <> inline bool isAlpha(wchar_t ch) { return std::iswalpha(ch) != 0; }
+template <class Char> inline
+bool isAsciiAlpha(Char c)
+{
+ static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, "");
+ return (static_cast<Char>('A') <= c && c <= static_cast<Char>('Z')) ||
+ (static_cast<Char>('a') <= c && c <= static_cast<Char>('z'));
+}
-template <class S, class T> inline
-bool startsWith(const S& str, const T& prefix)
+template <class S, class T, class Function> inline
+bool startsWith(const S& str, const T& prefix, Function cmpStringFun)
{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
const size_t pfLen = strLength(prefix);
if (strLength(str) < pfLen)
return false;
- const auto* const cmpFirst = strBegin(str);
- return std::equal(cmpFirst, cmpFirst + pfLen,
- strBegin(prefix));
+ return cmpStringFun(strBegin(str), pfLen,
+ strBegin(prefix), pfLen) == 0;
}
-template <class S, class T> inline
-bool endsWith(const S& str, const T& postfix)
+template <class S, class T, class Function> inline
+bool endsWith(const S& str, const T& postfix, Function cmpStringFun)
{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
const size_t strLen = strLength(str);
const size_t pfLen = strLength(postfix);
if (strLen < pfLen)
return false;
- const auto* const cmpFirst = strBegin(str) + strLen - pfLen;
- return std::equal(cmpFirst, cmpFirst + pfLen,
- strBegin(postfix));
+ return cmpStringFun(strBegin(str) + strLen - pfLen, pfLen,
+ strBegin(postfix), pfLen) == 0;
}
+template <class S, class T, class Function> inline
+bool strEqual(const S& lhs, const T& rhs, Function cmpStringFun)
+{
+ return cmpStringFun(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0;
+}
+
+
+template <class S, class T> inline bool startsWith(const S& str, const T& prefix ) { return startsWith(str, prefix, CmpBinary()); }
+template <class S, class T> inline bool endsWith (const S& str, const T& postfix) { return endsWith (str, postfix, CmpBinary()); }
+template <class S, class T> inline bool strEqual (const S& lhs, const T& rhs ) { return strEqual (lhs, rhs, CmpBinary()); }
+
+
template <class S, class T> inline
bool contains(const S& str, const T& term)
{
@@ -173,6 +209,7 @@ S afterLast(const S& str, const T& term, FailureReturnVal rv) {
static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
const size_t termLen = strLength(term);
+ assert(termLen > 0);
const auto* const strFirst = strBegin(str);
const auto* const strLast = strFirst + strLength(str);
@@ -192,12 +229,15 @@ template <class S, class T> inline S beforeLast(const S& str, const T& term, FailureReturnVal rv)
{
static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ const size_t termLen = strLength(term);
+ assert(termLen > 0);
+
const auto* const strFirst = strBegin(str);
const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
const auto* it = search_last(strFirst, strLast,
- termFirst, termFirst + strLength(term));
+ termFirst, termFirst + termLen);
if (it == strLast)
return rv == IF_MISSING_RETURN_ALL ? str : S();
@@ -210,6 +250,8 @@ S afterFirst(const S& str, const T& term, FailureReturnVal rv) {
static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
const size_t termLen = strLength(term);
+ assert(termLen > 0);
+
const auto* const strFirst = strBegin(str);
const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
@@ -228,12 +270,15 @@ template <class S, class T> inline S beforeFirst(const S& str, const T& term, FailureReturnVal rv)
{
static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ const size_t termLen = strLength(term);
+ assert(termLen > 0);
+
const auto* const strFirst = strBegin(str);
const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
auto it = std::search(strFirst, strLast,
- termFirst, termFirst + strLength(term));
+ termFirst, termFirst + termLen);
if (it == strLast)
return rv == IF_MISSING_RETURN_ALL ? str : S();
@@ -242,34 +287,35 @@ S beforeFirst(const S& str, const T& term, FailureReturnVal rv) template <class S, class T> inline
-std::vector<S> split(const S& str, const T& delimiter)
+std::vector<S> split(const S& str, const T& delimiter, SplitType st)
{
static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
-
const size_t delimLen = strLength(delimiter);
-
+ assert(delimLen > 0);
if (delimLen == 0)
- return { str };
- else
{
- const auto* const delimFirst = strBegin(delimiter);
- const auto* const delimLast = delimFirst + delimLen;
+ if (str.empty() && st == SplitType::SKIP_EMPTY)
+ return {};
+ return { str };
+ }
- const auto* blockStart = strBegin(str);
- const auto* const strLast = blockStart + strLength(str);
+ const auto* const delimFirst = strBegin(delimiter);
+ const auto* const delimLast = delimFirst + delimLen;
- std::vector<S> output;
-
- for (;;)
- {
- const auto* const blockEnd = std::search(blockStart, strLast,
- delimFirst, delimLast);
+ const auto* blockStart = strBegin(str);
+ const auto* const strLast = blockStart + strLength(str);
+ std::vector<S> output;
+ for (;;)
+ {
+ const auto* const blockEnd = std::search(blockStart, strLast,
+ delimFirst, delimLast);
+ if (blockStart != blockEnd || st == SplitType::ALLOW_EMPTY)
output.emplace_back(blockStart, blockEnd - blockStart);
- if (blockEnd == strLast) //clients expect: if delimiter not found, return str
- return output;
- blockStart = blockEnd + delimLen;
- }
+
+ if (blockEnd == strLast)
+ return output;
+ blockStart = blockEnd + delimLen;
}
}
@@ -389,33 +435,47 @@ struct CopyStringToString<T, T> //perf: we don't need a deep copy if string type template <class S>
T copy(S&& str) const { return std::forward<S>(str); }
};
+
+inline int strcmpWithNulls(const char* ptr1, const char* ptr2, size_t num) { return std::memcmp (ptr1, ptr2, num); }
+inline int strcmpWithNulls(const wchar_t* ptr1, const wchar_t* ptr2, size_t num) { return std::wmemcmp(ptr1, ptr2, num); }
}
template <class T, class S> inline
T copyStringTo(S&& str) { return impl::CopyStringToString<std::decay_t<S>, T>().copy(std::forward<S>(str)); }
-template <class S, class T> inline
-int cmpString(const S& lhs, const T& rhs)
+template <class Char> inline
+int CmpBinary::operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const
{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ //support embedded 0, unlike strncmp/wcsncmp!
+ const int rv = impl::strcmpWithNulls(lhs, rhs, std::min(lhsLen, rhsLen));
+ if (rv != 0)
+ return rv;
+ return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
+}
- const size_t lenL = strLength(lhs);
- const size_t lenR = strLength(rhs);
- const auto* strPosL = strBegin(lhs);
- const auto* strPosR = strBegin(rhs);
+template <class Char> inline
+int CmpAsciiNoCase::operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const
+{
+ auto asciiToLower = [](Char c) //ordering: lower-case chars have higher code points than uppper-case
+ {
+ if (static_cast<Char>('A') <= c && c <= static_cast<Char>('Z'))
+ return static_cast<Char>(c - static_cast<Char>('A') + static_cast<Char>('a'));
+ return c;
+ };
- const auto* const strPosLLast = strPosL + std::min(lenL, lenR);
+ const auto* const lhsLast = lhs + std::min(lhsLen, rhsLen);
- while (strPosL != strPosLLast)
+ while (lhs != lhsLast)
{
- const auto charL = static_cast<unsigned int>(*strPosL++); //unsigned char-comparison is the convention!
- const auto charR = static_cast<unsigned int>(*strPosR++);
+ const Char charL = asciiToLower(*lhs++);
+ const Char charR = asciiToLower(*rhs++);
if (charL != charR)
- return static_cast<int>(charL) - static_cast<int>(charR);
+ return static_cast<unsigned int>(charL) - static_cast<unsigned int>(charR); //unsigned char-comparison is the convention!
+ //unsigned underflow is well-defined!
}
- return static_cast<int>(lenL) - static_cast<int>(lenR);
+ return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
}
@@ -424,13 +484,13 @@ namespace impl template <class Num> inline
int saferPrintf(char* buffer, size_t bufferSize, const char* format, const Num& number) //there is no such thing as a "safe" printf ;)
{
- return std::snprintf(buffer, bufferSize, format, number); //C99
+ return std::snprintf(buffer, bufferSize, format, number); //C99: returns number of chars written if successful, < 0 or >= bufferSize on failure
}
template <class Num> inline
int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const Num& number)
{
- return std::swprintf(buffer, bufferSize, format, number); //C99
+ return std::swprintf(buffer, bufferSize, format, number); //C99: returns number of chars written if successful, < 0 on failure (including buffer too small)
}
}
@@ -444,7 +504,7 @@ S printNumber(const T& format, const Num& number) //format a single number using CharType buffer[BUFFER_SIZE]; //zero-initialize?
const int charsWritten = impl::saferPrintf(buffer, BUFFER_SIZE, strBegin(format), number);
- return charsWritten > 0 ? S(buffer, charsWritten) : S();
+ return 0 < charsWritten && charsWritten < BUFFER_SIZE ? S(buffer, charsWritten) : S();
}
@@ -607,12 +667,8 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i number *= 10;
number += c - static_cast<CharType>('0');
}
- else
- {
- //rest of string should contain whitespace only, it's NOT a bug if there is something else!
- //assert(std::all_of(iter, last, &isWhiteSpace<CharType>)); -> this is NO assert situation
- break;
- }
+ else //rest of string should contain whitespace only, it's NOT a bug if there is something else!
+ break; //assert(std::all_of(iter, last, &isWhiteSpace<CharType>)); -> this is NO assert situation
}
return number;
}
diff --git a/zen/sys_error.h b/zen/sys_error.h index a19409ab..f7c128ef 100755 --- a/zen/sys_error.h +++ b/zen/sys_error.h @@ -67,7 +67,7 @@ std::wstring formatSystemErrorRaw(ErrorCode ec) //return empty string on error std::wstring errorMsg;
ZEN_ON_SCOPE_EXIT(errno = currentError);
- errorMsg = utfCvrtTo<std::wstring>(::strerror(ec));
+ errorMsg = utfTo<std::wstring>(::strerror(ec));
trim(errorMsg); //Windows messages seem to end with a blank...
return errorMsg;
diff --git a/zen/thread.h b/zen/thread.h index a59f3807..ae4c347e 100755 --- a/zen/thread.h +++ b/zen/thread.h @@ -28,26 +28,26 @@ public: template <class Function>
InterruptibleThread(Function&& f);
- bool joinable () const { return stdThread.joinable(); }
+ bool joinable () const { return stdThread_.joinable(); }
void interrupt();
- void join () { stdThread.join(); }
- void detach () { stdThread.detach(); }
+ void join () { stdThread_.join(); }
+ void detach () { stdThread_.detach(); }
template <class Rep, class Period>
bool tryJoinFor(const std::chrono::duration<Rep, Period>& relTime)
{
- if (threadCompleted.wait_for(relTime) == std::future_status::ready)
+ if (threadCompleted_.wait_for(relTime) == std::future_status::ready)
{
- stdThread.join(); //runs thread-local destructors => this better be fast!!!
+ stdThread_.join(); //runs thread-local destructors => this better be fast!!!
return true;
}
return false;
}
private:
- std::thread stdThread;
+ std::thread stdThread_;
std::shared_ptr<InterruptionStatus> intStatus_;
- std::future<void> threadCompleted;
+ std::future<void> threadCompleted_;
};
//context of worker thread:
@@ -376,9 +376,9 @@ template <class Function> inline InterruptibleThread::InterruptibleThread(Function&& f) : intStatus_(std::make_shared<InterruptionStatus>())
{
std::promise<void> pFinished;
- threadCompleted = pFinished.get_future();
+ threadCompleted_ = pFinished.get_future();
- stdThread = std::thread([f = std::forward<Function>(f),
+ stdThread_ = std::thread([f = std::forward<Function>(f),
intStatus = this->intStatus_,
pFinished = std::move(pFinished)]() mutable
{
@@ -10,40 +10,25 @@ #include <cstdint>
#include <iterator>
#include "string_tools.h" //copyStringTo
+#include "optional.h"
namespace zen
{
//convert all(!) char- and wchar_t-based "string-like" objects applying a UTF8 conversions (but only if necessary!)
template <class TargetString, class SourceString>
-TargetString utfCvrtTo(const SourceString& str);
+TargetString utfTo(const SourceString& str);
const char BYTE_ORDER_MARK_UTF8[] = "\xEF\xBB\xBF";
-template <class CharString>
-bool isValidUtf8(const CharString& str); //check for UTF-8 encoding errors
-
-//---- explicit conversion: wide <-> utf8 ----
-template <class CharString, class WideString>
-CharString wideToUtf8(const WideString& str); //example: std::string tmp = wideToUtf8<std::string>(L"abc");
-
-template <class WideString, class CharString>
-WideString utf8ToWide(const CharString& str); //std::wstring tmp = utf8ToWide<std::wstring>("abc");
+template <class UtfString>
+bool isValidUtf(const UtfString& str); //check for UTF-8 encoding errors
//access unicode characters in UTF-encoded string (char- or wchar_t-based)
template <class UtfString>
size_t unicodeLength(const UtfString& str); //return number of code points for UTF-encoded string
template <class UtfString>
-size_t findUnicodePos(const UtfString& str, size_t unicodePos); //return position of unicode char in UTF-encoded string
-
-
-
-
-
-
-
-
-
+UtfString getUnicodeSubstring(const UtfString& str, size_t uniPosFirst, size_t uniPosLast);
@@ -58,7 +43,7 @@ namespace implementation {
using CodePoint = uint32_t;
using Char16 = uint16_t;
-using Char8 = unsigned char;
+using Char8 = uint8_t;
const CodePoint LEAD_SURROGATE = 0xd800;
const CodePoint TRAIL_SURROGATE = 0xdc00; //== LEAD_SURROGATE_MAX + 1
@@ -72,7 +57,6 @@ template <class Function> inline void codePointToUtf16(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a Char16
{
//http://en.wikipedia.org/wiki/UTF-16
-
if (cp < LEAD_SURROGATE)
writeOutput(static_cast<Char16>(cp));
else if (cp <= TRAIL_SURROGATE_MAX) //invalid code point
@@ -82,8 +66,8 @@ void codePointToUtf16(CodePoint cp, Function writeOutput) //"writeOutput" is a u else if (cp <= CODE_POINT_MAX)
{
cp -= 0x10000;
- writeOutput(LEAD_SURROGATE + static_cast<Char16>(cp >> 10));
- writeOutput(TRAIL_SURROGATE + static_cast<Char16>(cp & 0x3ff));
+ writeOutput(static_cast<Char16>( LEAD_SURROGATE + (cp >> 10)));
+ writeOutput(static_cast<Char16>(TRAIL_SURROGATE + (cp & 0x3ff)));
}
else //invalid code point
codePointToUtf16(REPLACEMENT_CHAR, writeOutput); //resolves to 1-character utf16
@@ -104,15 +88,19 @@ size_t getUtf16Len(Char16 ch) //ch must be first code unit! returns 0 on error! }
-template <class CharIterator, class Function> inline
-void utf16ToCodePoint(CharIterator first, CharIterator last, Function writeOutput) //"writeOutput" is a unary function taking a CodePoint
+class Utf16Decoder
{
- static_assert(sizeof(typename std::iterator_traits<CharIterator>::value_type) == 2, "");
+public:
+ Utf16Decoder(const Char16* str, size_t len) : it_(str), last_(str + len) {}
- for ( ; first != last; ++first)
+ Opt<CodePoint> getNext()
{
- CodePoint cp = static_cast<Char16>(*first);
- switch (getUtf16Len(static_cast<Char16>(cp)))
+ if (it_ == last_)
+ return NoValue();
+
+ const Char16 ch = *it_++;
+ CodePoint cp = ch;
+ switch (getUtf16Len(ch))
{
case 0: //invalid utf16 character
cp = REPLACEMENT_CHAR;
@@ -120,23 +108,33 @@ void utf16ToCodePoint(CharIterator first, CharIterator last, Function writeOutpu case 1:
break;
case 2:
- if (++first != last) //trail surrogate expected!
- {
- const Char16 ch = static_cast<Char16>(*first);
- if (TRAIL_SURROGATE <= ch && ch <= TRAIL_SURROGATE_MAX) //trail surrogate expected!
- {
- cp = ((cp - LEAD_SURROGATE) << 10) + (ch - TRAIL_SURROGATE) + 0x10000;
- break;
- }
- }
- --first;
- cp = REPLACEMENT_CHAR;
+ decodeTrail(cp);
break;
}
- writeOutput(cp);
+ return cp;
+ }
+
+private:
+ void decodeTrail(CodePoint& cp)
+ {
+ if (it_ != last_) //trail surrogate expected!
+ {
+ const Char16 ch = *it_;
+ if (TRAIL_SURROGATE <= ch && ch <= TRAIL_SURROGATE_MAX) //trail surrogate expected!
+ {
+ cp = ((cp - LEAD_SURROGATE) << 10) + (ch - TRAIL_SURROGATE) + 0x10000;
+ ++it_;
+ return;
+ }
+ }
+ cp = REPLACEMENT_CHAR;
}
-}
+ const Char16* it_;
+ const Char16* const last_;
+};
+
+//----------------------------------------------------------------------------------------------------------------
template <class Function> inline
void codePointToUtf8(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a Char8
@@ -155,14 +153,14 @@ void codePointToUtf8(CodePoint cp, Function writeOutput) //"writeOutput" is a un {
writeOutput(static_cast<Char8>( (cp >> 12 ) | 0xe0));
writeOutput(static_cast<Char8>(((cp >> 6) & 0x3f) | 0x80));
- writeOutput(static_cast<Char8>( (cp & 0x3f ) | 0x80));
+ writeOutput(static_cast<Char8>( (cp & 0x3f) | 0x80));
}
else if (cp <= CODE_POINT_MAX)
{
writeOutput(static_cast<Char8>( (cp >> 18 ) | 0xf0));
writeOutput(static_cast<Char8>(((cp >> 12) & 0x3f) | 0x80));
writeOutput(static_cast<Char8>(((cp >> 6) & 0x3f) | 0x80));
- writeOutput(static_cast<Char8>( (cp & 0x3f ) | 0x80));
+ writeOutput(static_cast<Char8>( (cp & 0x3f) | 0x80));
}
else //invalid code point
codePointToUtf8(REPLACEMENT_CHAR, writeOutput); //resolves to 3-byte utf8
@@ -170,7 +168,7 @@ void codePointToUtf8(CodePoint cp, Function writeOutput) //"writeOutput" is a un inline
-size_t getUtf8Len(unsigned char ch) //ch must be first code unit! returns 0 on error!
+size_t getUtf8Len(Char8 ch) //ch must be first code unit! returns 0 on error!
{
if (ch < 0x80)
return 1;
@@ -184,32 +182,19 @@ size_t getUtf8Len(unsigned char ch) //ch must be first code unit! returns 0 on e }
-template <class CharIterator> inline
-bool decodeTrail(CharIterator& first, CharIterator last, CodePoint& cp) //decode trailing surrogate byte
-{
- if (++first != last) //trail surrogate expected!
- {
- const Char8 ch = static_cast<Char8>(*first);
- if (ch >> 6 == 0x2) //trail surrogate expected!
- {
- cp = (cp << 6) + (ch & 0x3f);
- return true;
- }
- }
- --first;
- cp = REPLACEMENT_CHAR;
- return false;
-}
-
-template <class CharIterator, class Function> inline
-void utf8ToCodePoint(CharIterator first, CharIterator last, Function writeOutput) //"writeOutput" is a unary function taking a CodePoint
+class Utf8Decoder
{
- static_assert(sizeof(typename std::iterator_traits<CharIterator>::value_type) == 1, "");
+public:
+ Utf8Decoder(const Char8* str, size_t len) : it_(str), last_(str + len) {}
- for ( ; first != last; ++first)
+ Opt<CodePoint> getNext()
{
- CodePoint cp = static_cast<Char8>(*first);
- switch (getUtf8Len(static_cast<Char8>(cp)))
+ if (it_ == last_)
+ return NoValue();
+
+ const Char8 ch = *it_++;
+ CodePoint cp = ch;
+ switch (getUtf8Len(ch))
{
case 0: //invalid utf8 character
cp = REPLACEMENT_CHAR;
@@ -218,258 +203,184 @@ void utf8ToCodePoint(CharIterator first, CharIterator last, Function writeOutput break;
case 2:
cp &= 0x1f;
- decodeTrail(first, last, cp);
+ decodeTrail(cp);
break;
case 3:
cp &= 0xf;
- if (decodeTrail(first, last, cp))
- decodeTrail(first, last, cp);
+ if (decodeTrail(cp))
+ decodeTrail(cp);
break;
case 4:
cp &= 0x7;
- if (decodeTrail(first, last, cp))
- if (decodeTrail(first, last, cp))
- decodeTrail(first, last, cp);
+ if (decodeTrail(cp))
+ if (decodeTrail(cp))
+ decodeTrail(cp);
if (cp > CODE_POINT_MAX) cp = REPLACEMENT_CHAR;
break;
}
- writeOutput(cp);
+ return cp;
}
-}
-
-
-template <class CharString> inline
-size_t unicodeLength(const CharString& str, char) //utf8
-{
- using CharType = typename GetCharType<CharString>::Type;
- const CharType* strFirst = strBegin(str);
- const CharType* const strLast = strFirst + strLength(str);
-
- size_t len = 0;
- while (strFirst < strLast) //[!]
+private:
+ bool decodeTrail(CodePoint& cp)
{
- ++len;
- size_t utf8len = getUtf8Len(*strFirst);
- if (utf8len == 0) ++utf8len; //invalid utf8 character
- strFirst += utf8len;
+ if (it_ != last_) //trail surrogate expected!
+ {
+ const Char8 ch = *it_;
+ if (ch >> 6 == 0x2) //trail surrogate expected!
+ {
+ cp = (cp << 6) + (ch & 0x3f);
+ ++it_;
+ return true;
+ }
+ }
+ cp = REPLACEMENT_CHAR;
+ return false;
}
- return len;
-}
+ const Char8* it_;
+ const Char8* const last_;
+};
-template <class WideString> inline
-size_t unicodeLengthWide(const WideString& str, Int2Type<2>) //windows: utf16-wchar_t
-{
- using CharType = typename GetCharType<WideString>::Type;
+//----------------------------------------------------------------------------------------------------------------
- const CharType* strFirst = strBegin(str);
- const CharType* const strLast = strFirst + strLength(str);
+template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<1>) { codePointToUtf8 (cp, writeOutput); } //UTF8-char
+template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<2>) { codePointToUtf16(cp, writeOutput); } //Windows: UTF16-wchar_t
+template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<4>) { writeOutput(cp); } //other OS: UTF32-wchar_t
- size_t len = 0;
- while (strFirst < strLast) //[!]
- {
- ++len;
- size_t utf16len = getUtf16Len(*strFirst);
- if (utf16len == 0) ++utf16len; //invalid utf16 character
- strFirst += utf16len;
- }
- return len;
-}
-
-
-template <class WideString> inline
-size_t unicodeLengthWide(const WideString& str, Int2Type<4>) //other OS: utf32-wchar_t
+template <class CharType, class Function> inline
+void codePointToUtf(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a CharType
{
- return strLength(str);
+ return codePointToUtf(cp, writeOutput, Int2Type<sizeof(CharType)>());
}
+//----------------------------------------------------------------------------------------------------------------
-template <class WideString> inline
-size_t unicodeLength(const WideString& str, wchar_t)
-{
- return unicodeLengthWide(str, Int2Type<sizeof(wchar_t)>());
-}
-}
+template <class CharType, int charSize>
+class UtfDecoderImpl;
-template <class UtfString> inline
-size_t unicodeLength(const UtfString& str) //return number of code points
+template <class CharType>
+class UtfDecoderImpl<CharType, 1> //UTF8-char
{
- return implementation::unicodeLength(str, typename GetCharType<UtfString>::Type());
-}
+public:
+ UtfDecoderImpl(const CharType* str, size_t len) : decoder_(reinterpret_cast<const Char8*>(str), len) {}
+ Opt<CodePoint> getNext() { return decoder_.getNext(); }
+private:
+ Utf8Decoder decoder_;
+};
-namespace implementation
-{
-template <class CharString> inline
-size_t findUnicodePos(const CharString& str, size_t unicodePos, char) //utf8-char
+template <class CharType>
+class UtfDecoderImpl<CharType, 2> //Windows: UTF16-wchar_t
{
- using CharType = typename GetCharType<CharString>::Type;
-
- const CharType* strFirst = strBegin(str);
- const size_t strLen = strLength(str);
-
- size_t utfPos = 0;
- while (unicodePos-- > 0)
- {
- if (utfPos >= strLen)
- return strLen;
+public:
+ UtfDecoderImpl(const CharType* str, size_t len) : decoder_(reinterpret_cast<const Char16*>(str), len) {}
+ Opt<CodePoint> getNext() { return decoder_.getNext(); }
+private:
+ Utf16Decoder decoder_;
+};
- size_t utf8len = getUtf8Len(strFirst[utfPos]);
- if (utf8len == 0) ++utf8len; //invalid utf8 character
- utfPos += utf8len;
- }
- if (utfPos >= strLen)
- return strLen;
- return utfPos;
-}
-
-template <class WideString> inline
-size_t findUnicodePosWide(const WideString& str, size_t unicodePos, Int2Type<2>) //windows: utf16-wchar_t
+template <class CharType>
+class UtfDecoderImpl<CharType, 4> //other OS: UTF32-wchar_t
{
- using CharType = typename GetCharType<WideString>::Type;
-
- const CharType* strFirst = strBegin(str);
- const size_t strLen = strLength(str);
-
- size_t utfPos = 0;
- while (unicodePos-- > 0)
+public:
+ UtfDecoderImpl(const CharType* str, size_t len) : it_(reinterpret_cast<const CodePoint*>(str)), last_(it_ + len) {}
+ Opt<CodePoint> getNext()
{
- if (utfPos >= strLen)
- return strLen;
-
- size_t utf16len = getUtf16Len(strFirst[utfPos]);
- if (utf16len == 0) ++utf16len; //invalid utf16 character
- utfPos += utf16len;
+ if (it_ == last_)
+ return NoValue();
+ return *it_++;
}
- if (utfPos >= strLen)
- return strLen;
- return utfPos;
-}
+private:
+ const CodePoint* it_;
+ const CodePoint* last_;
+};
-template <class WideString> inline
-size_t findUnicodePosWide(const WideString& str, size_t unicodePos, Int2Type<4>) //other OS: utf32-wchar_t
-{
- return std::min(strLength(str), unicodePos);
-}
-
-
-template <class UtfString> inline
-size_t findUnicodePos(const UtfString& str, size_t unicodePos, wchar_t)
-{
- return findUnicodePosWide(str, unicodePos, Int2Type<sizeof(wchar_t)>());
-}
-}
-
-
-template <class UtfString> inline
-size_t findUnicodePos(const UtfString& str, size_t unicodePos) //return position of unicode char in UTF-encoded string
-{
- return implementation::findUnicodePos(str, unicodePos, typename GetCharType<UtfString>::Type());
+template <class CharType>
+using UtfDecoder = UtfDecoderImpl<CharType, sizeof(CharType)>;
}
//-------------------------------------------------------------------------------------------
-namespace implementation
-{
-template <class WideString, class CharString> inline
-WideString utf8ToWide(const CharString& str, Int2Type<2>) //windows: convert utf8 to utf16-wchar_t
+template <class UtfString> inline
+bool isValidUtf(const UtfString& str)
{
- WideString output;
- utf8ToCodePoint(strBegin(str), strBegin(str) + strLength(str),
- [&](CodePoint cp) { codePointToUtf16(cp, [&](Char16 c) { output += static_cast<wchar_t>(c); }); });
- return output;
-}
+ using namespace implementation;
+ UtfDecoder<typename GetCharType<UtfString>::Type> decoder(strBegin(str), strLength(str));
+ while (Opt<CodePoint> cp = decoder.getNext())
+ if (*cp == REPLACEMENT_CHAR)
+ return false;
-template <class WideString, class CharString> inline
-WideString utf8ToWide(const CharString& str, Int2Type<4>) //other OS: convert utf8 to utf32-wchar_t
-{
- WideString output;
- utf8ToCodePoint(strBegin(str), strBegin(str) + strLength(str),
- [&](CodePoint cp) { output += static_cast<wchar_t>(cp); });
- return output;
+ return true;
}
-template <class CharString, class WideString> inline
-CharString wideToUtf8(const WideString& str, Int2Type<2>) //windows: convert utf16-wchar_t to utf8
+template <class UtfString> inline
+size_t unicodeLength(const UtfString& str) //return number of code points (+ correctly handle broken UTF encoding)
{
- CharString output;
- utf16ToCodePoint(strBegin(str), strBegin(str) + strLength(str),
- [&](CodePoint cp) { codePointToUtf8(cp, [&](char c) { output += c; }); });
- return output;
+ size_t uniLen = 0;
+ implementation::UtfDecoder<typename GetCharType<UtfString>::Type> decoder(strBegin(str), strLength(str));
+ while (decoder.getNext())
+ ++uniLen;
+ return uniLen;
}
-template <class CharString, class WideString> inline
-CharString wideToUtf8(const WideString& str, Int2Type<4>) //other OS: convert utf32-wchar_t to utf8
+template <class UtfString> inline
+UtfString getUnicodeSubstring(const UtfString& str, size_t uniPosFirst, size_t uniPosLast) //return position of unicode char in UTF-encoded string
{
- CharString output;
- std::for_each(strBegin(str), strBegin(str) + strLength(str),
- [&](CodePoint cp) { codePointToUtf8(cp, [&](char c) { output += c; }); });
+ assert(uniPosFirst <= uniPosLast && uniPosLast <= unicodeLength(str));
+ using namespace implementation;
+ using CharType = typename GetCharType<UtfString>::Type;
+ UtfString output;
+ if (uniPosFirst >= uniPosLast) //optimize for empty range
+ return output;
+
+ UtfDecoder<CharType> decoder(strBegin(str), strLength(str));
+ for (size_t uniPos = 0; Opt<CodePoint> cp = decoder.getNext(); ++uniPos) //[!] declaration in condition part of the for-loop
+ if (uniPosFirst <= uniPos)
+ {
+ if (uniPos >= uniPosLast)
+ break;
+ codePointToUtf<CharType>(*cp, [&](CharType c) { output += c; });
+ }
return output;
}
-}
+//-------------------------------------------------------------------------------------------
-template <class CharString> inline
-bool isValidUtf8(const CharString& str)
+namespace implementation
{
- using namespace implementation;
- bool valid = true;
- utf8ToCodePoint(strBegin(str), strBegin(str) + strLength(str),
- [&](CodePoint cp)
- {
- if (cp == REPLACEMENT_CHAR)
- valid = false; //perf: should we use an (expensive) exception for iteration break?
- });
- return valid;
-}
-
-
-template <class WideString, class CharString> inline
-WideString utf8ToWide(const CharString& str)
+template <class TargetString, class SourceString> inline
+TargetString utfTo(const SourceString& str, FalseType)
{
- static_assert(IsSameType<typename GetCharType<CharString>::Type, char >::value, "");
- static_assert(IsSameType<typename GetCharType<WideString>::Type, wchar_t>::value, "");
+ using CharSrc = typename GetCharType<SourceString>::Type;
+ using CharTrg = typename GetCharType<TargetString>::Type;
+ static_assert(sizeof(CharSrc) != sizeof(CharTrg), "no UTF-conversion needed");
- return implementation::utf8ToWide<WideString>(str, Int2Type<sizeof(wchar_t)>());
-}
+ TargetString output;
+ UtfDecoder<CharSrc> decoder(strBegin(str), strLength(str));
+ while (Opt<CodePoint> cp = decoder.getNext())
+ codePointToUtf<CharTrg>(*cp, [&](CharTrg c) { output += c; });
-template <class CharString, class WideString> inline
-CharString wideToUtf8(const WideString& str)
-{
- static_assert(IsSameType<typename GetCharType<CharString>::Type, char >::value, "");
- static_assert(IsSameType<typename GetCharType<WideString>::Type, wchar_t>::value, "");
-
- return implementation::wideToUtf8<CharString>(str, Int2Type<sizeof(wchar_t)>());
+ return output;
}
-//-------------------------------------------------------------------------------------------
template <class TargetString, class SourceString> inline
-TargetString utfCvrtTo(const SourceString& str, char, wchar_t) { return utf8ToWide<TargetString>(str); }
-
-template <class TargetString, class SourceString> inline
-TargetString utfCvrtTo(const SourceString& str, wchar_t, char) { return wideToUtf8<TargetString>(str); }
-
-template <class TargetString, class SourceString> inline
-TargetString utfCvrtTo(const SourceString& str, char, char) { return copyStringTo<TargetString>(str); }
+TargetString utfTo(const SourceString& str, TrueType) { return copyStringTo<TargetString>(str); }
+}
-template <class TargetString, class SourceString> inline
-TargetString utfCvrtTo(const SourceString& str, wchar_t, wchar_t) { return copyStringTo<TargetString>(str); }
template <class TargetString, class SourceString> inline
-TargetString utfCvrtTo(const SourceString& str)
+TargetString utfTo(const SourceString& str)
{
- return utfCvrtTo<TargetString>(str,
- typename GetCharType<SourceString>::Type(),
- typename GetCharType<TargetString>::Type());
+ return implementation::utfTo<TargetString>(str, StaticBool<sizeof(typename GetCharType<SourceString>::Type) == sizeof(typename GetCharType<TargetString>::Type)>());
}
}
diff --git a/zen/zstring.cpp b/zen/zstring.cpp index 5f5b1ec8..a936efb5 100755 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -6,11 +6,14 @@ #include "zstring.h"
#include <stdexcept>
+#include "utf.h"
using namespace zen;
/*
+MSDN "Handling Sorting in Your Applications": https://msdn.microsoft.com/en-us/library/windows/desktop/dd318144
+
Perf test: compare strings 10 mio times; 64 bit build
-----------------------------------------------------
string a = "Fjk84$%kgfj$%T\\\\Gffg\\gsdgf\\fgsx----------d-"
@@ -32,3 +35,117 @@ time per call | function */
+
+
+namespace
+{
+int compareNoCaseUtf8(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen)
+{
+ //- strncasecmp implements ASCII CI-comparsion only! => signature is broken for UTF8-input; toupper() similarly doesn't support Unicode
+ //- wcsncasecmp: https://opensource.apple.com/source/Libc/Libc-763.12/string/wcsncasecmp-fbsd.c
+ // => re-implement comparison based on towlower() to avoid memory allocations
+ using namespace zen::implementation;
+
+ UtfDecoder<char> decL(lhs, lhsLen);
+ UtfDecoder<char> decR(rhs, rhsLen);
+ for (;;)
+ {
+ const Opt<CodePoint> cpL = decL.getNext();
+ const Opt<CodePoint> cpR = decR.getNext();
+ if (!cpL || !cpR)
+ return static_cast<int>(!cpR) - static_cast<int>(!cpL);
+
+ static_assert(sizeof(wchar_t) == sizeof(CodePoint), "");
+ const wchar_t charL = ::towlower(static_cast<wchar_t>(*cpL)); //ordering: towlower() converts to higher code points than towupper()
+ const wchar_t charR = ::towlower(static_cast<wchar_t>(*cpR)); //uses LC_CTYPE category of current locale
+ if (charL != charR)
+ return static_cast<unsigned int>(charL) - static_cast<unsigned int>(charR); //unsigned char-comparison is the convention!
+ //unsigned underflow is well-defined!
+ }
+}
+}
+
+
+int cmpStringNaturalLinux(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen)
+{
+ const char* const lhsEnd = lhs + lhsLen;
+ const char* const rhsEnd = rhs + rhsLen;
+ /*
+ - compare strings after conceptually creating blocks of whitespace/numbers/text
+ - implement strict weak ordering!
+ - don't follow broken "strnatcasecmp": https://github.com/php/php-src/blob/master/ext/standard/strnatcmp.c
+ 1. incorrect non-ASCII CI-comparison 2. incorrect bounds checks
+ 3. incorrect trimming of *all* whitespace 4. arbitrary handling of leading 0 only at string begin
+ 5. incorrect handling of whitespace following a number 6. code is a mess
+ */
+ for (;;)
+ {
+ if (lhs == lhsEnd || rhs == rhsEnd)
+ return static_cast<int>(lhs != lhsEnd) - static_cast<int>(rhs != rhsEnd); //"nothing" before "something"
+ //note: "something" never would have been condensed to "nothing" further below => can finish evaluation here
+
+ const bool wsL = isWhiteSpace(*lhs);
+ const bool wsR = isWhiteSpace(*rhs);
+ if (wsL != wsR)
+ return static_cast<int>(!wsL) - static_cast<int>(!wsR); //whitespace before non-ws!
+ if (wsL)
+ {
+ ++lhs, ++rhs;
+ while (lhs != lhsEnd && isWhiteSpace(*lhs)) ++lhs;
+ while (rhs != rhsEnd && isWhiteSpace(*rhs)) ++rhs;
+ continue;
+ }
+
+ const bool digitL = isDigit(*lhs);
+ const bool digitR = isDigit(*rhs);
+ if (digitL != digitR)
+ return static_cast<int>(!digitL) - static_cast<int>(!digitR); //number before chars!
+ if (digitL)
+ {
+ while (lhs != lhsEnd && *lhs == '0') ++lhs;
+ while (rhs != rhsEnd && *rhs == '0') ++rhs;
+
+ int rv = 0;
+ for (;; ++lhs, ++rhs)
+ {
+ const bool endL = lhs == lhsEnd || !isDigit(*lhs);
+ const bool endR = rhs == rhsEnd || !isDigit(*rhs);
+ if (endL != endR)
+ return static_cast<int>(!endL) - static_cast<int>(!endR); //more digits means bigger number
+ if (endL)
+ break; //same number of digits
+
+ if (rv == 0 && *lhs != *rhs)
+ rv = *lhs - *rhs; //found first digit difference comparing from left
+ }
+ if (rv != 0)
+ return rv;
+ continue;
+ }
+
+ //compare full junks of text: consider unicode encoding!
+ const char* textBeginL = lhs++;
+ const char* textBeginR = rhs++; //current char is neither white space nor digit at this point!
+ while (lhs != lhsEnd && !isWhiteSpace(*lhs) && !isDigit(*lhs)) ++lhs;
+ while (rhs != rhsEnd && !isWhiteSpace(*rhs) && !isDigit(*rhs)) ++rhs;
+
+ const int rv = compareNoCaseUtf8(textBeginL, lhs - textBeginL, textBeginR, rhs - textBeginR);
+ if (rv != 0)
+ return rv;
+ }
+}
+
+
+namespace
+{
+}
+
+
+int CmpNaturalSort::operator()(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen) const
+{
+ //auto strL = utfTo<std::string>(Zstring(lhs, lhsLen));
+ //auto strR = utfTo<std::string>(Zstring(rhs, rhsLen));
+ //return cmpStringNaturalLinux(strL.c_str(), strL.size(), strR.c_str(), strR.size());
+ return cmpStringNaturalLinux(lhs, lhsLen, rhs, rhsLen);
+
+}
\ No newline at end of file diff --git a/zen/zstring.h b/zen/zstring.h index 12bda29f..fdb71da0 100755 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -19,35 +19,39 @@ using Zstring = zen::Zbase<Zchar>;
-int cmpStringNoCase(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen);
- int cmpStringNoCase(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen);
-
-template <class S>
-S makeUpperCopy(S str);
-
-
//Compare filepaths: Windows/OS X does NOT distinguish between upper/lower-case, while Linux DOES
-int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen);
- int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen);
+struct CmpFilePath
+{
+ int operator()(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen) const;
+};
+struct CmpNaturalSort
+{
+ int operator()(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen) const;
+};
-template <class S, class T> inline
-bool equalFilePath(const S& lhs, const T& rhs) { using namespace zen; return cmpFilePath(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0; }
struct LessFilePath
{
- template <class S, class T>
- bool operator()(const S& lhs, const T& rhs) const { using namespace zen; return cmpFilePath(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; }
+ template <class S> //don't support heterogenous input! => use as container predicate only!
+ bool operator()(const S& lhs, const S& rhs) const { using namespace zen; return CmpFilePath()(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; }
};
-
-struct LessNoCase
+struct LessNaturalSort
{
- template <class S, class T>
- bool operator()(const S& lhs, const T& rhs) const { using namespace zen; return cmpStringNoCase(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; }
+ template <class S> //don't support heterogenous input! => use as container predicate only!
+ bool operator()(const S& lhs, const S& rhs) const { using namespace zen; return CmpNaturalSort()(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; }
};
+template <class S>
+S makeUpperCopy(S str);
+
+
+template <class S, class T> inline
+bool equalFilePath(const S& lhs, const T& rhs) { using namespace zen; return strEqual(lhs, rhs, CmpFilePath()); }
+
+
inline
Zstring appendSeparator(Zstring path) //support rvalue references!
{
@@ -63,35 +67,6 @@ Zstring getFileExtension(const Zstring& filePath) }
-template <class S, class T> inline
-bool ciEqual(const S& lhs, const T& rhs) { using namespace zen; return cmpStringNoCase(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0; }
-
-
-template <class S, class T> inline
-bool ciStartsWith(const S& str, const T& prefix)
-{
- using namespace zen;
- const size_t pfLen = strLength(prefix);
- if (strLength(str) < pfLen)
- return false;
-
- return cmpStringNoCase(strBegin(str), pfLen, strBegin(prefix), pfLen) == 0;
-}
-
-
-template <class S, class T> inline
-bool ciEndsWith(const S& str, const T& postfix)
-{
- using namespace zen;
- const size_t strLen = strLength(str);
- const size_t pfLen = strLength(postfix);
- if (strLen < pfLen)
- return false;
-
- return cmpStringNoCase(strBegin(str) + strLen - pfLen, pfLen, strBegin(postfix), pfLen) == 0;
-}
-
-
template <class S, class T, class U>
S ciReplaceCpy(const S& str, const T& oldTerm, const U& newTerm);
@@ -110,37 +85,11 @@ inline void makeUpperInPlace(char* str, size_t strLen)
{
std::for_each(str, str + strLen, [](char& c) { c = std::toupper(static_cast<unsigned char>(c)); }); //locale-dependent!
- //result of toupper() is an unsigned char mapped to int range, so the char representation is in the last 8 bits and we need not care about signedness!
+ //result of toupper() is an unsigned char mapped to int range: the char representation is in the last 8 bits and we need not care about signedness!
//this should work for UTF-8, too: all chars >= 128 are mapped upon themselves!
}
-inline
-int cmpStringNoCase(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen)
-{
- assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls!
- assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); //
-
- const int rv = ::wcsncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent!
- if (rv != 0)
- return rv;
- return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
-}
-
-
-inline
-int cmpStringNoCase(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen)
-{
- assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls!
- assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); //
-
- const int rv = ::strncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent!
- if (rv != 0)
- return rv;
- return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
-}
-
-
template <class S> inline
S makeUpperCopy(S str)
{
@@ -153,20 +102,7 @@ S makeUpperCopy(S str) inline
-int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen)
-{
- assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls!
- assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); //
-
- const int rv = std::wcsncmp(lhs, rhs, std::min(lhsLen, rhsLen));
- if (rv != 0)
- return rv;
- return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
-}
-
-
-inline
-int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen)
+int CmpFilePath::operator()(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen) const
{
assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls!
assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); //
@@ -214,6 +150,7 @@ S ciReplaceCpy(const S& str, const T& oldTerm, const U& newTerm) }
}
+ int cmpStringNaturalLinux(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen);
//---------------------------------------------------------------------------
//ZEN macro consistency checks:
diff --git a/zenXml/zenxml/bind.h b/zenXml/zenxml/bind.h new file mode 100755 index 00000000..6de10a44 --- /dev/null +++ b/zenXml/zenxml/bind.h @@ -0,0 +1,389 @@ +// ***************************************************************************** +// * 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 freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef BIND_H_9081740816593478258435 +#define BIND_H_9081740816593478258435 + +#include <set> +#include "cvrt_struc.h" +#include "parser.h" +#include "io.h" + +namespace zen +{ +/** +\file +\brief Map user data types to XML +*/ + +///Load XML document from a file +/** +Convenience function that does nothing more than calling loadStream() and parse(). + +\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... +\param filename Input file name +\returns The loaded XML document +\throw XmlFileError +\throw XmlParsingError +*/ +template <class String> inline +XmlDoc load(const String& filename) //throw XmlFileError, XmlParsingError +{ + std::string stream = loadStream(filename); //throw XmlFileError + return parse(stream); //throw XmlParsingError +} + + +///Save XML document to a file +/** +Convenience function that does nothing more than calling serialize() and saveStream(). + +\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... +\param doc The XML document to save +\param filename Output file name +\param lineBreak Line break, default: carriage return + new line +\param indent Indentation, default: four space characters +\throw XmlFileError +*/ +template <class String> inline +void save(const XmlDoc& doc, + const String& filename, + const std::string& lineBreak = "\r\n", + const std::string& indent = " ") //throw XmlFileError +{ + std::string stream = serialize(doc, lineBreak, indent); //throw () + saveStream(stream, filename); //throw XmlFileError +} + + +///Proxy class to conveniently convert user data into XML structure +class XmlOut +{ +public: + ///Construct an output proxy for an XML document + /** + \code + zen::XmlDoc doc; + + zen::XmlOut out(doc); + out["elem1"]( 1); // + out["elem2"]( 2); //write data into XML elements + out["elem3"](-3); // + + save(doc, "out.xml"); //throw XmlFileError + \endcode + Output: + \verbatim + <?xml version="1.0" encoding="UTF-8"?> + <Root> + <elem1>1</elem1> + <elem2>2</elem2> + <elem3>-3</elem3> + </Root> + \endverbatim + */ + XmlOut(XmlDoc& doc) : ref_(&doc.root()) {} + ///Construct an output proxy for a single XML element + /** + \sa XmlOut(XmlDoc& doc) + */ + XmlOut(XmlElement& element) : ref_(&element) {} + + ///Retrieve a handle to an XML child element for writing + /** + The child element will be created if it is not yet existing. + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \param name The name of the child element + */ + template <class String> + XmlOut operator[](const String& name) const + { + const std::string utf8name = utfTo<std::string>(name); + XmlElement* child = ref_->getChild(utf8name); + return child ? *child : ref_->addChild(utf8name); + } + + ///Write user data to the underlying XML element + /** + This conversion requires a specialization of zen::writeText() or zen::writeStruc() for type T. + \tparam T User type that is converted into an XML element value. + */ + template <class T> + void operator()(const T& value) { writeStruc(value, *ref_); } + + ///Write user data to an XML attribute + /** + This conversion requires a specialization of zen::writeText() for type T. + \code + zen::XmlDoc doc; + + zen::XmlOut out(doc); + out["elem"].attribute("attr1", 1); // + out["elem"].attribute("attr2", 2); //write data into XML attributes + out["elem"].attribute("attr3", -3); // + + save(doc, "out.xml"); //throw XmlFileError + \endcode + Output: + \verbatim + <?xml version="1.0" encoding="UTF-8"?> + <Root> + <elem attr1="1" attr2="2" attr3="-3"/> + </Root> + \endverbatim + + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \tparam T String-convertible user data type: e.g. any string-like type, all built-in arithmetic numbers + \sa XmlElement::setAttribute() + */ + template <class String, class T> + void attribute(const String& name, const T& value) { ref_->setAttribute(name, value); } + + ///Return a reference to the underlying Xml element + XmlElement& ref() { return *ref_; } + +private: + XmlElement* ref_; //always bound! +}; + + +///Proxy class to conveniently convert XML structure to user data +class XmlIn +{ + class ErrorLog; + +public: + ///Construct an input proxy for an XML document + /** + \code + zen::XmlDoc doc; + ... //load document + zen::XmlIn in(doc); + in["elem1"](value1); // + in["elem2"](value2); //read data from XML elements into variables "value1", "value2", "value3" + in["elem3"](value3); // + \endcode + */ + XmlIn(const XmlDoc& doc) : log(std::make_shared<ErrorLog>()) { refList.push_back(&doc.root()); } + ///Construct an input proxy for a single XML element, may be nullptr + /** + \sa XmlIn(const XmlDoc& doc) + */ + XmlIn(const XmlElement* element) : log(std::make_shared<ErrorLog>()) { refList.push_back(element); } + ///Construct an input proxy for a single XML element + /** + \sa XmlIn(const XmlDoc& doc) + */ + XmlIn(const XmlElement& element) : log(std::make_shared<ErrorLog>()) { refList.push_back(&element); } + + ///Retrieve a handle to an XML child element for reading + /** + It is \b not an error if the child element does not exist, but only later if a conversion to user data is attempted. + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \param name The name of the child element + */ + template <class String> + XmlIn operator[](const String& name) const + { + std::vector<const XmlElement*> childList; + + if (refIndex < refList.size()) + { + auto iterPair = refList[refIndex]->getChildren(name); + std::for_each(iterPair.first, iterPair.second, + [&](const XmlElement& child) { childList.push_back(&child); }); + } + + return XmlIn(childList, childList.empty() ? getChildNameFormatted(name) : std::string(), log); + } + + ///Refer to next sibling element with the same name + /** + <b>Example:</b> Loop over all XML child elements named "Item" + \verbatim + <?xml version="1.0" encoding="UTF-8"?> + <Root> + <Item>1</Item> + <Item>3</Item> + <Item>5</Item> + </Root> + \endverbatim + + \code + zen::XmlIn in(doc); + ... + for (zen::XmlIn child = in["Item"]; child; child.next()) + { + ... + } + \endcode + */ + void next() { ++refIndex; } + + ///Read user data from the underlying XML element + /** + This conversion requires a specialization of zen::readText() or zen::readStruc() for type T. + \tparam T User type that receives the data + \return "true" if data was read successfully + */ + template <class T> + bool operator()(T& value) const + { + if (refIndex < refList.size()) + { + bool success = readStruc(*refList[refIndex], value); + if (!success) + log->notifyConversionError(getNameFormatted()); + return success; + } + else + { + log->notifyMissingElement(getNameFormatted()); + return false; + } + } + + ///Read user data from an XML attribute + /** + This conversion requires a specialization of zen::readText() for type T. + + \code + zen::XmlDoc doc; + ... //load document + zen::XmlIn in(doc); + in["elem"].attribute("attr1", value1); // + in["elem"].attribute("attr2", value2); //read data from XML attributes into variables "value1", "value2", "value3" + in["elem"].attribute("attr3", value3); // + \endcode + + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \tparam T String-convertible user data type: e.g. any string-like type, all built-in arithmetic numbers + \returns "true" if the attribute was found and the conversion to the output value was successful. + \sa XmlElement::getAttribute() + */ + template <class String, class T> + bool attribute(const String& name, T& value) const + { + if (refIndex < refList.size()) + { + bool success = refList[refIndex]->getAttribute(name, value); + if (!success) + log->notifyMissingAttribute(getNameFormatted(), utfTo<std::string>(name)); + return success; + } + else + { + log->notifyMissingElement(getNameFormatted()); + return false; + } + } + + ///Return a pointer to the underlying Xml element, may be nullptr + const XmlElement* get() const { return refIndex < refList.size() ? refList[refIndex] : nullptr; } + + ///Test whether the underlying XML element exists + /** + \code + XmlIn in(doc); + XmlIn child = in["elem1"]; + if (child) + ... + \endcode + Use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20) + */ + explicit operator bool() const { return get() != nullptr; } + + ///Notifies errors while mapping the XML to user data + /** + Error logging is shared by each hiearchy of XmlIn proxy instances that are created from each other. Consequently it doesn't matter which instance you query for errors: + \code + XmlIn in(doc); + XmlIn inItem = in["item1"]; + + int value = 0; + inItem(value); //let's assume this conversion failed + + assert(in.errorsOccured() == inItem.errorsOccured()); + assert(in.getErrorsAs<std::string>() == inItem.getErrorsAs<std::string>()); + \endcode + + Note that error logging is \b NOT global, but owned by all instances of a hierarchy of XmlIn proxies. + Therefore it's safe to use unrelated XmlIn proxies in multiple threads. + \n\n + However be aware that the chain of connected proxy instances will be broken once you call XmlIn::get() to retrieve the underlying pointer. + Errors that occur when working with this pointer are not logged by the original set of related instances. + */ + bool errorsOccured() const { return !log->elementList().empty(); } + + ///Get a list of XML element and attribute names which failed to convert to user data. + /** + \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... + \returns A list of XML element and attribute names, empty list if no errors occured. + */ + template <class String> + std::vector<String> getErrorsAs() const + { + std::vector<String> output; + const auto& elements = log->elementList(); + std::transform(elements.begin(), elements.end(), std::back_inserter(output), [](const std::string& str) { return utfTo<String>(str); }); + return output; + } + +private: + XmlIn(const std::vector<const XmlElement*>& siblingList, const std::string& elementNameFmt, const std::shared_ptr<ErrorLog>& sharedlog) : + refList(siblingList), formattedName(elementNameFmt), log(sharedlog) + { assert((!siblingList.empty() && elementNameFmt.empty()) || (siblingList.empty() && !elementNameFmt.empty())); } + + static std::string getNameFormatted(const XmlElement& elem) //"<Root> <Level1> <Level2>" + { + return (elem.parent() ? getNameFormatted(*elem.parent()) + " " : std::string()) + "<" + elem.getNameAs<std::string>() + ">"; + } + + std::string getNameFormatted() const + { + if (refIndex < refList.size()) + { + assert(formattedName.empty()); + return getNameFormatted(*refList[refIndex]); + } + else + return formattedName; + } + + std::string getChildNameFormatted(const std::string& childName) const + { + std::string parentName = getNameFormatted(); + return (parentName.empty() ? std::string() : (parentName + " ")) + "<" + childName + ">"; + } + + class ErrorLog + { + public: + void notifyConversionError (const std::string& displayName) { insert(displayName); } + void notifyMissingElement (const std::string& displayName) { insert(displayName); } + void notifyMissingAttribute(const std::string& displayName, const std::string& attribName) { insert(displayName + " @" + attribName); } + + const std::vector<std::string>& elementList() const { return failedElements; } + + private: + void insert(const std::string& newVal) + { + if (usedElements.insert(newVal).second) + failedElements.push_back(newVal); + } + + std::vector<std::string> failedElements; //unique list of failed elements + std::set<std::string> usedElements; + }; + + std::vector<const XmlElement*> refList; //all sibling elements with same name (all pointers bound!) + size_t refIndex = 0; //this sibling's index in refList + std::string formattedName; //contains full and formatted element name if (and only if) refList is empty + std::shared_ptr<ErrorLog> log; //always bound +}; +} + +#endif //BIND_H_9081740816593478258435 diff --git a/zenXml/zenxml/cvrt_struc.h b/zenXml/zenxml/cvrt_struc.h new file mode 100755 index 00000000..7797c0fe --- /dev/null +++ b/zenXml/zenxml/cvrt_struc.h @@ -0,0 +1,210 @@ +// ***************************************************************************** +// * 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 freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef CVRT_STRUC_H_018727409908342709743 +#define CVRT_STRUC_H_018727409908342709743 + +#include "dom.h" + +namespace zen +{ +/** +\file +\brief Handle conversion of arbitrary types to and from XML elements. +See comments in cvrt_text.h +*/ + +///Convert XML element to structured user data +/** + \param input The input XML element. + \param value Conversion target value. + \return "true" if value was read successfully. +*/ +template <class T> bool readStruc(const XmlElement& input, T& value); +///Convert structured user data into an XML element +/** + \param value The value to be converted. + \param output The output XML element. +*/ +template <class T> void writeStruc(const T& value, XmlElement& output); + + + + + + + + + + + + + + + + + +//------------------------------ implementation ------------------------------------- +namespace impl_2384343 +{ +ZEN_INIT_DETECT_MEMBER_TYPE(value_type); +ZEN_INIT_DETECT_MEMBER_TYPE(iterator); +ZEN_INIT_DETECT_MEMBER_TYPE(const_iterator); + +ZEN_INIT_DETECT_MEMBER(begin) // +ZEN_INIT_DETECT_MEMBER(end) //we don't know the exact declaration of the member attribute: may be in a base class! +ZEN_INIT_DETECT_MEMBER(insert) // +} + +template <typename T> +struct IsStlContainer : + StaticBool< + impl_2384343::HasMemberType_value_type <T>::value&& + impl_2384343::HasMemberType_iterator <T>::value&& + impl_2384343::HasMemberType_const_iterator<T>::value&& + impl_2384343::HasMember_begin <T>::value&& + impl_2384343::HasMember_end <T>::value&& + impl_2384343::HasMember_insert <T>::value> {}; + + +namespace impl_2384343 +{ +ZEN_INIT_DETECT_MEMBER_TYPE(first_type); +ZEN_INIT_DETECT_MEMBER_TYPE(second_type); + +ZEN_INIT_DETECT_MEMBER(first) //we don't know the exact declaration of the member attribute: may be in a base class! +ZEN_INIT_DETECT_MEMBER(second) // +} + +template <typename T> +struct IsStlPair : + StaticBool< + impl_2384343::HasMemberType_first_type <T>::value&& + impl_2384343::HasMemberType_second_type<T>::value&& + impl_2384343::HasMember_first <T>::value&& + impl_2384343::HasMember_second <T>::value> {}; + +//###################################################################################### + +//Conversion from arbitrary types to an XML element +enum ValueType +{ + VALUE_TYPE_STL_CONTAINER, + VALUE_TYPE_STL_PAIR, + VALUE_TYPE_OTHER, +}; + +template <class T> +struct GetValueType : StaticEnum<ValueType, + GetTextType<T>::value != TEXT_TYPE_OTHER ? VALUE_TYPE_OTHER : //some string classes are also STL containers, so check this first + IsStlContainer<T>::value ? VALUE_TYPE_STL_CONTAINER : + IsStlPair<T>::value ? VALUE_TYPE_STL_PAIR : + VALUE_TYPE_OTHER> {}; + + +template <class T, ValueType type> +struct ConvertElement; +/* -> expected interface +{ + void writeStruc(const T& value, XmlElement& output) const; + bool readStruc(const XmlElement& input, T& value) const; +}; +*/ + + +//partial specialization: handle conversion for all STL-container types! +template <class T> +struct ConvertElement<T, VALUE_TYPE_STL_CONTAINER> +{ + void writeStruc(const T& value, XmlElement& output) const + { + for (const typename T::value_type& childVal : value) + { + XmlElement& newChild = output.addChild("Item"); + zen::writeStruc(childVal, newChild); + } + } + bool readStruc(const XmlElement& input, T& value) const + { + bool success = true; + value.clear(); + + auto itPair = input.getChildren("Item"); + for (auto it = itPair.first; it != itPair.second; ++it) + { + typename T::value_type childVal; //MSVC 2010 bug: cannot put this into a lambda body + if (zen::readStruc(*it, childVal)) + value.insert(value.end(), childVal); + else + success = false; + } + return success; + } +}; + + +//partial specialization: handle conversion for std::pair +template <class T> +struct ConvertElement<T, VALUE_TYPE_STL_PAIR> +{ + void writeStruc(const T& value, XmlElement& output) const + { + XmlElement& child1 = output.addChild("one"); //don't use "1st/2nd", this will confuse a few pedantic XML parsers + zen::writeStruc(value.first, child1); + + XmlElement& child2 = output.addChild("two"); + zen::writeStruc(value.second, child2); + } + bool readStruc(const XmlElement& input, T& value) const + { + bool success = true; + const XmlElement* child1 = input.getChild("one"); + if (!child1 || !zen::readStruc(*child1, value.first)) + success = false; + + const XmlElement* child2 = input.getChild("two"); + if (!child2 || !zen::readStruc(*child2, value.second)) + success = false; + + return success; + } +}; + + +//partial specialization: not a pure structured type, try text conversion (thereby respect user specializations of writeText()/readText()) +template <class T> +struct ConvertElement<T, VALUE_TYPE_OTHER> +{ + void writeStruc(const T& value, XmlElement& output) const + { + std::string tmp; + writeText(value, tmp); + output.setValue(tmp); + } + bool readStruc(const XmlElement& input, T& value) const + { + std::string rawStr; + input.getValue(rawStr); + return readText(rawStr, value); + } +}; + + +template <class T> inline +void writeStruc(const T& value, XmlElement& output) +{ + ConvertElement<T, GetValueType<T>::value>().writeStruc(value, output); +} + + +template <class T> inline +bool readStruc(const XmlElement& input, T& value) +{ + return ConvertElement<T, GetValueType<T>::value>().readStruc(input, value); +} +} + +#endif //CVRT_STRUC_H_018727409908342709743 diff --git a/zenXml/zenxml/cvrt_text.h b/zenXml/zenxml/cvrt_text.h new file mode 100755 index 00000000..529ead57 --- /dev/null +++ b/zenXml/zenxml/cvrt_text.h @@ -0,0 +1,220 @@ +// ***************************************************************************** +// * 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 freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef CVRT_TEXT_H_018727339083427097434 +#define CVRT_TEXT_H_018727339083427097434 + +#include <zen/utf.h> +#include <zen/string_tools.h> + +namespace zen +{ +/** +\file +\brief Handle conversion of string-convertible types to and from std::string. + +It is \b not required to call these functions directly. They are implicitly used by zen::XmlElement::getValue(), +zen::XmlElement::setValue(), zen::XmlElement::getAttribute() and zen::XmlElement::setAttribute(). +\n\n +Conversions for the following user types are supported by default: + - strings - std::string, std::wstring, char*, wchar_t*, char, wchar_t, ect..., all STL-compatible-string-classes + - numbers - int, double, float, bool, long, ect..., all built-in numbers + - STL containers - std::map, std::set, std::vector, std::list, ect..., all STL-compatible-containers + - std::pair + +You can add support for additional types via template specialization. \n\n +Specialize zen::readStruc() and zen::writeStruc() to enable conversion from structured user types to XML elements. +Specialize zen::readText() and zen::writeText() to enable conversion from string-convertible user types to std::string. +Prefer latter if possible since it does not only enable conversions from XML elements to user data, but also from and to XML attributes. +\n\n +<b> Example: </b> type "bool" +\code +namespace zen +{ +template <> inline +void writeText(const bool& value, std::string& output) +{ + output = value ? "true" : "false"; +} + +template <> inline +bool readText(const std::string& input, bool& value) +{ + const std::string tmp = trimCpy(input); + if (tmp == "true") + value = true; + else if (tmp == "false") + value = false; + else + return false; + return true; +} +} +\endcode +*/ + + +///Convert text to user data - used by XML elements and attributes +/** + \param input Input text. + \param value Conversion target value. + \return "true" if value was read successfully. +*/ +template <class T> bool readText(const std::string& input, T& value); +///Convert user data into text - used by XML elements and attributes +/** + \param value The value to be converted. + \param output Output text. +*/ +template <class T> void writeText(const T& value, std::string& output); + + +/* Different classes of data types: + +----------------------------- +| structured | readStruc/writeStruc - e.g. string-convertible types, STL containers, std::pair, structured user types +| ------------------------- | +| | to-string-convertible | | readText/writeText - e.g. string-like types, all built-in arithmetic numbers, bool +| | --------------- | | +| | | string-like | | | utfTo - e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... +| | --------------- | | +| ------------------------- | +----------------------------- +*/ + + + + + + + + + + + + + + + +//------------------------------ implementation ------------------------------------- + +//Conversion from arbitrary types to text (for use with XML elements and attributes) +enum TextType +{ + TEXT_TYPE_BOOL, + TEXT_TYPE_NUMBER, + TEXT_TYPE_STRING, + TEXT_TYPE_OTHER, +}; + +template <class T> +struct GetTextType : StaticEnum<TextType, + IsSameType<T, bool>::value ? TEXT_TYPE_BOOL : + IsStringLike<T>::value ? TEXT_TYPE_STRING : //string before number to correctly handle char/wchar_t -> this was an issue with Loki only! + IsArithmetic<T>::value ? TEXT_TYPE_NUMBER : // + TEXT_TYPE_OTHER> {}; + +//###################################################################################### + +template <class T, TextType type> +struct ConvertText; +/* -> expected interface +{ + void writeText(const T& value, std::string& output) const; + bool readText(const std::string& input, T& value) const; +}; +*/ + +//partial specialization: type bool +template <class T> +struct ConvertText<T, TEXT_TYPE_BOOL> +{ + void writeText(bool value, std::string& output) const + { + output = value ? "true" : "false"; + } + bool readText(const std::string& input, bool& value) const + { + const std::string tmp = trimCpy(input); + if (tmp == "true") + value = true; + else if (tmp == "false") + value = false; + else + return false; + return true; + } +}; + +//partial specialization: handle conversion for all built-in arithmetic types! +template <class T> +struct ConvertText<T, TEXT_TYPE_NUMBER> +{ + void writeText(const T& value, std::string& output) const + { + output = numberTo<std::string>(value); + } + bool readText(const std::string& input, T& value) const + { + value = stringTo<T>(input); + return true; + } +}; + +//partial specialization: handle conversion for all string-like types! +template <class T> +struct ConvertText<T, TEXT_TYPE_STRING> +{ + void writeText(const T& value, std::string& output) const + { + output = utfTo<std::string>(value); + } + bool readText(const std::string& input, T& value) const + { + value = utfTo<T>(input); + return true; + } +}; + + +//partial specialization: unknown type +template <class T> +struct ConvertText<T, TEXT_TYPE_OTHER> +{ + //########################################################################################################################################### + static_assert(sizeof(T) == -1, ""); + /* + ATTENTION: The data type T is yet unknown to the zen::Xml framework! + + Please provide a specialization for T of the following two functions in order to handle conversions to XML elements and attributes + + template <> void zen::writeText(const T& value, std::string& output) + template <> bool zen::readText(const std::string& input, T& value) + + If T is structured and cannot be converted to a text representation specialize these two functions to allow at least for conversions to XML elements: + + template <> void zen::writeStruc(const T& value, XmlElement& output) + template <> bool zen::readStruc(const XmlElement& input, T& value) + */ + //########################################################################################################################################### +}; + + +template <class T> inline +void writeText(const T& value, std::string& output) +{ + ConvertText<T, GetTextType<T>::value>().writeText(value, output); +} + + +template <class T> inline +bool readText(const std::string& input, T& value) +{ + return ConvertText<T, GetTextType<T>::value>().readText(input, value); +} +} + +#endif //CVRT_TEXT_H_018727339083427097434 diff --git a/zenXml/zenxml/dom.h b/zenXml/zenxml/dom.h new file mode 100755 index 00000000..1b1267d8 --- /dev/null +++ b/zenXml/zenxml/dom.h @@ -0,0 +1,332 @@ +// ***************************************************************************** +// * 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 freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef DOM_H_82085720723894567204564256 +#define DOM_H_82085720723894567204564256 + +#include <string> +#include <map> +#include <zen/fixed_list.h> +#include "cvrt_text.h" //"readText/writeText" + +namespace zen +{ +class XmlDoc; + +/// An XML element +class XmlElement +{ +public: + XmlElement() : parent_(nullptr) {} + + //Construct an empty XML element + template <class String> + explicit XmlElement(const String& name, XmlElement* parentElement = nullptr) : name_(utfTo<std::string>(name)), parent_(parentElement) {} + + ///Retrieve the name of this XML element. + /** + \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... + \returns Name of the XML element. + */ + template <class String> + String getNameAs() const { return utfTo<String>(name_); } + + ///Get the value of this element as a user type. + /** + \tparam T Arbitrary user data type: e.g. any string class, all built-in arithmetic numbers, STL container, ... + \returns "true" if Xml element was successfully converted to value, cannot fail for string-like types + */ + template <class T> + bool getValue(T& value) const { return readStruc(*this, value); } + + ///Set the value of this element. + /** + \tparam T Arbitrary user data type: e.g. any string-like type, all built-in arithmetic numbers, STL container, ... + */ + template <class T> + void setValue(const T& value) { writeStruc(value, *this); } + + ///Retrieve an attribute by name. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \tparam T String-convertible user data type: e.g. any string class, all built-in arithmetic numbers + \param name The name of the attribute to retrieve. + \param value The value of the attribute converted to T. + \return "true" if value was retrieved successfully. + */ + template <class String, class T> + bool getAttribute(const String& name, T& value) const + { + auto it = attributes.find(utfTo<std::string>(name)); + return it == attributes.end() ? false : readText(it->second, value); + } + + ///Create or update an XML attribute. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \tparam T String-convertible user data type: e.g. any string-like type, all built-in arithmetic numbers + \param name The name of the attribute to create or update. + \param value The value to set. + */ + template <class String, class T> + void setAttribute(const String& name, const T& value) + { + std::string attrValue; + writeText(value, attrValue); + attributes[utfTo<std::string>(name)] = attrValue; + } + + ///Remove the attribute with the given name. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + */ + template <class String> + void removeAttribute(const String& name) { attributes.erase(utfTo<std::string>(name)); } + + ///Create a new child element and return a reference to it. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \param name The name of the child element to be created. + */ + template <class String> + XmlElement& addChild(const String& name) + { + std::string utf8Name = utfTo<std::string>(name); + childElements.emplace_back(utf8Name, this); + XmlElement& newElement = childElements.back(); + childElementsSorted.emplace(utf8Name, &newElement); + return newElement; + } + + ///Retrieve a child element with the given name. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \param name The name of the child element to be retrieved. + \return A pointer to the child element or nullptr if none was found. + */ + template <class String> + const XmlElement* getChild(const String& name) const + { + auto it = childElementsSorted.find(utfTo<std::string>(name)); + return it == childElementsSorted.end() ? nullptr : it->second; + } + + ///\sa getChild + template <class String> + XmlElement* getChild(const String& name) + { + return const_cast<XmlElement*>(static_cast<const XmlElement*>(this)->getChild(name)); + } + + template < class IterTy, //underlying iterator type + class T, //target object type + class AccessPolicy > //access policy: see AccessPtrMap + class PtrIter : public std::iterator<std::input_iterator_tag, T>, private AccessPolicy //get rid of shared_ptr indirection + { + public: + PtrIter(IterTy it) : it_(it) {} + PtrIter(const PtrIter& other) : it_(other.it_) {} + PtrIter& operator++() { ++it_; return *this; } + PtrIter operator++(int) { PtrIter tmp(*this); operator++(); return tmp; } + inline friend bool operator==(const PtrIter& lhs, const PtrIter& rhs) { return lhs.it_ == rhs.it_; } + inline friend bool operator!=(const PtrIter& lhs, const PtrIter& rhs) { return !(lhs == rhs); } + T& operator* () const { return AccessPolicy::template objectRef<T>(it_); } + T* operator->() const { return &AccessPolicy::template objectRef<T>(it_); } + private: + IterTy it_; + }; + + struct AccessMapElement + { + template <class T, class IterTy> + T& objectRef(const IterTy& it) const { return *(it->second); } + }; + + using ChildIter2 = PtrIter<std::multimap<std::string, XmlElement*>::iterator, XmlElement, AccessMapElement>; + using ChildIterConst2 = PtrIter<std::multimap<std::string, XmlElement*>::const_iterator, const XmlElement, AccessMapElement>; + + ///Access all child elements with the given name via STL iterators. + /** + \code + auto iterPair = elem.getChildren("Item"); + std::for_each(iterPair.first, iterPair.second, + [](const XmlElement& child) { ... }); + \endcode + \param name The name of the child elements to be retrieved. + \return A pair of STL begin/end iterators to access the child elements sequentially. + */ + template <class String> + std::pair<ChildIterConst2, ChildIterConst2> getChildren(const String& name) const { return childElementsSorted.equal_range(utfTo<std::string>(name)); } + + ///\sa getChildren + template <class String> + std::pair<ChildIter2, ChildIter2> getChildren(const String& name) { return childElementsSorted.equal_range(utfTo<std::string>(name)); } + + struct AccessListElement + { + template <class T, class IterTy> + T& objectRef(const IterTy& it) const { return *it; } + }; + + using ChildIter = PtrIter<FixedList<XmlElement>::iterator, XmlElement, AccessListElement>; + using ChildIterConst = PtrIter<FixedList<XmlElement>::const_iterator, const XmlElement, AccessListElement>; + + ///Access all child elements sequentially via STL iterators. + /** + \code + auto iterPair = elem.getChildren(); + std::for_each(iterPair.first, iterPair.second, + [](const XmlElement& child) { ... }); + \endcode + \return A pair of STL begin/end iterators to access all child elements sequentially. + */ + std::pair<ChildIterConst, ChildIterConst> getChildren() const { return std::make_pair(childElements.begin(), childElements.end()); } + + ///\sa getChildren + std::pair<ChildIter, ChildIter> getChildren() { return std::make_pair(childElements.begin(), childElements.end()); } + + ///Get parent XML element, may be nullptr for root element + XmlElement* parent() { return parent_; } + ///Get parent XML element, may be nullptr for root element + const XmlElement* parent() const { return parent_; } + + using AttrIter = std::map<std::string, std::string>::const_iterator; + + /* -> disabled documentation extraction + \brief Get all attributes associated with the element. + \code + auto iterPair = elem.getAttributes(); + for (auto it = iterPair.first; it != iterPair.second; ++it) + std::cout << "name: " << it->first << " value: " << it->second << "\n"; + \endcode + \return A pair of STL begin/end iterators to access all attributes sequentially as a list of name/value pairs of std::string. + */ + std::pair<AttrIter, AttrIter> getAttributes() const { return std::make_pair(attributes.begin(), attributes.end()); } + + //swap two elements while keeping references to parent. -> disabled documentation extraction + void swapSubtree(XmlElement& other) + { + name_ .swap(other.name_); + value_ .swap(other.value_); + attributes.swap(other.attributes); + childElements.swap(other.childElements); + childElementsSorted.swap(other.childElementsSorted); + //std::swap(parent_, other.parent_); -> parent is physical location; update children's parent reference instead: + for (XmlElement& child : childElements) + child.parent_ = this; + for (XmlElement& child : other.childElements) + child.parent_ = &other; + } + +private: + XmlElement (const XmlElement&) = delete; + XmlElement& operator=(const XmlElement&) = delete; + + std::string name_; + std::string value_; + std::map<std::string, std::string> attributes; + FixedList<XmlElement> childElements; //all child elements in order of creation + std::multimap<std::string, XmlElement*> childElementsSorted; //alternate key: sorted by element name + XmlElement* parent_; +}; + + +//XmlElement::setValue<T>() calls zen::writeStruc() which calls XmlElement::setValue() ... => these two specializations end the circle +template <> inline +void XmlElement::setValue(const std::string& value) { value_ = value; } + +template <> inline +bool XmlElement::getValue(std::string& value) const { value = value_; return true; } + + +///The complete XML document +class XmlDoc +{ +public: + ///Default constructor setting up an empty XML document with a standard declaration: <?xml version="1.0" encoding="UTF-8" ?> + XmlDoc() : version_("1.0"), encoding_("UTF-8"), rootElement("Root") {} + + XmlDoc(XmlDoc&& tmp) { swap(tmp); } + XmlDoc& operator=(XmlDoc&& tmp) { swap(tmp); return *this; } + + //Setup an empty XML document + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \param rootName The name of the XML document's root element. + */ + template <class String> + XmlDoc(String rootName) : version_("1.0"), encoding_("UTF-8"), rootElement(rootName) {} + + ///Get a const reference to the document's root element. + const XmlElement& root() const { return rootElement; } + ///Get a reference to the document's root element. + XmlElement& root() { return rootElement; } + + ///Get the version used in the XML declaration. + /** + \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... + */ + template <class String> + String getVersionAs() const { return utfTo<String>(version_); } + + ///Set the version used in the XML declaration. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + */ + template <class String> + void setVersion(const String& version) { version_ = utfTo<std::string>(version); } + + ///Get the encoding used in the XML declaration. + /** + \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... + */ + template <class String> + String getEncodingAs() const { return utfTo<String>(encoding_); } + + ///Set the encoding used in the XML declaration. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + */ + template <class String> + void setEncoding(const String& encoding) { encoding_ = utfTo<std::string>(encoding); } + + ///Get the standalone string used in the XML declaration. + /** + \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... + */ + template <class String> + String getStandaloneAs() const { return utfTo<String>(standalone_); } + + ///Set the standalone string used in the XML declaration. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + */ + template <class String> + void setStandalone(const String& standalone) { standalone_ = utfTo<std::string>(standalone); } + + //Transactionally swap two elements. -> disabled documentation extraction + void swap(XmlDoc& other) + { + version_ .swap(other.version_); + encoding_ .swap(other.encoding_); + standalone_.swap(other.standalone_); + rootElement.swapSubtree(other.rootElement); + } + +private: + XmlDoc (const XmlDoc&) = delete; //not implemented, thanks to XmlElement::parent_ + XmlDoc& operator=(const XmlDoc&) = delete; + + std::string version_; + std::string encoding_; + std::string standalone_; + + XmlElement rootElement; +}; + +} + +#endif //DOM_H_82085720723894567204564256 diff --git a/zenXml/zenxml/error.h b/zenXml/zenxml/error.h new file mode 100755 index 00000000..b50da44a --- /dev/null +++ b/zenXml/zenxml/error.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-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef ERROR_H_01873461843302148947321 +#define ERROR_H_01873461843302148947321 + +namespace zen +{ +///Exception base class for zen::Xml +struct XmlError +{ + virtual ~XmlError() {} +}; +} + +#endif //ERROR_H_01873461843302148947321 diff --git a/zenXml/zenxml/io.h b/zenXml/zenxml/io.h new file mode 100755 index 00000000..7bb1a01d --- /dev/null +++ b/zenXml/zenxml/io.h @@ -0,0 +1,97 @@ +// ***************************************************************************** +// * 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 freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef IO_H_8917640501480763248343343 +#define IO_H_8917640501480763248343343 + +#include <cstdio> +#include <cerrno> +#include <zen/scope_guard.h> +#include <zen/utf.h> +#include "error.h" + +namespace zen +{ +/** +\file +\brief Save and load byte streams from files +*/ + + + +///Exception thrown due to failed file I/O +struct XmlFileError : public XmlError +{ + using ErrorCode = int; + + explicit XmlFileError(ErrorCode ec) : lastError(ec) {} + ///Native error code: errno + ErrorCode lastError; +}; + + + + +///Save byte stream to a file +/** +\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... +\param stream Input byte stream +\param filename Output file name +\throw XmlFileError +*/ +template <class String> +void saveStream(const std::string& stream, const String& filename) //throw XmlFileError +{ + FILE* handle = ::fopen(utfTo<std::string>(filename).c_str(), "w"); + if (!handle) + throw XmlFileError(errno); + ZEN_ON_SCOPE_EXIT(::fclose(handle)); + + const size_t bytesWritten = ::fwrite(stream.c_str(), 1, stream.size(), handle); + if (::ferror(handle) != 0) + throw XmlFileError(errno); + + (void)bytesWritten; + assert(bytesWritten == stream.size()); +} + + +///Load byte stream from a file +/** +\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... +\param filename Input file name +\return Output byte stream +\throw XmlFileError +*/ +template <class String> +std::string loadStream(const String& filename) //throw XmlFileError +{ + FILE* handle = ::fopen(utfTo<std::string>(filename).c_str(), "r"); + if (!handle) + throw XmlFileError(errno); + ZEN_ON_SCOPE_EXIT(::fclose(handle)); + + std::string stream; + const size_t blockSize = 64 * 1024; + do + { + stream.resize(stream.size() + blockSize); //let's pray std::string implements exponential growth! + + const size_t bytesRead = ::fread(&*(stream.end() - blockSize), 1, blockSize, handle); + if (::ferror(handle)) + throw XmlFileError(errno); + if (bytesRead > blockSize) + throw XmlFileError(0); + if (bytesRead < blockSize) + stream.resize(stream.size() - blockSize + bytesRead); //caveat: unsigned arithmetics + } + while (!::feof(handle)); + + return stream; +} +} + +#endif //IO_H_8917640501480763248343343 diff --git a/zenXml/zenxml/parser.h b/zenXml/zenxml/parser.h new file mode 100755 index 00000000..5a529f9a --- /dev/null +++ b/zenXml/zenxml/parser.h @@ -0,0 +1,582 @@ +// ***************************************************************************** +// * 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 freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef PARSER_H_81248670213764583021432 +#define PARSER_H_81248670213764583021432 + +#include <cstdio> +#include <cstddef> //ptrdiff_t; req. on Linux +#include <zen/string_tools.h> +#include "dom.h" +#include "error.h" + + +namespace zen +{ +/** +\file +\brief Convert an XML document object model (class XmlDoc) to and from a byte stream representation. +*/ + +///Save XML document as a byte stream +/** +\param doc Input XML document +\param lineBreak Line break, default: carriage return + new line +\param indent Indentation, default: four space characters +\return Output byte stream +*/ +std::string serialize(const XmlDoc& doc, + const std::string& lineBreak = "\r\n", + const std::string& indent = " "); //throw () + +///Exception thrown due to an XML parsing error +struct XmlParsingError : public XmlError +{ + XmlParsingError(size_t rowNo, size_t colNo) : row(rowNo), col(colNo) {} + ///Input file row where the parsing error occured (zero-based) + const size_t row; //beginning with 0 + ///Input file column where the parsing error occured (zero-based) + const size_t col; // +}; + + +///Load XML document from a byte stream +/** +\param stream Input byte stream +\returns Output XML document +\throw XmlParsingError +*/ +XmlDoc parse(const std::string& stream); //throw XmlParsingError + + + + + + + + + + + + + + + + + + + + +//---------------------------- implementation ---------------------------- +//see: http://www.w3.org/TR/xml/ + +namespace implementation +{ +template <class Predicate> inline +std::string normalize(const std::string& str, Predicate pred) //pred: unary function taking a char, return true if value shall be encoded as hex +{ + std::string output; + for (const char c : str) + { + if (c == '&') // + output += "&"; + else if (c == '<') //normalization mandatory: http://www.w3.org/TR/xml/#syntax + output += "<"; + else if (c == '>') // + output += ">"; + else if (pred(c)) + { + if (c == '\'') + output += "'"; + else if (c == '\"') + output += """; + else + { + output += "&#x"; + const auto hexDigits = hexify(c); + output += hexDigits.first; + output += hexDigits.second; + output += ';'; + } + } + else + output += c; + } + return output; +} + +inline +std::string normalizeName(const std::string& str) +{ + return normalize(str, [](char c) { return isWhiteSpace(c) || c == '=' || c == '/' || c == '\'' || c == '\"'; }); +} + +inline +std::string normalizeElementValue(const std::string& str) +{ + return normalize(str, [](char c) { return static_cast<unsigned char>(c) < 32; }); +} + +inline +std::string normalizeAttribValue(const std::string& str) +{ + return normalize(str, [](char c) { return static_cast<unsigned char>(c) < 32 || c == '\'' || c == '\"'; }); +} + + +template <class CharIterator, size_t N> inline +bool checkEntity(CharIterator& first, CharIterator last, const char (&placeholder)[N]) +{ + assert(placeholder[N - 1] == 0); + const ptrdiff_t strLen = N - 1; //don't count null-terminator + if (last - first >= strLen && std::equal(first, first + strLen, placeholder)) + { + first += strLen - 1; + return true; + } + return false; +} + + +namespace +{ +std::string denormalize(const std::string& str) +{ + std::string output; + for (auto it = str.begin(); it != str.end(); ++it) + { + const char c = *it; + + if (c == '&') + { + if (checkEntity(it, str.end(), "&")) + output += '&'; + else if (checkEntity(it, str.end(), "<")) + output += '<'; + else if (checkEntity(it, str.end(), ">")) + output += '>'; + else if (checkEntity(it, str.end(), "'")) + output += '\''; + else if (checkEntity(it, str.end(), """)) + output += '\"'; + else if (str.end() - it >= 6 && + it[1] == '#' && + it[2] == 'x' && + it[5] == ';') + { + output += unhexify(it[3], it[4]); + it += 5; + } + else + output += c; //unexpected char! + } + else if (c == '\r') //map all end-of-line characters to \n http://www.w3.org/TR/xml/#sec-line-ends + { + auto itNext = it + 1; + if (itNext != str.end() && *itNext == '\n') + ++it; + output += '\n'; + } + else + output += c; + } + return output; +} + + +void serialize(const XmlElement& element, std::string& stream, + const std::string& lineBreak, + const std::string& indent, + size_t indentLevel) +{ + const std::string& nameFmt = normalizeName(element.getNameAs<std::string>()); + + for (size_t i = 0; i < indentLevel; ++i) + stream += indent; + + stream += '<' + nameFmt; + + auto attr = element.getAttributes(); + for (auto it = attr.first; it != attr.second; ++it) + stream += ' ' + normalizeName(it->first) + "=\"" + normalizeAttribValue(it->second) + '\"'; + + //no support for mixed-mode content + auto iterPair = element.getChildren(); + if (iterPair.first != iterPair.second) //structured element + { + stream += '>' + lineBreak; + + std::for_each(iterPair.first, iterPair.second, + [&](const XmlElement& el) { serialize(el, stream, lineBreak, indent, indentLevel + 1); }); + + for (size_t i = 0; i < indentLevel; ++i) + stream += indent; + stream += "</" + nameFmt + '>' + lineBreak; + } + else + { + std::string value; + element.getValue(value); + + if (!value.empty()) //value element + stream += '>' + normalizeElementValue(value) + "</" + nameFmt + '>' + lineBreak; + else //empty element + stream += "/>" + lineBreak; + } +} + +std::string serialize(const XmlDoc& doc, + const std::string& lineBreak, + const std::string& indent) +{ + std::string version = doc.getVersionAs<std::string>(); + if (!version.empty()) + version = " version=\"" + normalizeAttribValue(version) + '\"'; + + std::string encoding = doc.getEncodingAs<std::string>(); + if (!encoding.empty()) + encoding = " encoding=\"" + normalizeAttribValue(encoding) + '\"'; + + std::string standalone = doc.getStandaloneAs<std::string>(); + if (!standalone.empty()) + standalone = " standalone=\"" + normalizeAttribValue(standalone) + '\"'; + + std::string output = "<?xml" + version + encoding + standalone + "?>" + lineBreak; + serialize(doc.root(), output, lineBreak, indent, 0); + return output; +} +} +} + +inline +std::string serialize(const XmlDoc& doc, + const std::string& lineBreak, + const std::string& indent) { return implementation::serialize(doc, lineBreak, indent); } + +/* +Grammar for XML parser +------------------------------- +document-expression: + <?xml version="1.0" encoding="UTF-8" standalone="yes"?> + element-expression: + +element-expression: + <string attributes-expression/> + <string attributes-expression> pm-expression </string> + +element-list-expression: + <empty> + element-expression element-list-expression + +attributes-expression: + <empty> + string="string" attributes-expression + +pm-expression: + string + element-list-expression +*/ + +namespace implementation +{ +struct Token +{ + enum Type + { + TK_LESS, + TK_GREATER, + TK_LESS_SLASH, + TK_SLASH_GREATER, + TK_EQUAL, + TK_QUOTE, + TK_DECL_BEGIN, + TK_DECL_END, + TK_NAME, + TK_END + }; + + Token(Type t) : type(t) {} + Token(const std::string& txt) : type(TK_NAME), name(txt) {} + + Type type; + std::string name; //filled if type == TK_NAME +}; + +class Scanner +{ +public: + Scanner(const std::string& stream) : stream_(stream), pos(stream_.begin()) + { + if (zen::startsWith(stream_, BYTE_ORDER_MARK_UTF8)) + pos += strLength(BYTE_ORDER_MARK_UTF8); + } + + Token nextToken() //throw XmlParsingError + { + //skip whitespace + pos = std::find_if(pos, stream_.end(), [](char c) { return !zen::isWhiteSpace(c); }); + + if (pos == stream_.end()) + return Token::TK_END; + + //skip XML comments + if (startsWith(xmlCommentBegin)) + { + auto it = std::search(pos + xmlCommentBegin.size(), stream_.end(), xmlCommentEnd.begin(), xmlCommentEnd.end()); + if (it != stream_.end()) + { + pos = it + xmlCommentEnd.size(); + return nextToken(); + } + } + + for (auto it = tokens.begin(); it != tokens.end(); ++it) + if (startsWith(it->first)) + { + pos += it->first.size(); + return it->second; + } + + auto nameEnd = std::find_if(pos, stream_.end(), [](char c) + { + return c == '<' || + c == '>' || + c == '=' || + c == '/' || + c == '\'' || + c == '\"' || + zen::isWhiteSpace(c); + }); + + if (nameEnd != pos) + { + std::string name(&*pos, nameEnd - pos); + pos = nameEnd; + return implementation::denormalize(name); + } + + //unknown token + throw XmlParsingError(posRow(), posCol()); + } + + std::string extractElementValue() + { + auto it = std::find_if(pos, stream_.end(), [](char c) + { + return c == '<' || + c == '>'; + }); + std::string output(pos, it); + pos = it; + return implementation::denormalize(output); + } + + std::string extractAttributeValue() + { + auto it = std::find_if(pos, stream_.end(), [](char c) + { + return c == '<' || + c == '>' || + c == '\'' || + c == '\"'; + }); + std::string output(pos, it); + pos = it; + return implementation::denormalize(output); + } + + size_t posRow() const //current row beginning with 0 + { + const size_t crSum = std::count(stream_.begin(), pos, '\r'); //carriage returns + const size_t nlSum = std::count(stream_.begin(), pos, '\n'); //new lines + assert(crSum == 0 || nlSum == 0 || crSum == nlSum); + return std::max(crSum, nlSum); //be compatible with Linux/Mac/Win + } + + size_t posCol() const //current col beginning with 0 + { + //seek beginning of line + for (auto it = pos; it != stream_.begin(); ) + { + --it; + if (*it == '\r' || *it == '\n') + return pos - it - 1; + } + return pos - stream_.begin(); + } + +private: + Scanner (const Scanner&) = delete; + Scanner& operator=(const Scanner&) = delete; + + bool startsWith(const std::string& prefix) const + { + if (stream_.end() - pos < static_cast<ptrdiff_t>(prefix.size())) + return false; + return std::equal(prefix.begin(), prefix.end(), pos); + } + + using TokenList = std::vector<std::pair<std::string, Token::Type>>; + const TokenList tokens + { + { "<?xml", Token::TK_DECL_BEGIN }, + { "?>", Token::TK_DECL_END }, + { "</", Token::TK_LESS_SLASH }, + { "/>", Token::TK_SLASH_GREATER }, + { "<", Token::TK_LESS }, //evaluate after TK_DECL_BEGIN! + { ">", Token::TK_GREATER }, + { "=", Token::TK_EQUAL }, + { "\"", Token::TK_QUOTE }, + { "\'", Token::TK_QUOTE }, + }; + + const std::string xmlCommentBegin = "<!--"; + const std::string xmlCommentEnd = "-->"; + + const std::string stream_; + std::string::const_iterator pos; +}; + + +class XmlParser +{ +public: + XmlParser(const std::string& stream) : + scn_(stream), + tk_(scn_.nextToken()) {} + + XmlDoc parse() //throw XmlParsingError + { + XmlDoc doc; + + //declaration (optional) + if (token().type == Token::TK_DECL_BEGIN) + { + nextToken(); + + while (token().type == Token::TK_NAME) + { + std::string attribName = token().name; + nextToken(); + + consumeToken(Token::TK_EQUAL); + expectToken(Token::TK_QUOTE); + std::string attribValue = scn_.extractAttributeValue(); + nextToken(); + + consumeToken(Token::TK_QUOTE); + + if (attribName == "version") + doc.setVersion(attribValue); + else if (attribName == "encoding") + doc.setEncoding(attribValue); + else if (attribName == "standalone") + doc.setStandalone(attribValue); + } + consumeToken(Token::TK_DECL_END); + } + + XmlElement dummy; + parseChildElements(dummy); + + auto itPair = dummy.getChildren(); + if (itPair.first != itPair.second) + doc.root().swapSubtree(*itPair.first); + + expectToken(Token::TK_END); + return doc; + } + +private: + XmlParser (const XmlParser&) = delete; + XmlParser& operator=(const XmlParser&) = delete; + + void parseChildElements(XmlElement& parent) + { + while (token().type == Token::TK_LESS) + { + nextToken(); + + expectToken(Token::TK_NAME); + std::string elementName = token().name; + nextToken(); + + XmlElement& newElement = parent.addChild(elementName); + + parseAttributes(newElement); + + if (token().type == Token::TK_SLASH_GREATER) //empty element + { + nextToken(); + continue; + } + + expectToken(Token::TK_GREATER); + std::string elementValue = scn_.extractElementValue(); + nextToken(); + + //no support for mixed-mode content + if (token().type == Token::TK_LESS) //structured element + parseChildElements(newElement); + else //value element + newElement.setValue(elementValue); + + consumeToken(Token::TK_LESS_SLASH); + + if (token().type != Token::TK_NAME || + elementName != token().name) + throw XmlParsingError(scn_.posRow(), scn_.posCol()); + nextToken(); + + consumeToken(Token::TK_GREATER); + } + } + + void parseAttributes(XmlElement& element) + { + while (token().type == Token::TK_NAME) + { + std::string attribName = token().name; + nextToken(); + + consumeToken(Token::TK_EQUAL); + expectToken(Token::TK_QUOTE); + std::string attribValue = scn_.extractAttributeValue(); + nextToken(); + + consumeToken(Token::TK_QUOTE); + element.setAttribute(attribName, attribValue); + } + } + + const Token& token() const { return tk_; } + void nextToken() { tk_ = scn_.nextToken(); } + + void consumeToken(Token::Type t) //throw XmlParsingError + { + expectToken(t); //throw XmlParsingError + nextToken(); + } + + void expectToken(Token::Type t) //throw XmlParsingError + { + if (token().type != t) + throw XmlParsingError(scn_.posRow(), scn_.posCol()); + } + + Scanner scn_; + Token tk_; +}; +} + +inline +XmlDoc parse(const std::string& stream) //throw XmlParsingError +{ + return implementation::XmlParser(stream).parse(); //throw XmlParsingError +} +} + +#endif //PARSER_H_81248670213764583021432 diff --git a/zenXml/zenxml/xml.h b/zenXml/zenxml/xml.h new file mode 100755 index 00000000..d3ce2814 --- /dev/null +++ b/zenXml/zenxml/xml.h @@ -0,0 +1,15 @@ +// ***************************************************************************** +// * 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 freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef XML_H_349578228034572457454554 +#define XML_H_349578228034572457454554 + +#include "bind.h" + +/// The zen::Xml namespace +namespace zen {} + +#endif //XML_H_349578228034572457454554 |