summaryrefslogtreecommitdiff
path: root/lib/process_xml.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/process_xml.cpp')
-rw-r--r--lib/process_xml.cpp1174
1 files changed, 1174 insertions, 0 deletions
diff --git a/lib/process_xml.cpp b/lib/process_xml.cpp
new file mode 100644
index 00000000..777c3ed6
--- /dev/null
+++ b/lib/process_xml.cpp
@@ -0,0 +1,1174 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) *
+// **************************************************************************
+
+#include "process_xml.h"
+#include <zenxml/xml.h>
+#include "ffs_paths.h"
+#include <wx+/string_conv.h>
+#include <zen/file_handling.h>
+#include <zen/file_io.h>
+#include "xml_base.h"
+
+using namespace zen;
+using namespace xmlAccess; //functionally needed for correct overload resolution!!!
+
+
+XmlType getXmlType(const zen::XmlDoc& doc) //throw()
+{
+ if (doc.root().getNameAs<std::string>() == "FreeFileSync")
+ {
+ std::string type;
+ if (doc.root().getAttribute("XmlType", type))
+ {
+ if (type == "GUI")
+ return XML_TYPE_GUI;
+ else if (type == "BATCH")
+ return XML_TYPE_BATCH;
+ else if (type == "GLOBAL")
+ return XML_TYPE_GLOBAL;
+ }
+ }
+ return XML_TYPE_OTHER;
+}
+
+
+XmlType xmlAccess::getXmlType(const wxString& filename) //throw()
+{
+ XmlDoc doc;
+ try
+ {
+ //do NOT use zen::loadStream as it will superfluously load even huge files!
+ loadXmlDocument(toZ(filename), doc); //throw FfsXmlError, quick exit if file is not an FFS XML
+ }
+ catch (const FfsXmlError&)
+ {
+ return XML_TYPE_OTHER;
+ }
+ return ::getXmlType(doc);
+}
+
+
+void setXmlType(XmlDoc& doc, XmlType type) //throw()
+{
+ switch (type)
+ {
+ case XML_TYPE_GUI:
+ doc.root().setAttribute("XmlType", "GUI");
+ break;
+ case XML_TYPE_BATCH:
+ doc.root().setAttribute("XmlType", "BATCH");
+ break;
+ case XML_TYPE_GLOBAL:
+ doc.root().setAttribute("XmlType", "GLOBAL");
+ break;
+ case XML_TYPE_OTHER:
+ assert(false);
+ break;
+ }
+}
+//################################################################################################################
+
+
+wxString xmlAccess::getGlobalConfigFile()
+{
+ return zen::getConfigDir() + wxT("GlobalSettings.xml");
+}
+
+
+void xmlAccess::OptionalDialogs::resetDialogs()
+{
+ warningDependentFolders = true;
+ warningMultiFolderWriteAccess = true;
+ warningSignificantDifference = true;
+ warningNotEnoughDiskSpace = true;
+ warningUnresolvedConflicts = true;
+ warningSyncDatabase = true;
+ warningRecyclerMissing = true;
+ popupOnConfigChange = true;
+ showSummaryBeforeSync = true;
+}
+
+
+xmlAccess::XmlGuiConfig xmlAccess::convertBatchToGui(const xmlAccess::XmlBatchConfig& batchCfg)
+{
+ XmlGuiConfig output;
+ output.mainCfg = batchCfg.mainCfg;
+
+ switch (batchCfg.handleError)
+ {
+ case ON_ERROR_EXIT:
+ case ON_ERROR_POPUP:
+ output.handleError = ON_GUIERROR_POPUP;
+ break;
+ case ON_ERROR_IGNORE:
+ output.handleError = ON_GUIERROR_IGNORE;
+ break;
+ }
+ return output;
+}
+
+
+xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg, const wxString& referenceFile)
+{
+ //try to take over batch-specific settings from reference
+ if (!referenceFile.empty() && getXmlType(referenceFile) == XML_TYPE_BATCH)
+ try
+ {
+ XmlBatchConfig output;
+
+ std::vector<wxString> filenames;
+ filenames.push_back(referenceFile);
+ convertConfig(filenames, output); //throw xmlAccess::FfsXmlError
+
+ output.mainCfg = guiCfg.mainCfg;
+ return output;
+ }
+ catch (xmlAccess::FfsXmlError&) {}
+
+ XmlBatchConfig output; //use default batch-settings
+ output.mainCfg = guiCfg.mainCfg;
+
+ switch (guiCfg.handleError)
+ {
+ case ON_GUIERROR_POPUP:
+ output.handleError = ON_ERROR_POPUP;
+ break;
+ case ON_GUIERROR_IGNORE:
+ output.handleError = ON_ERROR_IGNORE;
+ break;
+ }
+
+ return output;
+}
+
+
+xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<wxString>& filenames) //throw ()
+{
+ bool guiCfgExists = false;
+ bool batchCfgExists = false;
+
+ for (auto iter = filenames.begin(); iter != filenames.end(); ++iter)
+ {
+ switch (xmlAccess::getXmlType(*iter)) //throw()
+ {
+ case XML_TYPE_GUI:
+ guiCfgExists = true;
+ break;
+
+ case XML_TYPE_BATCH:
+ batchCfgExists = true;
+ break;
+
+ case XML_TYPE_GLOBAL:
+ case XML_TYPE_OTHER:
+ return MERGE_OTHER;
+ }
+ }
+
+ if (guiCfgExists && batchCfgExists)
+ return MERGE_GUI_BATCH;
+ else if (guiCfgExists && !batchCfgExists)
+ return MERGE_GUI;
+ else if (!guiCfgExists && batchCfgExists)
+ return MERGE_BATCH;
+ else
+ return MERGE_OTHER;
+}
+
+
+namespace
+{
+template <class XmlCfg>
+XmlCfg loadCfgImpl(const wxString& filename, std::unique_ptr<xmlAccess::FfsXmlError>& exeption) //throw xmlAccess::FfsXmlError
+{
+ XmlCfg cfg;
+ try
+ {
+ xmlAccess::readConfig(filename, cfg); //throw xmlAccess::FfsXmlError
+ }
+ catch (const xmlAccess::FfsXmlError& e)
+ {
+ if (e.getSeverity() == xmlAccess::FfsXmlError::FATAL)
+ throw;
+ else
+ exeption.reset(new xmlAccess::FfsXmlError(e));
+ }
+ return cfg;
+}
+
+
+template <class XmlCfg>
+void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config) //throw xmlAccess::FfsXmlError
+{
+ assert(!filenames.empty());
+ if (filenames.empty())
+ return;
+
+ std::vector<zen::MainConfiguration> mainCfgs;
+ std::unique_ptr<FfsXmlError> savedException;
+
+ std::for_each(filenames.begin(), filenames.end(),
+ [&](const wxString& filename)
+ {
+ switch (getXmlType(filename))
+ {
+ case XML_TYPE_GUI:
+ mainCfgs.push_back(loadCfgImpl<XmlGuiConfig>(filename, savedException).mainCfg); //throw xmlAccess::FfsXmlError
+ break;
+
+ case XML_TYPE_BATCH:
+ mainCfgs.push_back(loadCfgImpl<XmlBatchConfig>(filename, savedException).mainCfg); //throw xmlAccess::FfsXmlError
+ break;
+
+ case XML_TYPE_GLOBAL:
+ case XML_TYPE_OTHER:
+ break;
+ }
+ });
+
+ if (mainCfgs.empty())
+ throw FfsXmlError(_("Invalid FreeFileSync config file!"));
+
+ try //...to init all non-"mainCfg" settings with first config file
+ {
+ xmlAccess::readConfig(filenames[0], config); //throw xmlAccess::FfsXmlError
+ }
+ catch (xmlAccess::FfsXmlError&) {}
+
+ config.mainCfg = merge(mainCfgs);
+
+ if (savedException.get()) //"re-throw" exception
+ throw* savedException;
+}
+}
+
+
+void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlGuiConfig& config) //throw (xmlAccess::FfsXmlError)
+{
+ mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::FfsXmlError)
+}
+
+
+void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlBatchConfig& config) //throw (xmlAccess::FfsXmlError);
+{
+ mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::FfsXmlError)
+}
+
+
+namespace zen
+{
+template <> inline
+void writeText(const CompareVariant& value, std::string& output)
+{
+ switch (value)
+ {
+ case zen::CMP_BY_TIME_SIZE:
+ output = "ByTimeAndSize";
+ break;
+ case zen::CMP_BY_CONTENT:
+ output = "ByContent";
+ break;
+ }
+}
+
+template <> inline
+bool readText(const std::string& input, CompareVariant& value)
+{
+ std::string tmp = input;
+ zen::trim(tmp);
+ if (tmp == "ByTimeAndSize")
+ value = zen::CMP_BY_TIME_SIZE;
+ else if (tmp == "ByContent")
+ value = zen::CMP_BY_CONTENT;
+ else
+ return false;
+ return true;
+}
+
+
+template <> inline
+void writeText(const SyncDirection& value, std::string& output)
+{
+ switch (value)
+ {
+ case SYNC_DIR_LEFT:
+ output = "left";
+ break;
+ case SYNC_DIR_RIGHT:
+ output = "right";
+ break;
+ case SYNC_DIR_NONE:
+ output = "none";
+ break;
+ }
+}
+
+template <> inline
+bool readText(const std::string& input, SyncDirection& value)
+{
+ std::string tmp = input;
+ zen::trim(tmp);
+ if (tmp == "left")
+ value = SYNC_DIR_LEFT;
+ else if (tmp == "right")
+ value = SYNC_DIR_RIGHT;
+ else if (tmp == "none")
+ value = SYNC_DIR_NONE;
+ else
+ return false;
+ return true;
+}
+
+
+template <> inline
+void writeText(const OnError& value, std::string& output)
+{
+ switch (value)
+ {
+ case ON_ERROR_IGNORE:
+ output = "Ignore";
+ break;
+ case ON_ERROR_EXIT:
+ output = "Exit";
+ break;
+ case ON_ERROR_POPUP:
+ output = "Popup";
+ break;
+ }
+}
+
+template <> inline
+bool readText(const std::string& input, OnError& value)
+{
+ std::string tmp = input;
+ zen::trim(tmp);
+ if (tmp == "Ignore")
+ value = ON_ERROR_IGNORE;
+ else if (tmp == "Exit")
+ value = ON_ERROR_EXIT;
+ else if (tmp == "Popup")
+ value = ON_ERROR_POPUP;
+ else
+ return false;
+ return true;
+}
+
+
+template <> inline
+void writeText(const OnGuiError& value, std::string& output)
+{
+ switch (value)
+ {
+ case ON_GUIERROR_IGNORE:
+ output = "Ignore";
+ break;
+ case ON_GUIERROR_POPUP:
+ output = "Popup";
+ break;
+ }
+}
+
+template <> inline
+bool readText(const std::string& input, OnGuiError& value)
+{
+ std::string tmp = input;
+ zen::trim(tmp);
+ if (tmp == "Ignore")
+ value = ON_GUIERROR_IGNORE;
+ else if (tmp == "Popup")
+ value = ON_GUIERROR_POPUP;
+ else
+ return false;
+ return true;
+}
+
+
+template <> inline
+void writeText(const FileIconSize& value, std::string& output)
+{
+ switch (value)
+ {
+ case ICON_SIZE_SMALL:
+ output = "Small";
+ break;
+ case ICON_SIZE_MEDIUM:
+ output = "Medium";
+ break;
+ case ICON_SIZE_LARGE:
+ output = "Large";
+ break;
+ }
+}
+
+
+template <> inline
+bool readText(const std::string& input, FileIconSize& value)
+{
+ std::string tmp = input;
+ zen::trim(tmp);
+ if (tmp == "Small")
+ value = ICON_SIZE_SMALL;
+ else if (tmp == "Medium")
+ value = ICON_SIZE_MEDIUM;
+ else if (tmp == "Large")
+ value = ICON_SIZE_LARGE;
+ else
+ return false;
+ return true;
+}
+
+
+template <> inline
+void writeText(const DeletionPolicy& value, std::string& output)
+{
+ switch (value)
+ {
+ case DELETE_PERMANENTLY:
+ output = "DeletePermanently";
+ break;
+ case MOVE_TO_RECYCLE_BIN:
+ output = "MoveToRecycleBin";
+ break;
+ case MOVE_TO_CUSTOM_DIRECTORY:
+ output = "MoveToCustomDirectory";
+ break;
+ }
+}
+
+template <> inline
+bool readText(const std::string& input, DeletionPolicy& value)
+{
+ std::string tmp = input;
+ zen::trim(tmp);
+ if (tmp == "DeletePermanently")
+ value = DELETE_PERMANENTLY;
+ else if (tmp == "MoveToRecycleBin")
+ value = MOVE_TO_RECYCLE_BIN;
+ else if (tmp == "MoveToCustomDirectory")
+ value = MOVE_TO_CUSTOM_DIRECTORY;
+ else
+ return false;
+ return true;
+}
+
+
+template <> inline
+void writeText(const SymLinkHandling& value, std::string& output)
+{
+ switch (value)
+ {
+ case SYMLINK_IGNORE:
+ output = "Ignore";
+ break;
+ case SYMLINK_USE_DIRECTLY:
+ output = "UseDirectly";
+ break;
+ case SYMLINK_FOLLOW_LINK:
+ output = "FollowLink";
+ break;
+ }
+}
+
+template <> inline
+bool readText(const std::string& input, SymLinkHandling& value)
+{
+ std::string tmp = input;
+ zen::trim(tmp);
+ if (tmp == "Ignore")
+ value = SYMLINK_IGNORE;
+ else if (tmp == "UseDirectly")
+ value = SYMLINK_USE_DIRECTLY;
+ else if (tmp == "FollowLink")
+ value = SYMLINK_FOLLOW_LINK;
+ else
+ return false;
+ return true;
+}
+
+
+template <> inline
+void writeText(const UnitTime& value, std::string& output)
+{
+ switch (value)
+ {
+ case UTIME_NONE:
+ output = "Inactive";
+ break;
+ // case UTIME_LAST_X_HOURS:
+ // output = "x-hours";
+ // break;
+ case UTIME_TODAY:
+ output = "Today";
+ break;
+ case UTIME_THIS_WEEK:
+ output = "Week";
+ break;
+ case UTIME_THIS_MONTH:
+ output = "Month";
+ break;
+ case UTIME_THIS_YEAR:
+ output = "Year";
+ break;
+ }
+}
+
+template <> inline
+bool readText(const std::string& input, UnitTime& value)
+{
+ std::string tmp = input;
+ zen::trim(tmp);
+ if (tmp == "Inactive")
+ value = UTIME_NONE;
+ // else if (tmp == "x-hours")
+ // value = UTIME_LAST_X_HOURS;
+ else if (tmp == "Today")
+ value = UTIME_TODAY;
+ else if (tmp == "Week")
+ value = UTIME_THIS_WEEK;
+ else if (tmp == "Month")
+ value = UTIME_THIS_MONTH;
+ else if (tmp == "Year")
+ value = UTIME_THIS_YEAR;
+ else
+ return false;
+ return true;
+}
+
+
+template <> inline
+void writeText(const ColumnTypes& value, std::string& output)
+{
+ output = toString<std::string>(value);
+}
+
+template <> inline
+bool readText(const std::string& input, ColumnTypes& value)
+{
+ value = static_cast<ColumnTypes>(toNumber<int>(input));
+ return true;
+}
+
+
+template <> inline
+void writeText(const UnitSize& value, std::string& output)
+{
+ switch (value)
+ {
+ case USIZE_NONE:
+ output = "Inactive";
+ break;
+ case USIZE_BYTE:
+ output = "Byte";
+ break;
+ case USIZE_KB:
+ output = "KB";
+ break;
+ case USIZE_MB:
+ output = "MB";
+ break;
+ }
+}
+
+template <> inline
+bool readText(const std::string& input, UnitSize& value)
+{
+ std::string tmp = input;
+ zen::trim(tmp);
+ if (tmp == "Inactive")
+ value = USIZE_NONE;
+ else if (tmp == "Byte")
+ value = USIZE_BYTE;
+ else if (tmp == "KB")
+ value = USIZE_KB;
+ else if (tmp == "MB")
+ value = USIZE_MB;
+ else
+ return false;
+ return true;
+}
+
+
+template <> inline
+void writeText(const DirectionConfig::Variant& value, std::string& output)
+{
+ switch (value)
+ {
+ case DirectionConfig::AUTOMATIC:
+ output = "Automatic";
+ break;
+ case DirectionConfig::MIRROR:
+ output = "Mirror";
+ break;
+ case DirectionConfig::UPDATE:
+ output = "Update";
+ break;
+ case DirectionConfig::CUSTOM:
+ output = "Custom";
+ break;
+ }
+}
+
+template <> inline
+bool readText(const std::string& input, DirectionConfig::Variant& value)
+{
+ std::string tmp = input;
+ zen::trim(tmp);
+ if (tmp == "Automatic")
+ value = DirectionConfig::AUTOMATIC;
+ else if (tmp == "Mirror")
+ value = DirectionConfig::MIRROR;
+ else if (tmp == "Update")
+ value = DirectionConfig::UPDATE;
+ else if (tmp == "Custom")
+ value = DirectionConfig::CUSTOM;
+ else
+ return false;
+ return true;
+}
+
+
+template <> inline
+bool readValue(const XmlElement& input, ColumnAttrib& value)
+{
+ XmlIn in(input);
+ bool rv1 = in.attribute("Type", value.type);
+ bool rv2 = in.attribute("Visible", value.visible);
+ bool rv3 = in.attribute("Width", value.width);
+ value.position = 0;
+ return rv1 && rv2 && rv3;
+}
+
+template <> inline
+void writeValue(const ColumnAttrib& value, XmlElement& output)
+{
+ XmlOut out(output);
+ out.attribute("Type", value.type);
+ out.attribute("Visible", value.visible);
+ out.attribute("Width", value.width);
+}
+}
+
+
+namespace
+{
+void readConfig(const XmlIn& in, CompConfig& cmpConfig)
+{
+ in["Variant" ](cmpConfig.compareVar);
+ in["HandleSymlinks"](cmpConfig.handleSymlinks);
+}
+
+
+void readConfig(const XmlIn& in, DirectionConfig& directCfg)
+{
+ in["Variant"](directCfg.var);
+
+ XmlIn inCustDir = in["CustomDirections"];
+ inCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly);
+ inCustDir["RightOnly" ](directCfg.custom.exRightSideOnly);
+ inCustDir["LeftNewer" ](directCfg.custom.leftNewer);
+ inCustDir["RightNewer"](directCfg.custom.rightNewer);
+ inCustDir["Different" ](directCfg.custom.different);
+ inCustDir["Conflict" ](directCfg.custom.conflict);
+}
+
+
+void readConfig(const XmlIn& in, SyncConfig& syncCfg)
+{
+ readConfig(in, syncCfg.directionCfg);
+
+ in["DeletionPolicy" ](syncCfg.handleDeletion);
+ in["CustomDeletionFolder"](syncCfg.customDeletionDirectory);
+}
+
+
+void readConfig(const XmlIn& in, FilterConfig& filter)
+{
+ in["Include"](filter.includeFilter);
+ in["Exclude"](filter.excludeFilter);
+
+ in["TimeSpan" ](filter.timeSpan);
+ in["UnitTimeSpan"](filter.unitTimeSpan);
+
+ in["SizeMin" ](filter.sizeMin);
+ in["UnitSizeMin"](filter.unitSizeMin);
+
+ in["SizeMax" ](filter.sizeMax);
+ in["UnitSizeMax"](filter.unitSizeMax);
+}
+
+
+void readConfig(const XmlIn& in, FolderPairEnh& enhPair)
+{
+ //read folder pairs
+ in["Left" ](enhPair.leftDirectory);
+ in["Right"](enhPair.rightDirectory);
+
+ //###########################################################
+ //alternate comp configuration (optional)
+ XmlIn inAltCmp = in["CompareConfig"];
+ if (inAltCmp)
+ {
+ CompConfig altCmpCfg;
+ readConfig(inAltCmp, altCmpCfg);
+
+ enhPair.altCmpConfig = std::make_shared<CompConfig>(altCmpCfg);;
+ }
+ //###########################################################
+ //alternate sync configuration (optional)
+ XmlIn inAltSync = in["SyncConfig"];
+ if (inAltSync)
+ {
+ SyncConfig altSyncCfg;
+ readConfig(inAltSync, altSyncCfg);
+
+ enhPair.altSyncConfig = std::make_shared<SyncConfig>(altSyncCfg);
+ }
+
+ //###########################################################
+ //alternate filter configuration
+ readConfig(in["LocalFilter"], enhPair.localFilter);
+}
+
+
+void readConfig(const XmlIn& in, MainConfiguration& mainCfg)
+{
+ //read compare settings
+ XmlIn inCmp = in["MainConfig"]["Comparison"];
+
+ readConfig(inCmp, mainCfg.cmpConfig);
+ //###########################################################
+
+ XmlIn inSync = in["MainConfig"]["SyncConfig"];
+
+ //read sync configuration
+ readConfig(inSync, mainCfg.syncCfg);
+ //###########################################################
+
+ XmlIn inFilter = in["MainConfig"]["GlobalFilter"];
+ //read filter settings
+ readConfig(inFilter, mainCfg.globalFilter);
+
+ //###########################################################
+ //read all folder pairs
+ mainCfg.additionalPairs.clear();
+
+ bool firstIter = true;
+ for (XmlIn inPair = in["MainConfig"]["FolderPairs"]["Pair"]; inPair; inPair.next())
+ {
+ FolderPairEnh newPair;
+ readConfig(inPair, newPair);
+
+ if (firstIter)
+ {
+ firstIter = false;
+ mainCfg.firstPair = newPair; //set first folder pair
+ }
+ else
+ mainCfg.additionalPairs.push_back(newPair); //set additional folder pairs
+ }
+}
+
+
+void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config)
+{
+ ::readConfig(in, config.mainCfg); //read main config
+
+ //read GUI specific config data
+ XmlIn inGuiCfg = in["GuiConfig"];
+
+ inGuiCfg["HideFiltered" ](config.hideFilteredElements);
+ inGuiCfg["HandleError" ](config.handleError);
+ inGuiCfg["SyncPreviewActive"](config.syncPreviewEnabled);
+}
+
+
+void readConfig(const XmlIn& in, xmlAccess::XmlBatchConfig& config)
+{
+ ::readConfig(in, config.mainCfg); //read main config
+
+ //read GUI specific config data
+ XmlIn inBatchCfg = in["BatchConfig"];
+
+ inBatchCfg["Silent" ](config.silent);
+ inBatchCfg["LogfileDirectory"](config.logFileDirectory);
+ inBatchCfg["LogfileCountMax" ](config.logFileCountMax);
+ inBatchCfg["HandleError" ](config.handleError);
+}
+
+
+void readConfig(const XmlIn& in, XmlGlobalSettings& config)
+{
+ XmlIn inShared = in["Shared"];
+
+ //try to read program language setting
+ inShared["Language"](config.programLanguage);
+
+ inShared["CopyLockedFiles" ](config.copyLockedFiles);
+ inShared["CopyFilePermissions" ](config.copyFilePermissions);
+ inShared["TransactionalFileCopy"](config.transactionalFileCopy);
+ inShared["VerifyCopiedFiles" ](config.verifyFileCopy);
+
+ //max. allowed file time deviation
+ inShared["FileTimeTolerance"](config.fileTimeTolerance);
+
+ XmlIn inOpt = inShared["ShowOptionalDialogs"];
+ inOpt["CheckForDependentFolders" ](config.optDialogs.warningDependentFolders);
+ inOpt["CheckForMultipleWriteAccess" ](config.optDialogs.warningMultiFolderWriteAccess);
+ inOpt["CheckForSignificantDifference"](config.optDialogs.warningSignificantDifference);
+ inOpt["CheckForFreeDiskSpace"](config.optDialogs.warningNotEnoughDiskSpace);
+ inOpt["CheckForUnresolvedConflicts"](config.optDialogs.warningUnresolvedConflicts);
+ inOpt["NotifyDatabaseError"](config.optDialogs.warningSyncDatabase);
+ inOpt["CheckMissingRecycleBin"](config.optDialogs.warningRecyclerMissing);
+ inOpt["PopupOnConfigChange"](config.optDialogs.popupOnConfigChange);
+ inOpt["SummaryBeforeSync" ](config.optDialogs.showSummaryBeforeSync);
+
+ //gui specific global settings (optional)
+ XmlIn inGui = in["Gui"];
+ XmlIn inWnd = inGui["Windows"]["Main"];
+
+ //read application window size and position
+ inWnd["Width" ](config.gui.dlgSize.x);
+ inWnd["Height" ](config.gui.dlgSize.y);
+ inWnd["PosX" ](config.gui.dlgPos.x);
+ inWnd["PosY" ](config.gui.dlgPos.y);
+ inWnd["Maximized"](config.gui.isMaximized);
+
+ inWnd["MaxFolderPairsVisible"](config.gui.maxFolderPairsVisible);
+
+ inWnd["ManualDeletionOnBothSides"](config.gui.deleteOnBothSides);
+ inWnd["ManualDeletionUseRecycler"](config.gui.useRecyclerForManualDeletion);
+ inWnd["RespectCaseOnSearch" ](config.gui.textSearchRespectCase);
+
+ inWnd["IconSize"](config.gui.iconSize);
+
+ //###########################################################
+ //read column attributes
+ XmlIn inColLeft = inWnd["LeftColumns"];
+ inColLeft.attribute("AutoAdjust", config.gui.autoAdjustColumnsLeft);
+
+ inColLeft(config.gui.columnAttribLeft);
+ for (size_t i = 0; i < config.gui.columnAttribLeft.size(); ++i)
+ config.gui.columnAttribLeft[i].position = i;
+
+ //###########################################################
+ XmlIn inColRight = inWnd["RightColumns"];
+ inColRight.attribute("AutoAdjust", config.gui.autoAdjustColumnsRight);
+
+ inColRight(config.gui.columnAttribRight);
+ for (size_t i = 0; i < config.gui.columnAttribRight.size(); ++i)
+ config.gui.columnAttribRight[i].position = i;
+
+ inWnd["FolderHistoryLeft" ](config.gui.folderHistoryLeft);
+ inWnd["FolderHistoryRight"](config.gui.folderHistoryRight);
+ inWnd["MaximumHistorySize"](config.gui.folderHistMax);
+ inWnd["Perspective" ](config.gui.guiPerspectiveLast);
+
+ //external applications
+ inGui["ExternalApplications"](config.gui.externelApplications);
+
+ //load config file history
+ inGui["LastConfigActive"](config.gui.lastUsedConfigFiles);
+ inGui["ConfigHistory"](config.gui.cfgFileHistory);
+
+ //last update check
+ inGui["LastUpdateCheck"](config.gui.lastUpdateCheck);
+
+ //batch specific global settings
+ //XmlIn inBatch = in["Batch"];
+}
+
+
+template <class ConfigType>
+void readConfig(const Zstring& filename, XmlType type, ConfigType& config)
+{
+ XmlDoc doc;
+ loadXmlDocument(filename, doc); //throw FfsXmlError
+
+ if (getXmlType(doc) != type) //throw()
+ throw FfsXmlError(_("Error parsing configuration file:") + "\n\"" + filename + "\"");
+
+ XmlIn in(doc);
+ ::readConfig(in, config);
+
+ if (in.errorsOccured())
+ throw FfsXmlError(_("Error parsing configuration file:") + "\n\"" + filename + "\"\n\n" +
+ getErrorMessageFormatted(in), FfsXmlError::WARNING);
+}
+}
+
+
+void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlGuiConfig& config)
+{
+ ::readConfig(toZ(filename), XML_TYPE_GUI, config);
+}
+
+
+void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlBatchConfig& config)
+{
+ ::readConfig(toZ(filename), XML_TYPE_BATCH, config);
+}
+
+
+void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& config)
+{
+ ::readConfig(toZ(getGlobalConfigFile()), XML_TYPE_GLOBAL, config);
+}
+
+
+//################################################################################################
+namespace
+{
+void writeConfig(const CompConfig& cmpConfig, XmlOut& out)
+{
+ out["Variant" ](cmpConfig.compareVar);
+ out["HandleSymlinks"](cmpConfig.handleSymlinks);
+}
+
+
+void writeConfig(const DirectionConfig& directCfg, XmlOut& out)
+{
+ out["Variant"](directCfg.var);
+
+ XmlOut outCustDir = out["CustomDirections"];
+ outCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly);
+ outCustDir["RightOnly" ](directCfg.custom.exRightSideOnly);
+ outCustDir["LeftNewer" ](directCfg.custom.leftNewer);
+ outCustDir["RightNewer"](directCfg.custom.rightNewer);
+ outCustDir["Different" ](directCfg.custom.different);
+ outCustDir["Conflict" ](directCfg.custom.conflict);
+}
+
+
+void writeConfig(const SyncConfig& syncCfg, XmlOut& out)
+{
+ writeConfig(syncCfg.directionCfg, out);
+
+ out["DeletionPolicy" ](syncCfg.handleDeletion);
+ out["CustomDeletionFolder"](syncCfg.customDeletionDirectory);
+}
+
+
+void writeConfig(const FilterConfig& filter, XmlOut& out)
+{
+ out["Include"](filter.includeFilter);
+ out["Exclude"](filter.excludeFilter);
+
+ out["TimeSpan" ](filter.timeSpan);
+ out["UnitTimeSpan"](filter.unitTimeSpan);
+
+ out["SizeMin" ](filter.sizeMin);
+ out["UnitSizeMin"](filter.unitSizeMin);
+
+ out["SizeMax" ](filter.sizeMax);
+ out["UnitSizeMax"](filter.unitSizeMax);
+}
+
+
+void writeConfigFolderPair(const FolderPairEnh& enhPair, XmlOut& out)
+{
+ XmlOut outPair = out.ref().addChild("Pair");
+
+ //read folder pairs
+ outPair["Left" ](enhPair.leftDirectory);
+ outPair["Right"](enhPair.rightDirectory);
+
+ //###########################################################
+ //alternate comp configuration (optional)
+ if (enhPair.altCmpConfig.get())
+ {
+ XmlOut outAlt = outPair["CompareConfig"];
+
+ writeConfig(*enhPair.altCmpConfig, outAlt);
+ }
+ //###########################################################
+ //alternate sync configuration (optional)
+ if (enhPair.altSyncConfig.get())
+ {
+ XmlOut outAltSync = outPair["SyncConfig"];
+
+ writeConfig(*enhPair.altSyncConfig, outAltSync);
+ }
+
+ //###########################################################
+ //alternate filter configuration
+ XmlOut outFilter = outPair["LocalFilter"];
+ writeConfig(enhPair.localFilter, outFilter);
+}
+
+
+void writeConfig(const MainConfiguration& mainCfg, XmlOut& out)
+{
+ XmlOut outCmp = out["MainConfig"]["Comparison"];
+
+ writeConfig(mainCfg.cmpConfig, outCmp);
+ //###########################################################
+
+ XmlOut outSync = out["MainConfig"]["SyncConfig"];
+
+ writeConfig(mainCfg.syncCfg, outSync);
+ //###########################################################
+
+ XmlOut outFilter = out["MainConfig"]["GlobalFilter"];
+ //write filter settings
+ writeConfig(mainCfg.globalFilter, outFilter);
+
+ //###########################################################
+ //write all folder pairs
+
+ XmlOut outFp = out["MainConfig"]["FolderPairs"];
+
+ //write first folder pair
+ writeConfigFolderPair(mainCfg.firstPair, outFp);
+
+ //write additional folder pairs
+ std::for_each(mainCfg.additionalPairs.begin(), mainCfg.additionalPairs.end(),
+ [&](const FolderPairEnh& fp) { writeConfigFolderPair(fp, outFp); });
+}
+
+
+void writeConfig(const XmlGuiConfig& config, XmlOut& out)
+{
+ writeConfig(config.mainCfg, out); //write main config
+
+ //write GUI specific config data
+ XmlOut outGuiCfg = out["GuiConfig"];
+
+ outGuiCfg["HideFiltered" ](config.hideFilteredElements);
+ outGuiCfg["HandleError" ](config.handleError);
+ outGuiCfg["SyncPreviewActive"](config.syncPreviewEnabled);
+}
+
+void writeConfig(const XmlBatchConfig& config, XmlOut& out)
+{
+
+ writeConfig(config.mainCfg, out); //write main config
+
+ //write GUI specific config data
+ XmlOut outBatchCfg = out["BatchConfig"];
+
+ outBatchCfg["Silent" ](config.silent);
+ outBatchCfg["LogfileDirectory"](config.logFileDirectory);
+ outBatchCfg["LogfileCountMax" ](config.logFileCountMax);
+ outBatchCfg["HandleError" ](config.handleError);
+}
+
+
+void writeConfig(const XmlGlobalSettings& config, XmlOut& out)
+{
+ XmlOut outShared = out["Shared"];
+
+ //write program language setting
+ outShared["Language"](config.programLanguage);
+
+ outShared["CopyLockedFiles" ](config.copyLockedFiles);
+ outShared["CopyFilePermissions" ](config.copyFilePermissions);
+ outShared["TransactionalFileCopy"](config.transactionalFileCopy);
+ outShared["VerifyCopiedFiles" ](config.verifyFileCopy);
+
+ //max. allowed file time deviation
+ outShared["FileTimeTolerance"](config.fileTimeTolerance);
+
+ XmlOut outOpt = outShared["ShowOptionalDialogs"];
+ outOpt["CheckForDependentFolders" ](config.optDialogs.warningDependentFolders);
+ outOpt["CheckForMultipleWriteAccess" ](config.optDialogs.warningMultiFolderWriteAccess);
+ outOpt["CheckForSignificantDifference"](config.optDialogs.warningSignificantDifference);
+ outOpt["CheckForFreeDiskSpace"](config.optDialogs.warningNotEnoughDiskSpace);
+ outOpt["CheckForUnresolvedConflicts"](config.optDialogs.warningUnresolvedConflicts);
+ outOpt["NotifyDatabaseError"](config.optDialogs.warningSyncDatabase);
+ outOpt["CheckMissingRecycleBin"](config.optDialogs.warningRecyclerMissing);
+ outOpt["PopupOnConfigChange"](config.optDialogs.popupOnConfigChange);
+ outOpt["SummaryBeforeSync" ](config.optDialogs.showSummaryBeforeSync);
+
+ //gui specific global settings (optional)
+ XmlOut outGui = out["Gui"];
+ XmlOut outWnd = outGui["Windows"]["Main"];
+
+ //write application window size and position
+ outWnd["Width" ](config.gui.dlgSize.x);
+ outWnd["Height" ](config.gui.dlgSize.y);
+ outWnd["PosX" ](config.gui.dlgPos.x);
+ outWnd["PosY" ](config.gui.dlgPos.y);
+ outWnd["Maximized"](config.gui.isMaximized);
+
+ outWnd["MaxFolderPairsVisible"](config.gui.maxFolderPairsVisible);
+
+ outWnd["ManualDeletionOnBothSides"](config.gui.deleteOnBothSides);
+ outWnd["ManualDeletionUseRecycler"](config.gui.useRecyclerForManualDeletion);
+ outWnd["RespectCaseOnSearch" ](config.gui.textSearchRespectCase);
+
+ outWnd["IconSize"](config.gui.iconSize);
+
+ //###########################################################
+
+ //write column attributes
+ XmlOut outColLeft = outWnd["LeftColumns"];
+ outColLeft.attribute("AutoAdjust", config.gui.autoAdjustColumnsLeft);
+
+ outColLeft(config.gui.columnAttribLeft);
+
+ //###########################################################
+ XmlOut outColRight = outWnd["RightColumns"];
+ outColRight.attribute("AutoAdjust", config.gui.autoAdjustColumnsRight);
+
+ outColRight(config.gui.columnAttribRight);
+
+ outWnd["FolderHistoryLeft" ](config.gui.folderHistoryLeft);
+ outWnd["FolderHistoryRight"](config.gui.folderHistoryRight);
+ outWnd["MaximumHistorySize"](config.gui.folderHistMax);
+ outWnd["Perspective" ](config.gui.guiPerspectiveLast);
+
+ //external applications
+ outGui["ExternalApplications"](config.gui.externelApplications);
+
+ //load config file history
+ outGui["LastConfigActive"](config.gui.lastUsedConfigFiles);
+ outGui["ConfigHistory"](config.gui.cfgFileHistory);
+
+ //last update check
+ outGui["LastUpdateCheck"](config.gui.lastUpdateCheck);
+
+ //batch specific global settings
+ //XmlOut outBatch = out["Batch"];
+}
+
+
+template <class ConfigType>
+void writeConfig(const ConfigType& config, XmlType type, const wxString& filename)
+{
+ XmlDoc doc("FreeFileSync");
+ setXmlType(doc, type); //throw()
+
+ XmlOut out(doc);
+ writeConfig(config, out);
+
+ saveXmlDocument(doc, toZ(filename)); //throw FfsXmlError
+}
+}
+
+void xmlAccess::writeConfig(const XmlGuiConfig& config, const wxString& filename)
+{
+ ::writeConfig(config, XML_TYPE_GUI, filename); //throw FfsXmlError
+}
+
+
+void xmlAccess::writeConfig(const XmlBatchConfig& config, const wxString& filename)
+{
+ ::writeConfig(config, XML_TYPE_BATCH, filename); //throw FfsXmlError
+}
+
+
+void xmlAccess::writeConfig(const XmlGlobalSettings& config)
+{
+ ::writeConfig(config, XML_TYPE_GLOBAL, getGlobalConfigFile()); //throw FfsXmlError
+}
+
+
+wxString xmlAccess::extractJobName(const wxString& configFilename)
+{
+ const wxString shortName = configFilename.AfterLast(FILE_NAME_SEPARATOR); //returns the whole string if seperator not found
+ const wxString jobName = shortName.BeforeLast(wxChar('.')); //returns empty string if seperator not found
+ return jobName.IsEmpty() ? shortName : jobName;
+}
bgstack15