diff options
Diffstat (limited to 'library/process_xml.cpp')
-rw-r--r-- | library/process_xml.cpp | 1646 |
1 files changed, 700 insertions, 946 deletions
diff --git a/library/process_xml.cpp b/library/process_xml.cpp index 0e591116..1b5f6360 100644 --- a/library/process_xml.cpp +++ b/library/process_xml.cpp @@ -1,44 +1,30 @@ -// ************************************************************************** +// ************************************************************************** // * 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/zenxml.h> #include "../shared/i18n.h" #include "../shared/global_func.h" #include "../shared/standard_paths.h" #include "../shared/string_conv.h" #include "../shared/file_handling.h" +#include "../shared/file_io.h" +#include "../shared/xml_base.h" using namespace zen; -using namespace xmlAccess; //functionally needed!!! - - -XmlType xmlAccess::getXmlType(const wxString& filename) //throw() -{ - try - { - TiXmlDocument doc; - loadXmlDocument(filename, doc); //throw (XmlError) - return getXmlType(doc); - } - catch (const XmlError&) {} - - return XML_TYPE_OTHER; -} +using namespace xmlAccess; //functionally needed for correct overload resolution!!! -XmlType xmlAccess::getXmlType(const TiXmlDocument& doc) //throw() +XmlType getXmlType(const zen::XmlDoc& doc) //throw() { - const TiXmlElement* root = doc.RootElement(); - if (root && root->ValueStr() == std::string("FreeFileSync")) + if (doc.root().getNameAs<std::string>() == "FreeFileSync") { - const char* cfgType = root->Attribute("XmlType"); - if (cfgType) + std::string type; + if (doc.root().getAttribute("XmlType", type)) { - const std::string type(cfgType); - if (type == "GUI") return XML_TYPE_GUI; else if (type == "BATCH") @@ -51,1288 +37,1056 @@ XmlType xmlAccess::getXmlType(const TiXmlDocument& doc) //throw() } -void xmlAccess::initXmlDocument(TiXmlDocument& doc, XmlType type) //throw() +XmlType xmlAccess::getXmlType(const wxString& filename) //throw() { - TiXmlDeclaration* decl = new TiXmlDeclaration("1.0", "UTF-8", ""); //delete won't be necessary later; ownership passed to TiXmlDocument! - doc.LinkEndChild(decl); + XmlDoc doc; + try + { + std::string stream = loadStream(filename); //throw XmlFileError + parse(stream, doc); //throw XmlParsingError + } + catch (const zen::XmlError&) //catch XmlFileError, XmlParsingError + { + return XML_TYPE_OTHER; + } + return ::getXmlType(doc); +} - TiXmlElement* root = new TiXmlElement("FreeFileSync"); - doc.LinkEndChild(root); +void setXmlType(XmlDoc& doc, XmlType type) //throw() +{ switch (type) { case XML_TYPE_GUI: - addXmlAttribute("XmlType", "GUI", doc.RootElement()); + doc.root().setAttribute("XmlType", "GUI"); break; case XML_TYPE_BATCH: - addXmlAttribute("XmlType", "BATCH", doc.RootElement()); + doc.root().setAttribute("XmlType", "BATCH"); break; case XML_TYPE_GLOBAL: - addXmlAttribute("XmlType", "GLOBAL", doc.RootElement()); + doc.root().setAttribute("XmlType", "GLOBAL"); break; case XML_TYPE_OTHER: + assert(false); break; } } +//################################################################################################################ -class FfsXmlErrorLogger : public xmlAccess::XmlErrorLogger -{ -public: - //read gui settings, all values retrieved are optional, so check for initial values! (== -1) - void readConfig(const TiXmlElement* root, xmlAccess::XmlGuiConfig& outputCfg); - //read batch settings, all values retrieved are optional - void readConfig(const TiXmlElement* root, xmlAccess::XmlBatchConfig& outputCfg); - //read global settings, valid for both GUI and batch mode, independent from configuration - void readConfig(const TiXmlElement* root, xmlAccess::XmlGlobalSettings& outputCfg); - -private: - //read alternate configuration (optional) => might point to NULL - void readConfig(const TiXmlElement& folderPair, FolderPairEnh& enhPair); - void readFilter(const TiXmlElement& parent, FilterConfig& output); - - //read basic FreefileSync settings (used by commandline and GUI) - void readConfig(const TiXmlElement* root, MainConfiguration& mainCfg); -}; - - - -bool readXmlElement(const std::string& name, const TiXmlElement* parent, CompareVariant& output) +wxString xmlAccess::getGlobalConfigFile() { - std::string dummy; - if (xmlAccess::readXmlElement(name, parent, dummy)) - { - if (dummy == "ByTimeAndSize") - output = zen::CMP_BY_TIME_SIZE; - else if (dummy == "ByContent") - output = zen::CMP_BY_CONTENT; - else - return false; - - return true; - } - else - return false; + return zen::getConfigDir() + wxT("GlobalSettings.xml"); } -bool readXmlElement(const std::string& name, const TiXmlElement* parent, SyncDirection& output) +void xmlAccess::OptionalDialogs::resetDialogs() { - std::string dummy; - if (xmlAccess::readXmlElement(name, parent, dummy)) - { - if (dummy == "left") - output = SYNC_DIR_LEFT; - else if (dummy == "right") - output = SYNC_DIR_RIGHT; - else //treat all other input as "none" - output = SYNC_DIR_NONE; - - return true; - } - else - return false; + warningDependentFolders = true; + warningMultiFolderWriteAccess = true; + warningSignificantDifference = true; + warningNotEnoughDiskSpace = true; + warningUnresolvedConflicts = true; + warningSyncDatabase = true; + popupOnConfigChange = true; + showSummaryBeforeSync = true; } -bool readXmlElement(const std::string& name, const TiXmlElement* parent, xmlAccess::OnError& output) +xmlAccess::XmlGuiConfig xmlAccess::convertBatchToGui(const xmlAccess::XmlBatchConfig& batchCfg) { - std::string dummy; - if (xmlAccess::readXmlElement(name, parent, dummy)) - { - if (dummy == "Ignore") - output = xmlAccess::ON_ERROR_IGNORE; - else if (dummy == "Exit") - output = xmlAccess::ON_ERROR_EXIT; - else //treat all other input as popup - output = xmlAccess::ON_ERROR_POPUP; - - return true; - } - else - return false; + XmlGuiConfig output; + output.mainCfg = batchCfg.mainCfg; + return output; } -bool readXmlElement(const std::string& name, const TiXmlElement* parent , OnGuiError& output) +xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg) { - std::string dummy; - if (xmlAccess::readXmlElement(name, parent, dummy)) - { - if (dummy == "Popup") - output = ON_GUIERROR_POPUP; - else if (dummy == "Ignore") - output = ON_GUIERROR_IGNORE; - else - return false; - - return true; - } - return false; -} - + XmlBatchConfig output; + output.mainCfg = guiCfg.mainCfg; -bool readXmlElement(const std::string& name, const TiXmlElement* parent , zen::DeletionPolicy& output) -{ - std::string dummy; - if (xmlAccess::readXmlElement(name, parent, dummy)) + switch (guiCfg.handleError) { - if (dummy == "DeletePermanently") - output = zen::DELETE_PERMANENTLY; - else if (dummy == "MoveToRecycleBin") - output = zen::MOVE_TO_RECYCLE_BIN; - else if (dummy == "MoveToCustomDirectory") - output = zen::MOVE_TO_CUSTOM_DIRECTORY; - else - return false; - - return true; + case ON_GUIERROR_POPUP: + output.handleError = xmlAccess::ON_ERROR_POPUP; + break; + case ON_GUIERROR_IGNORE: + output.handleError = xmlAccess::ON_ERROR_IGNORE; + break; } - return false; -} - - -bool readXmlElement(const std::string& name, const TiXmlElement* parent , zen::SymLinkHandling& output) -{ - std::string dummy; - if (xmlAccess::readXmlElement(name, parent, dummy)) - { - if (dummy == "Ignore") - output = zen::SYMLINK_IGNORE; - else if (dummy == "UseDirectly") - output = zen::SYMLINK_USE_DIRECTLY; - else if (dummy == "FollowLink") - output = zen::SYMLINK_FOLLOW_LINK; - else - return false; - return true; - } - return false; + return output; } -bool readXmlElement(const std::string& name, const TiXmlElement* parent , zen::UnitTime& output) +xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<wxString>& filenames) //throw () { - std::string dummy; - if (xmlAccess::readXmlElement(name, parent, dummy)) - { - if (dummy == "Inactive") - output = zen::UTIME_NONE; - else if (dummy == "Second") - output = zen::UTIME_SEC; - else if (dummy == "Minute") - output = zen::UTIME_MIN; - else if (dummy == "Hour") - output = zen::UTIME_HOUR; - else if (dummy == "Day") - output = zen::UTIME_DAY; - else - return false; - - return true; - } - return false; -} - + bool guiCfgExists = false; + bool batchCfgExists = false; -bool readXmlElement(const std::string& name, const TiXmlElement* parent , zen::UnitSize& output) -{ - std::string dummy; - if (xmlAccess::readXmlElement(name, parent, dummy)) + for (std::vector<wxString>::const_iterator i = filenames.begin(); i != filenames.end(); ++i) { - if (dummy == "Inactive") - output = zen::USIZE_NONE; - else if (dummy == "Byte") - output = zen::USIZE_BYTE; - else if (dummy == "KB") - output = zen::USIZE_KB; - else if (dummy == "MB") - output = zen::USIZE_MB; - else - return false; - - return true; - } - return false; -} + switch (xmlAccess::getXmlType(*i)) //throw() + { + case XML_TYPE_GUI: + guiCfgExists = true; + break; + case XML_TYPE_BATCH: + batchCfgExists = true; + break; -bool readXmlElement(const std::string& name, const TiXmlElement* parent , zen::SyncConfig::Variant& output) -{ - std::string dummy; - if (!xmlAccess::readXmlElement(name, parent, dummy)) - return false; + case XML_TYPE_GLOBAL: + case XML_TYPE_OTHER: + return MERGE_OTHER; + } + } - if (dummy == "Automatic") - output = SyncConfig::AUTOMATIC; - else if (dummy == "Mirror") - output = SyncConfig::MIRROR; - else if (dummy == "Update") - output = SyncConfig::UPDATE; - else if (dummy == "Custom") - output = SyncConfig::CUSTOM; + if (guiCfgExists && batchCfgExists) + return MERGE_GUI_BATCH; + else if (guiCfgExists && !batchCfgExists) + return MERGE_GUI; + else if (!guiCfgExists && batchCfgExists) + return MERGE_BATCH; else - return false; - - return true; + return MERGE_OTHER; } -bool readXmlElement(const std::string& name, const TiXmlElement* parent, Zstring& output) +namespace { - wxString dummy; - if (!xmlAccess::readXmlElement(name, parent, dummy)) - return false; - - output = wxToZ(dummy); - return true; -} - - -bool readXmlAttribute(const std::string& name, const TiXmlElement* node, xmlAccess::ColumnTypes& output) +template <class XmlCfg> +XmlCfg loadCfgImpl(const wxString& filename, std::unique_ptr<xmlAccess::FfsXmlError>& exeption) //throw (xmlAccess::FfsXmlError) { - int dummy = 0; - if (xmlAccess::readXmlAttribute(name, node, dummy)) + XmlCfg cfg; + try { - output = static_cast<xmlAccess::ColumnTypes>(dummy); - return true; + xmlAccess::readConfig(filename, cfg); //throw (xmlAccess::FfsXmlError); } - else - return false; -} - - -void FfsXmlErrorLogger::readFilter(const TiXmlElement& parent, FilterConfig& output) -{ - //read filter settings - readXmlElementLogging("Include", &parent, output.includeFilter); - readXmlElementLogging("Exclude", &parent, output.excludeFilter); - - //migration "strategy": no error checking on these... :P - - readXmlElementLogging("TimeSpan", &parent, output.timeSpan); - readXmlElementLogging("UnitTimeSpan", &parent, output.unitTimeSpan); - - readXmlElementLogging("SizeMin", &parent, output.sizeMin); - readXmlElementLogging("UnitSizeMin", &parent, output.unitSizeMin); - - readXmlElementLogging("SizeMax", &parent, output.sizeMax); - readXmlElementLogging("UnitSizeMax", &parent, output.unitSizeMax); -} - - -//################################################################################################################ -void FfsXmlErrorLogger::readConfig(const TiXmlElement& folderPair, FolderPairEnh& enhPair) -{ - //read folder pairs - readXmlElementLogging("Left", &folderPair, enhPair.leftDirectory); - readXmlElementLogging("Right", &folderPair, enhPair.rightDirectory); - - - //########################################################### - //alternate sync configuration - const TiXmlElement* xmlAltSyncCfg = TiXmlHandleConst(&folderPair).FirstChild("AlternateSyncConfig").ToElement(); - if (xmlAltSyncCfg) + catch (const xmlAccess::FfsXmlError& e) { - AlternateSyncConfig* altSyncCfg = new AlternateSyncConfig; - enhPair.altSyncConfig.reset(altSyncCfg); - - const TiXmlElement* xmlSyncDirections = TiXmlHandleConst(xmlAltSyncCfg).FirstChild("CustomDirections").ToElement(); - - //read sync configuration - readXmlElementLogging("Variant", xmlAltSyncCfg, altSyncCfg->syncConfiguration.var); - - readXmlElementLogging("LeftOnly", xmlSyncDirections, altSyncCfg->syncConfiguration.custom.exLeftSideOnly); - readXmlElementLogging("RightOnly", xmlSyncDirections, altSyncCfg->syncConfiguration.custom.exRightSideOnly); - readXmlElementLogging("LeftNewer", xmlSyncDirections, altSyncCfg->syncConfiguration.custom.leftNewer); - readXmlElementLogging("RightNewer", xmlSyncDirections, altSyncCfg->syncConfiguration.custom.rightNewer); - readXmlElementLogging("Different", xmlSyncDirections, altSyncCfg->syncConfiguration.custom.different); - readXmlElementLogging("Conflict", xmlSyncDirections, altSyncCfg->syncConfiguration.custom.conflict); - - readXmlElementLogging("DeletionPolicy", xmlAltSyncCfg, altSyncCfg->handleDeletion); - readXmlElementLogging("CustomDeletionFolder", xmlAltSyncCfg, altSyncCfg->customDeletionDirectory); + if (e.getSeverity() == xmlAccess::FfsXmlError::FATAL) + throw; + else + exeption.reset(new xmlAccess::FfsXmlError(e)); } - - //########################################################### - //alternate filter configuration - const TiXmlElement* filterCfg = TiXmlHandleConst(&folderPair).FirstChild("LocalFilter").ToElement(); - if (filterCfg) - readFilter(*filterCfg, enhPair.localFilter); + return cfg; } -void FfsXmlErrorLogger::readConfig(const TiXmlElement* root, MainConfiguration& mainCfg) +template <class XmlCfg> +void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config) //throw (xmlAccess::FfsXmlError) { - TiXmlHandleConst hRoot(root); //custom const handle: TiXml API seems broken in this regard - - //########################################################### - const TiXmlElement* xmlMainConfig = hRoot.FirstChild("MainConfig").ToElement(); - - const TiXmlElement* cmpSettings = hRoot.FirstChild("MainConfig").FirstChild("Comparison").ToElement(); - - //read compare variant - readXmlElementLogging("Variant", cmpSettings, mainCfg.compareVar); - - //include symbolic links at all? - readXmlElementLogging("HandleSymlinks", cmpSettings, mainCfg.handleSymlinks); - - //########################################################### - const TiXmlElement* xmlSyncCfg = hRoot.FirstChild("MainConfig").FirstChild("SyncConfig").ToElement(); - const TiXmlElement* syncDirections = TiXmlHandleConst(xmlSyncCfg).FirstChild("CustomDirections").ToElement(); - - //read sync configuration - readXmlElementLogging("Variant", xmlSyncCfg, mainCfg.syncConfiguration.var); - - readXmlElementLogging("LeftOnly", syncDirections, mainCfg.syncConfiguration.custom.exLeftSideOnly); - readXmlElementLogging("RightOnly", syncDirections, mainCfg.syncConfiguration.custom.exRightSideOnly); - readXmlElementLogging("LeftNewer", syncDirections, mainCfg.syncConfiguration.custom.leftNewer); - readXmlElementLogging("RightNewer", syncDirections, mainCfg.syncConfiguration.custom.rightNewer); - readXmlElementLogging("Different", syncDirections, mainCfg.syncConfiguration.custom.different); - readXmlElementLogging("Conflict", syncDirections, mainCfg.syncConfiguration.custom.conflict); - - //########################################################### - //misc - readXmlElementLogging("DeletionPolicy", xmlSyncCfg, mainCfg.handleDeletion); - readXmlElementLogging("CustomDeletionFolder", xmlSyncCfg, mainCfg.customDeletionDirectory); - //########################################################### - const TiXmlElement* filter = TiXmlHandleConst(xmlMainConfig).FirstChild("GlobalFilter").ToElement(); + using namespace xmlAccess; - //read filter settings - if (filter) - readFilter(*filter, mainCfg.globalFilter); - else - logError("GlobalFilter"); + assert(!filenames.empty()); + if (filenames.empty()) + return; - //########################################################### - const TiXmlElement* pairs = hRoot.FirstChild("MainConfig").FirstChild("FolderPairs").FirstChild("Pair").ToElement(); + std::vector<zen::MainConfiguration> mainCfgs; + std::unique_ptr<FfsXmlError> savedException; - //read all folder pairs - mainCfg.additionalPairs.clear(); - bool firstLoop = true; - while (pairs) + for (std::vector<wxString>::const_iterator i = filenames.begin(); i != filenames.end(); ++i) { - FolderPairEnh newPair; - readConfig(*pairs, newPair); - - if (firstLoop) //read first folder pair + switch (getXmlType(*i)) { - firstLoop = false; - mainCfg.firstPair = newPair; - } - else //read additional folder pairs - mainCfg.additionalPairs.push_back(newPair); + case XML_TYPE_GUI: + mainCfgs.push_back(loadCfgImpl<XmlGuiConfig>(*i, savedException).mainCfg); //throw (xmlAccess::FfsXmlError) + break; + + case XML_TYPE_BATCH: + mainCfgs.push_back(loadCfgImpl<XmlBatchConfig>(*i, savedException).mainCfg); //throw (xmlAccess::FfsXmlError) + break; - pairs = pairs->NextSiblingElement(); + case XML_TYPE_GLOBAL: + case XML_TYPE_OTHER: + break; + } } -} + if (mainCfgs.empty()) + throw FfsXmlError(_("Invalid FreeFileSync config file!")); -void FfsXmlErrorLogger::readConfig(const TiXmlElement* root, xmlAccess::XmlGuiConfig& outputCfg) -{ - //read main config - readConfig(root, outputCfg.mainCfg); + try //...to init all non-"mainCfg" settings with first config file + { + xmlAccess::readConfig(filenames[0], config); //throw (xmlAccess::FfsXmlError); + } + catch (...) {} - //read GUI specific config data - const TiXmlElement* guiConfig = TiXmlHandleConst(root).FirstChild("GuiConfig").ToElement(); + config.mainCfg = merge(mainCfgs); - readXmlElementLogging("HideFiltered", guiConfig, outputCfg.hideFilteredElements); - readXmlElementLogging("HandleError", guiConfig, outputCfg.handleError); - readXmlElementLogging("SyncPreviewActive", guiConfig, outputCfg.syncPreviewEnabled); + if (savedException.get()) //"re-throw" exception + throw* savedException; +} } -void FfsXmlErrorLogger::readConfig(const TiXmlElement* root, xmlAccess::XmlBatchConfig& outputCfg) +void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlGuiConfig& config) //throw (xmlAccess::FfsXmlError) { - //read main config - readConfig(root, outputCfg.mainCfg); - - //read batch specific config - const TiXmlElement* batchConfig = TiXmlHandleConst(root).FirstChild("BatchConfig").ToElement(); - - readXmlElementLogging("Silent", batchConfig, outputCfg.silent); - readXmlElementLogging("LogfileDirectory", batchConfig, outputCfg.logFileDirectory); - readXmlElementLogging("LogfileCountMax", batchConfig, outputCfg.logFileCountMax); - readXmlElementLogging("HandleError", batchConfig, outputCfg.handleError); + mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::FfsXmlError) } -void FfsXmlErrorLogger::readConfig(const TiXmlElement* root, xmlAccess::XmlGlobalSettings& outputCfg) +void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlBatchConfig& config) //throw (xmlAccess::FfsXmlError); { - //read global settings - const TiXmlElement* global = TiXmlHandleConst(root).FirstChild("Shared").ToElement(); - - //try to read program language setting - readXmlElementLogging("Language", global, outputCfg.programLanguage); - - //copy locked files using VSS - readXmlElementLogging("CopyLockedFiles", global, outputCfg.copyLockedFiles); - - //file permissions - readXmlElementLogging("CopyFilePermissions", global, outputCfg.copyFilePermissions); - - //verify file copying - readXmlElementLogging("VerifyCopiedFiles", global, outputCfg.verifyFileCopy); - - //max. allowed file time deviation - readXmlElementLogging("FileTimeTolerance", global, outputCfg.fileTimeTolerance); - - - const TiXmlElement* optionalDialogs = TiXmlHandleConst(root).FirstChild("Shared").FirstChild("ShowOptionalDialogs").ToElement(); - - //folder dependency check - readXmlElementLogging("CheckForDependentFolders", optionalDialogs, outputCfg.optDialogs.warningDependentFolders); - - //check for multi write access for directory as part of multiple pairs - readXmlElementLogging("CheckForMultipleWriteAccess", optionalDialogs, outputCfg.optDialogs.warningMultiFolderWriteAccess); - - //significant difference check - readXmlElementLogging("CheckForSignificantDifference", optionalDialogs, outputCfg.optDialogs.warningSignificantDifference); - //check free disk space - readXmlElementLogging("CheckForFreeDiskSpace", optionalDialogs, outputCfg.optDialogs.warningNotEnoughDiskSpace); - //check for unresolved conflicts - readXmlElementLogging("CheckForUnresolvedConflicts", optionalDialogs, outputCfg.optDialogs.warningUnresolvedConflicts); - - readXmlElementLogging("NotifyDatabaseError", optionalDialogs, outputCfg.optDialogs.warningSyncDatabase); - - readXmlElementLogging("PopupOnConfigChange", optionalDialogs, outputCfg.optDialogs.popupOnConfigChange); - - readXmlElementLogging("SummaryBeforeSync", optionalDialogs, outputCfg.optDialogs.showSummaryBeforeSync); - - - //gui specific global settings (optional) - const TiXmlElement* gui = TiXmlHandleConst(root).FirstChild("Gui").ToElement(); - const TiXmlElement* mainWindow = TiXmlHandleConst(gui).FirstChild("Windows").FirstChild("Main").ToElement(); - - //read application window size and position - readXmlElementLogging("Width", mainWindow, outputCfg.gui.widthNotMaximized); - readXmlElementLogging("Height", mainWindow, outputCfg.gui.heightNotMaximized); - readXmlElementLogging("PosX", mainWindow, outputCfg.gui.posXNotMaximized); - readXmlElementLogging("PosY", mainWindow, outputCfg.gui.posYNotMaximized); - readXmlElementLogging("Maximized", mainWindow, outputCfg.gui.isMaximized); - - readXmlElementLogging("ManualDeletionOnBothSides", mainWindow, outputCfg.gui.deleteOnBothSides); - readXmlElementLogging("ManualDeletionUseRecycler", mainWindow, outputCfg.gui.useRecyclerForManualDeletion); - - readXmlElementLogging("RespectCaseOnSearch", mainWindow, outputCfg.gui.textSearchRespectCase); - - size_t folderPairMax = 0; - readXmlElementLogging("FolderPairsMax", mainWindow, folderPairMax); - if (folderPairMax != 0) //if reading fails, leave at default - outputCfg.gui.addFolderPairCountMax = std::max(static_cast<size_t>(2), folderPairMax) - 1; //map folderPairMax to additionalFolderPairMax - - //########################################################### - //read column attributes - readXmlAttributeLogging("AutoAdjust", TiXmlHandleConst(mainWindow).FirstChild("LeftColumns").ToElement(), outputCfg.gui.autoAdjustColumnsLeft); - readXmlAttributeLogging("ShowFileIcons", TiXmlHandleConst(mainWindow).FirstChild("LeftColumns").ToElement(), outputCfg.gui.showFileIconsLeft); - - const TiXmlElement* leftColumn = TiXmlHandleConst(mainWindow).FirstChild("LeftColumns").FirstChild("Column").ToElement(); - size_t colPos = 0; - while (leftColumn) - { - ColumnAttrib newAttrib; - newAttrib.position = colPos++; - readXmlAttributeLogging("Type", leftColumn, newAttrib.type); - readXmlAttributeLogging("Visible", leftColumn, newAttrib.visible); - readXmlAttributeLogging("Width", leftColumn, newAttrib.width); - outputCfg.gui.columnAttribLeft.push_back(newAttrib); - - leftColumn = leftColumn->NextSiblingElement(); - } - - readXmlAttributeLogging("AutoAdjust", TiXmlHandleConst(mainWindow).FirstChild("RightColumns").ToElement(), outputCfg.gui.autoAdjustColumnsRight); - readXmlAttributeLogging("ShowFileIcons", TiXmlHandleConst(mainWindow).FirstChild("RightColumns").ToElement(), outputCfg.gui.showFileIconsRight); - - const TiXmlElement* rightColumn = TiXmlHandleConst(mainWindow).FirstChild("RightColumns").FirstChild("Column").ToElement(); - colPos = 0; - while (rightColumn) - { - ColumnAttrib newAttrib; - newAttrib.position = colPos++; - readXmlAttributeLogging("Type", rightColumn, newAttrib.type); - readXmlAttributeLogging("Visible", rightColumn, newAttrib.visible); - readXmlAttributeLogging("Width", rightColumn, newAttrib.width); - outputCfg.gui.columnAttribRight.push_back(newAttrib); - - rightColumn = rightColumn->NextSiblingElement(); - } - - readXmlElementLogging("FolderHistoryLeft", mainWindow, outputCfg.gui.folderHistoryLeft); - readXmlElementLogging("FolderHistoryRight", mainWindow, outputCfg.gui.folderHistoryRight); - readXmlElementLogging("MaximumHistorySize", mainWindow, outputCfg.gui.folderHistMax); - - readXmlElementLogging("Perspective", mainWindow, outputCfg.gui.guiPerspectiveLast); - - //external applications - const TiXmlElement* extApps = TiXmlHandleConst(gui).FirstChild("ExternalApplications").FirstChild("Commandline").ToElement(); - if (extApps) - { - outputCfg.gui.externelApplications.clear(); - while (extApps) - { - wxString description; - wxString cmdLine; - - readXmlAttributeLogging("Description", extApps, description); - const char* text = extApps->GetText(); - if (text) cmdLine = wxString::FromUTF8(text); //read commandline - outputCfg.gui.externelApplications.push_back(std::make_pair(description, cmdLine)); - - extApps = extApps->NextSiblingElement(); - } - } - //load config file history - const TiXmlElement* cfgHistory = TiXmlHandleConst(gui).FirstChild("ConfigHistory").ToElement(); - - //load config history elements - readXmlAttributeLogging("LastUsed", cfgHistory, outputCfg.gui.lastUsedConfigFile); - readXmlElementLogging("File", cfgHistory, outputCfg.gui.cfgFileHistory); - - //last update check - readXmlElementLogging("LastUpdateCheck", gui, outputCfg.gui.lastUpdateCheck); - - - //batch specific global settings - //const TiXmlElement* batch = TiXmlHandleConst(root).FirstChild("Batch").ToElement(); + mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::FfsXmlError) } -void addXmlElement(const std::string& name, const CompareVariant variant, TiXmlElement* parent) +namespace zen +{ +template <> inline +void writeText(const CompareVariant& value, std::string& output) { - switch (variant) + switch (value) { case zen::CMP_BY_TIME_SIZE: - xmlAccess::addXmlElement(name, std::string("ByTimeAndSize"), parent); + output = "ByTimeAndSize"; break; case zen::CMP_BY_CONTENT: - xmlAccess::addXmlElement(name, std::string("ByContent"), parent); + 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; +} -void addXmlElement(const std::string& name, const SyncDirection value, TiXmlElement* parent) + +template <> inline +void writeText(const SyncDirection& value, std::string& output) { switch (value) { case SYNC_DIR_LEFT: - xmlAccess::addXmlElement(name, std::string("left"), parent); + output = "left"; break; case SYNC_DIR_RIGHT: - xmlAccess::addXmlElement(name, std::string("right"), parent); + output = "right"; break; case SYNC_DIR_NONE: - xmlAccess::addXmlElement(name, std::string("none"), parent); + 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; +} + -void addXmlElement(const std::string& name, const xmlAccess::OnError value, TiXmlElement* parent) +template <> inline +void writeText(const OnError& value, std::string& output) { switch (value) { - case xmlAccess::ON_ERROR_IGNORE: - xmlAccess::addXmlElement(name, std::string("Ignore"), parent); + case ON_ERROR_IGNORE: + output = "Ignore"; break; - case xmlAccess::ON_ERROR_EXIT: - xmlAccess::addXmlElement(name, std::string("Exit"), parent); + case ON_ERROR_EXIT: + output = "Exit"; break; - case xmlAccess::ON_ERROR_POPUP: - xmlAccess::addXmlElement(name, std::string("Popup"), parent); + 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; +} + -void addXmlElement(const std::string& name, const OnGuiError value, TiXmlElement* parent) +template <> inline +void writeText(const OnGuiError& value, std::string& output) { switch (value) { case ON_GUIERROR_IGNORE: - xmlAccess::addXmlElement(name, std::string("Ignore"), parent); + output = "Ignore"; break; case ON_GUIERROR_POPUP: - xmlAccess::addXmlElement(name, std::string("Popup"), parent); + 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; +} -void addXmlElement(const std::string& name, const zen::DeletionPolicy value, TiXmlElement* parent) + +template <> inline +void writeText(const DeletionPolicy& value, std::string& output) { switch (value) { - case zen::DELETE_PERMANENTLY: - xmlAccess::addXmlElement(name, std::string("DeletePermanently"), parent); + case DELETE_PERMANENTLY: + output = "DeletePermanently"; break; - case zen::MOVE_TO_RECYCLE_BIN: - xmlAccess::addXmlElement(name, std::string("MoveToRecycleBin"), parent); + case MOVE_TO_RECYCLE_BIN: + output = "MoveToRecycleBin"; break; - case zen::MOVE_TO_CUSTOM_DIRECTORY: - xmlAccess::addXmlElement(name, std::string("MoveToCustomDirectory"), parent); + 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; +} + -void addXmlElement(const std::string& name, const zen::SymLinkHandling value, TiXmlElement* parent) +template <> inline +void writeText(const SymLinkHandling& value, std::string& output) { switch (value) { - case zen::SYMLINK_IGNORE: - xmlAccess::addXmlElement(name, std::string("Ignore"), parent); + case SYMLINK_IGNORE: + output = "Ignore"; break; - case zen::SYMLINK_USE_DIRECTLY: - xmlAccess::addXmlElement(name, std::string("UseDirectly"), parent); + case SYMLINK_USE_DIRECTLY: + output = "UseDirectly"; break; - case zen::SYMLINK_FOLLOW_LINK: - xmlAccess::addXmlElement(name, std::string("FollowLink"), parent); + 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; +} + -void addXmlElement(const std::string& name, const zen::UnitTime value, TiXmlElement* parent) +template <> inline +void writeText(const UnitTime& value, std::string& output) { switch (value) { - case zen::UTIME_NONE: - xmlAccess::addXmlElement(name, std::string("Inactive"), parent); + case UTIME_NONE: + output = "Inactive"; break; - case zen::UTIME_SEC: - xmlAccess::addXmlElement(name, std::string("Second"), parent); + case UTIME_SEC: + output = "Second"; break; - case zen::UTIME_MIN: - xmlAccess::addXmlElement(name, std::string("Minute"), parent); + case UTIME_MIN: + output = "Minute"; break; - case zen::UTIME_HOUR: - xmlAccess::addXmlElement(name, std::string("Hour"), parent); + case UTIME_HOUR: + output = "Hour"; break; - case zen::UTIME_DAY: - xmlAccess::addXmlElement(name, std::string("Day"), parent); + case UTIME_DAY: + output = "Day"; 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 == "Second") + value = UTIME_SEC; + else if (tmp == "Minute") + value = UTIME_MIN; + else if (tmp == "Hour") + value = UTIME_HOUR; + else if (tmp == "Day") + value = UTIME_DAY; + else + return false; + return true; +} + + +template <> inline +void writeText(const ColumnTypes& value, std::string& output) +{ + output = toString<std::string>(value); +} -void addXmlElement(const std::string& name, zen::UnitSize value, TiXmlElement* parent) +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 zen::USIZE_NONE: - xmlAccess::addXmlElement(name, std::string("Inactive"), parent); + case USIZE_NONE: + output = "Inactive"; break; - case zen::USIZE_BYTE: - xmlAccess::addXmlElement(name, std::string("Byte"), parent); + case USIZE_BYTE: + output = "Byte"; break; - case zen::USIZE_KB: - xmlAccess::addXmlElement(name, std::string("KB"), parent); + case USIZE_KB: + output = "KB"; break; - case zen::USIZE_MB: - xmlAccess::addXmlElement(name, std::string("MB"), parent); + 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; +} + -void addXmlElement(const std::string& name, SyncConfig::Variant value, TiXmlElement* parent) +template <> inline +void writeText(const SyncConfig::Variant& value, std::string& output) { switch (value) { case SyncConfig::AUTOMATIC: - xmlAccess::addXmlElement(name, std::string("Automatic"), parent); + output = "Automatic"; break; case SyncConfig::MIRROR: - xmlAccess::addXmlElement(name, std::string("Mirror"), parent); + output = "Mirror"; break; case SyncConfig::UPDATE: - xmlAccess::addXmlElement(name, std::string("Update"), parent); + output = "Update"; break; case SyncConfig::CUSTOM: - xmlAccess::addXmlElement(name, std::string("Custom"), parent); + output = "Custom"; break; } } - -void addXmlElement(const std::string& name, const Zstring& value, TiXmlElement* parent) +template <> inline +bool readText(const std::string& input, SyncConfig::Variant& value) { - xmlAccess::addXmlElement(name, wxString(zToWx(value)), parent); + std::string tmp = input; + zen::trim(tmp); + if (tmp == "Automatic") + value = SyncConfig::AUTOMATIC; + else if (tmp == "Mirror") + value = SyncConfig::MIRROR; + else if (tmp == "Update") + value = SyncConfig::UPDATE; + else if (tmp == "Custom") + value = SyncConfig::CUSTOM; + else + return false; + return true; } -void addXmlAttribute(const std::string& name, const xmlAccess::ColumnTypes value, TiXmlElement* node) +template <> inline +bool readValue(const XmlElement& input, ColumnAttrib& value) { - xmlAccess::addXmlAttribute(name, static_cast<int>(value), node); + 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; } - -void writeFilter(const FilterConfig& input, TiXmlElement& parent) +template <> inline +void writeValue(const ColumnAttrib& value, XmlElement& output) { - addXmlElement("Include", input.includeFilter, &parent); - addXmlElement("Exclude", input.excludeFilter, &parent); - - addXmlElement("TimeSpan", input.timeSpan, &parent); - addXmlElement("UnitTimeSpan", input.unitTimeSpan, &parent); + XmlOut out(output); + out.attribute("Type", value.type); + out.attribute("Visible", value.visible); + out.attribute("Width", value.width); +} +} - addXmlElement("SizeMin", input.sizeMin, &parent); - addXmlElement("UnitSizeMin", input.unitSizeMin, &parent); - addXmlElement("SizeMax", input.sizeMax, &parent); - addXmlElement("UnitSizeMax", input.unitSizeMax, &parent); +namespace +{ +void readConfig(const XmlIn& in, SyncConfig& syncCfg) +{ + in["Variant"](syncCfg.var); + + XmlIn inCustDir = in["CustomDirections"]; + inCustDir["LeftOnly" ](syncCfg.custom.exLeftSideOnly); + inCustDir["RightOnly" ](syncCfg.custom.exRightSideOnly); + inCustDir["LeftNewer" ](syncCfg.custom.leftNewer); + inCustDir["RightNewer"](syncCfg.custom.rightNewer); + inCustDir["Different" ](syncCfg.custom.different); + inCustDir["Conflict" ](syncCfg.custom.conflict); } -void writeXmlLocalConfig(const FolderPairEnh& enhPair, TiXmlElement& parent) +void readConfig(const XmlIn& in, FilterConfig& filter) { - //write folder pairs - TiXmlElement* newfolderPair = new TiXmlElement("Pair"); - parent.LinkEndChild(newfolderPair); + in["Include"](filter.includeFilter); + in["Exclude"](filter.excludeFilter); - addXmlElement("Left", enhPair.leftDirectory, newfolderPair); - addXmlElement("Right", enhPair.rightDirectory, newfolderPair); + in["TimeSpan" ](filter.timeSpan); + in["UnitTimeSpan"](filter.unitTimeSpan); + in["SizeMin" ](filter.sizeMin); + in["UnitSizeMin"](filter.unitSizeMin); - //alternate sync configuration - const AlternateSyncConfig* altSyncConfig = enhPair.altSyncConfig.get(); - if (altSyncConfig) - { - TiXmlElement* xmlAltSyncCfg = new TiXmlElement("AlternateSyncConfig"); - newfolderPair->LinkEndChild(xmlAltSyncCfg); + in["SizeMax" ](filter.sizeMax); + in["UnitSizeMax"](filter.unitSizeMax); +} - //write sync configuration - addXmlElement("Variant", altSyncConfig->syncConfiguration.var, xmlAltSyncCfg); - TiXmlElement* syncDirections = new TiXmlElement("CustomDirections"); - xmlAltSyncCfg->LinkEndChild(syncDirections); +void readConfig(const XmlIn& in, FolderPairEnh& enhPair) +{ + //read folder pairs + in["Left" ](enhPair.leftDirectory); + in["Right"](enhPair.rightDirectory); - addXmlElement("LeftOnly", altSyncConfig->syncConfiguration.custom.exLeftSideOnly, syncDirections); - addXmlElement("RightOnly", altSyncConfig->syncConfiguration.custom.exRightSideOnly, syncDirections); - addXmlElement("LeftNewer", altSyncConfig->syncConfiguration.custom.leftNewer, syncDirections); - addXmlElement("RightNewer", altSyncConfig->syncConfiguration.custom.rightNewer, syncDirections); - addXmlElement("Different", altSyncConfig->syncConfiguration.custom.different, syncDirections); - addXmlElement("Conflict", altSyncConfig->syncConfiguration.custom.conflict, syncDirections); + //########################################################### + //alternate sync configuration (optional) + XmlIn inAlt = in["AlternateSyncConfig"]; + if (inAlt) + { + std::shared_ptr<AlternateSyncConfig> altSyncCfg = std::make_shared<AlternateSyncConfig>(); + enhPair.altSyncConfig = altSyncCfg; + + //read sync configuration + readConfig(inAlt, altSyncCfg->syncConfiguration); - //misc - addXmlElement("DeletionPolicy", altSyncConfig->handleDeletion, xmlAltSyncCfg); - addXmlElement("CustomDeletionFolder", altSyncConfig->customDeletionDirectory, xmlAltSyncCfg); + inAlt["DeletionPolicy" ](altSyncCfg->handleDeletion); + inAlt["CustomDeletionFolder"](altSyncCfg->customDeletionDirectory); } //########################################################### //alternate filter configuration - TiXmlElement* filterCfg = new TiXmlElement("LocalFilter"); - newfolderPair->LinkEndChild(filterCfg); - - //write filter settings - writeFilter(enhPair.localFilter, *filterCfg); + readConfig(in["LocalFilter"], enhPair.localFilter); } -void writeConfig(const MainConfiguration& mainCfg, TiXmlElement& root) +void readConfig(const XmlIn& in, MainConfiguration& mainCfg) { - TiXmlElement* xmlMainCfg = new TiXmlElement("MainConfig"); - root.LinkEndChild(xmlMainCfg); - - //########################################################### - TiXmlElement* cmpSettings = new TiXmlElement("Comparison"); - xmlMainCfg->LinkEndChild(cmpSettings); + XmlIn inCmp = in["MainConfig"]["Comparison"]; - //write compare algorithm - addXmlElement("Variant", mainCfg.compareVar, cmpSettings); + //read compare variant + inCmp["Variant"](mainCfg.compareVar); //include symbolic links at all? - addXmlElement("HandleSymlinks", mainCfg.handleSymlinks, cmpSettings); + inCmp["HandleSymlinks"](mainCfg.handleSymlinks); //########################################################### - TiXmlElement* xmlSyncCfg = new TiXmlElement("SyncConfig"); - xmlMainCfg->LinkEndChild(xmlSyncCfg); - - //write sync configuration - addXmlElement("Variant", mainCfg.syncConfiguration.var, xmlSyncCfg); - - TiXmlElement* syncDirections = new TiXmlElement("CustomDirections"); - xmlSyncCfg->LinkEndChild(syncDirections); - - addXmlElement("LeftOnly", mainCfg.syncConfiguration.custom.exLeftSideOnly, syncDirections); - addXmlElement("RightOnly", mainCfg.syncConfiguration.custom.exRightSideOnly, syncDirections); - addXmlElement("LeftNewer", mainCfg.syncConfiguration.custom.leftNewer, syncDirections); - addXmlElement("RightNewer", mainCfg.syncConfiguration.custom.rightNewer, syncDirections); - addXmlElement("Different", mainCfg.syncConfiguration.custom.different, syncDirections); - addXmlElement("Conflict", mainCfg.syncConfiguration.custom.conflict, syncDirections); + XmlIn inSync = in["MainConfig"]["SyncConfig"]; + //read sync configuration + readConfig(inSync, mainCfg.syncConfiguration); //########################################################### - //write filter settings - TiXmlElement* filter = new TiXmlElement("GlobalFilter"); - xmlMainCfg->LinkEndChild(filter); - writeFilter(mainCfg.globalFilter, *filter); + //misc + inSync["DeletionPolicy" ](mainCfg.handleDeletion); + inSync["CustomDeletionFolder"](mainCfg.customDeletionDirectory); + //########################################################### - //other - addXmlElement("DeletionPolicy", mainCfg.handleDeletion, xmlSyncCfg); - addXmlElement("CustomDeletionFolder", mainCfg.customDeletionDirectory, xmlSyncCfg); + XmlIn inFilter = in["MainConfig"]["GlobalFilter"]; + //read filter settings + readConfig(inFilter, mainCfg.globalFilter); //########################################################### - TiXmlElement* pairs = new TiXmlElement("FolderPairs"); - xmlMainCfg->LinkEndChild(pairs); + //read all folder pairs + mainCfg.additionalPairs.clear(); - //write first folder pair - writeXmlLocalConfig(mainCfg.firstPair, *pairs); + bool firstIter = true; + for (XmlIn inPair = in["MainConfig"]["FolderPairs"]["Pair"]; inPair; inPair.next()) + { + FolderPairEnh newPair; + readConfig(inPair, newPair); - //write additional folder pairs - for (std::vector<FolderPairEnh>::const_iterator i = mainCfg.additionalPairs.begin(); i != mainCfg.additionalPairs.end(); ++i) - writeXmlLocalConfig(*i, *pairs); + if (firstIter) + { + firstIter = false; + mainCfg.firstPair = newPair; //set first folder pair + } + else + mainCfg.additionalPairs.push_back(newPair); //set additional folder pairs + } } -void writeConfig(const xmlAccess::XmlGuiConfig& inputCfg, TiXmlElement& root) +void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config) { - //write main config - writeConfig(inputCfg.mainCfg, root); + ::readConfig(in, config.mainCfg); //read main config - //write GUI specific config - TiXmlElement* guiConfig = new TiXmlElement("GuiConfig"); - root.LinkEndChild(guiConfig); + //read GUI specific config data + XmlIn inGuiCfg = in["GuiConfig"]; - addXmlElement("HideFiltered", inputCfg.hideFilteredElements, guiConfig); - addXmlElement("HandleError", inputCfg.handleError, guiConfig); - addXmlElement("SyncPreviewActive", inputCfg.syncPreviewEnabled, guiConfig); + inGuiCfg["HideFiltered" ](config.hideFilteredElements); + inGuiCfg["HandleError" ](config.handleError); + inGuiCfg["SyncPreviewActive"](config.syncPreviewEnabled); } -void writeConfig(const xmlAccess::XmlBatchConfig& inputCfg, TiXmlElement& root) +void readConfig(const XmlIn& in, xmlAccess::XmlBatchConfig& config) { - //write main config - writeConfig(inputCfg.mainCfg, root); + ::readConfig(in, config.mainCfg); //read main config - //write GUI specific config - TiXmlElement* batchConfig = new TiXmlElement("BatchConfig"); - root.LinkEndChild(batchConfig); + //read GUI specific config data + XmlIn inBatchCfg = in["BatchConfig"]; - addXmlElement("Silent", inputCfg.silent, batchConfig); - addXmlElement("LogfileDirectory", inputCfg.logFileDirectory, batchConfig); - addXmlElement("LogfileCountMax", inputCfg.logFileCountMax, batchConfig); - addXmlElement("HandleError", inputCfg.handleError, batchConfig); + inBatchCfg["Silent" ](config.silent); + inBatchCfg["LogfileDirectory"](config.logFileDirectory); + inBatchCfg["LogfileCountMax" ](config.logFileCountMax); + inBatchCfg["HandleError" ](config.handleError); } -void writeConfig(const xmlAccess::XmlGlobalSettings& inputCfg, TiXmlElement& root) +void readConfig(const XmlIn& in, XmlGlobalSettings& config) { - //write global settings - TiXmlElement* global = new TiXmlElement("Shared"); - root.LinkEndChild(global); + XmlIn inShared = in["Shared"]; - //program language - addXmlElement("Language", inputCfg.programLanguage, global); + //try to read program language setting + inShared["Language"](config.programLanguage); //copy locked files using VSS - addXmlElement("CopyLockedFiles", inputCfg.copyLockedFiles, global); + inShared["CopyLockedFiles"](config.copyLockedFiles); //file permissions - addXmlElement("CopyFilePermissions", inputCfg.copyFilePermissions, global); + inShared["CopyFilePermissions"](config.copyFilePermissions); //verify file copying - addXmlElement("VerifyCopiedFiles", inputCfg.verifyFileCopy, global); + inShared["VerifyCopiedFiles"](config.verifyFileCopy); //max. allowed file time deviation - addXmlElement("FileTimeTolerance", inputCfg.fileTimeTolerance, global); - - - //optional dialogs - TiXmlElement* optionalDialogs = new TiXmlElement("ShowOptionalDialogs"); - global->LinkEndChild(optionalDialogs); - - //warning: dependent folders - addXmlElement("CheckForDependentFolders", inputCfg.optDialogs.warningDependentFolders, optionalDialogs); - - //check for multi write access for directory as part of multiple pairs - addXmlElement("CheckForMultipleWriteAccess", inputCfg.optDialogs.warningMultiFolderWriteAccess, optionalDialogs); - - //significant difference check - addXmlElement("CheckForSignificantDifference", inputCfg.optDialogs.warningSignificantDifference, optionalDialogs); - - //check free disk space - addXmlElement("CheckForFreeDiskSpace", inputCfg.optDialogs.warningNotEnoughDiskSpace, optionalDialogs); - - //check for unresolved conflicts - addXmlElement("CheckForUnresolvedConflicts", inputCfg.optDialogs.warningUnresolvedConflicts, optionalDialogs); - - addXmlElement("NotifyDatabaseError", inputCfg.optDialogs.warningSyncDatabase, optionalDialogs); - - addXmlElement("PopupOnConfigChange", inputCfg.optDialogs.popupOnConfigChange, optionalDialogs); - - addXmlElement("SummaryBeforeSync", inputCfg.optDialogs.showSummaryBeforeSync, optionalDialogs); - - - //################################################################### - //write global gui settings - TiXmlElement* gui = new TiXmlElement("Gui"); - root.LinkEndChild(gui); - - TiXmlElement* windows = new TiXmlElement("Windows"); - gui->LinkEndChild(windows); + 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["PopupOnConfigChange"](config.optDialogs.popupOnConfigChange); + inOpt["SummaryBeforeSync" ](config.optDialogs.showSummaryBeforeSync); - TiXmlElement* mainWindow = new TiXmlElement("Main"); - windows->LinkEndChild(mainWindow); + //gui specific global settings (optional) + XmlIn inGui = in["Gui"]; + XmlIn inWnd = inGui["Windows"]["Main"]; - //window size - addXmlElement("Width", inputCfg.gui.widthNotMaximized, mainWindow); - addXmlElement("Height", inputCfg.gui.heightNotMaximized, mainWindow); + //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); - //window position - addXmlElement("PosX", inputCfg.gui.posXNotMaximized, mainWindow); - addXmlElement("PosY", inputCfg.gui.posYNotMaximized, mainWindow); - addXmlElement("Maximized", inputCfg.gui.isMaximized, mainWindow); + inWnd["ManualDeletionOnBothSides"](config.gui.deleteOnBothSides); + inWnd["ManualDeletionUseRecycler"](config.gui.useRecyclerForManualDeletion); + inWnd["RespectCaseOnSearch" ](config.gui.textSearchRespectCase); - addXmlElement("ManualDeletionOnBothSides", inputCfg.gui.deleteOnBothSides, mainWindow); - addXmlElement("ManualDeletionUseRecycler", inputCfg.gui.useRecyclerForManualDeletion, mainWindow); + //########################################################### - addXmlElement("RespectCaseOnSearch", inputCfg.gui.textSearchRespectCase, mainWindow); + //read column attributes + XmlIn inColLeft = inWnd["LeftColumns"]; - addXmlElement("FolderPairsMax", inputCfg.gui.addFolderPairCountMax + 1 /*add main pair*/, mainWindow); + inColLeft.attribute("AutoAdjust", config.gui.autoAdjustColumnsLeft); + inColLeft.attribute("ShowFileIcons", config.gui.showFileIconsLeft); + inColLeft(config.gui.columnAttribLeft); + for (size_t i = 0; i < config.gui.columnAttribLeft.size(); ++i) + config.gui.columnAttribLeft[i].position = i; - //write column attributes - TiXmlElement* leftColumn = new TiXmlElement("LeftColumns"); - mainWindow->LinkEndChild(leftColumn); - - addXmlAttribute("AutoAdjust", inputCfg.gui.autoAdjustColumnsLeft, leftColumn); - addXmlAttribute("ShowFileIcons", inputCfg.gui.showFileIconsLeft, leftColumn); + //########################################################### + XmlIn inColRight = inWnd["RightColumns"]; - ColumnAttributes columnAtrribLeftCopy = inputCfg.gui.columnAttribLeft; //can't change const vector - sort(columnAtrribLeftCopy.begin(), columnAtrribLeftCopy.end(), xmlAccess::sortByPositionOnly); - for (size_t i = 0; i < columnAtrribLeftCopy.size(); ++i) - { - TiXmlElement* subElement = new TiXmlElement("Column"); - leftColumn->LinkEndChild(subElement); + inColRight.attribute("AutoAdjust", config.gui.autoAdjustColumnsRight); + inColRight.attribute("ShowFileIcons", config.gui.showFileIconsRight); - const ColumnAttrib& colAttrib = columnAtrribLeftCopy[i]; - addXmlAttribute("Type", colAttrib.type, subElement); - addXmlAttribute("Visible", colAttrib.visible, subElement); - addXmlAttribute("Width", colAttrib.width, subElement); - } + inColRight(config.gui.columnAttribRight); + for (size_t i = 0; i < config.gui.columnAttribRight.size(); ++i) + config.gui.columnAttribRight[i].position = i; - TiXmlElement* rightColumn = new TiXmlElement("RightColumns"); - mainWindow->LinkEndChild(rightColumn); + inWnd["FolderHistoryLeft" ](config.gui.folderHistoryLeft); + inWnd["FolderHistoryRight"](config.gui.folderHistoryRight); + inWnd["MaximumHistorySize"](config.gui.folderHistMax); + inWnd["Perspective" ](config.gui.guiPerspectiveLast); - addXmlAttribute("AutoAdjust", inputCfg.gui.autoAdjustColumnsRight, rightColumn); - addXmlAttribute("ShowFileIcons", inputCfg.gui.showFileIconsRight, rightColumn); + //external applications + inGui["ExternalApplications"](config.gui.externelApplications); - ColumnAttributes columnAtrribRightCopy = inputCfg.gui.columnAttribRight; - sort(columnAtrribRightCopy.begin(), columnAtrribRightCopy.end(), xmlAccess::sortByPositionOnly); - for (size_t i = 0; i < columnAtrribRightCopy.size(); ++i) - { - TiXmlElement* subElement = new TiXmlElement("Column"); - rightColumn->LinkEndChild(subElement); + //load config file history + XmlIn inHist = inGui["ConfigHistory"]; - const ColumnAttrib& colAttrib = columnAtrribRightCopy[i]; - addXmlAttribute("Type", colAttrib.type, subElement); - addXmlAttribute("Visible", colAttrib.visible, subElement); - addXmlAttribute("Width", colAttrib.width, subElement); - } + inHist.attribute("LastUsed", config.gui.lastUsedConfigFile); + inHist(config.gui.cfgFileHistory); - addXmlElement("FolderHistoryLeft", inputCfg.gui.folderHistoryLeft , mainWindow); - addXmlElement("FolderHistoryRight", inputCfg.gui.folderHistoryRight, mainWindow); - addXmlElement("MaximumHistorySize", inputCfg.gui.folderHistMax , mainWindow); + //last update check + inGui["LastUpdateCheck"](config.gui.lastUpdateCheck); - addXmlElement("Perspective", inputCfg.gui.guiPerspectiveLast, mainWindow); + //batch specific global settings + //XmlIn inBatch = in["Batch"]; +} - //external applications - TiXmlElement* extApp = new TiXmlElement("ExternalApplications"); - gui->LinkEndChild(extApp); - for (ExternalApps::const_iterator i = inputCfg.gui.externelApplications.begin(); i != inputCfg.gui.externelApplications.end(); ++i) - { - TiXmlElement* newEntry = new TiXmlElement("Commandline"); - extApp->LinkEndChild(newEntry); +template <class ConfigType> +void readConfig(const wxString& filename, XmlType type, ConfigType& config) +{ + if (!fileExists(wxToZ(filename))) + throw FfsXmlError(wxString(_("File does not exist:")) + wxT("\n\"") + filename + wxT("\"")); - addXmlAttribute("Description", i->first, newEntry); - newEntry->LinkEndChild(new TiXmlText(i->second.ToUTF8())); //commandline - } + XmlDoc doc; + loadXmlDocument(filename, doc); //throw (FfsXmlError) - //write config file history - TiXmlElement* cfgHistory = new TiXmlElement("ConfigHistory"); - gui->LinkEndChild(cfgHistory); + if (getXmlType(doc) != type) //throw() + throw FfsXmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"")); - addXmlAttribute("LastUsed", inputCfg.gui.lastUsedConfigFile, cfgHistory); - addXmlElement("File", inputCfg.gui.cfgFileHistory, cfgHistory); + XmlIn in(doc); + ::readConfig(in, config); - //last update check - addXmlElement("LastUpdateCheck", inputCfg.gui.lastUpdateCheck, gui); + if (in.errorsOccured()) + throw FfsXmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"\n\n") + + getErrorMessageFormatted(in), FfsXmlError::WARNING); +} +} - //################################################################### - //write global batch settings - TiXmlElement* batch = new TiXmlElement("Batch"); - root.LinkEndChild(batch); +void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlGuiConfig& config) +{ + ::readConfig(filename, XML_TYPE_GUI, config); } -wxString xmlAccess::getGlobalConfigFile() +void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlBatchConfig& config) { - return zen::getConfigDir() + wxT("GlobalSettings.xml"); + ::readConfig(filename, XML_TYPE_BATCH, config); } -void xmlAccess::OptionalDialogs::resetDialogs() +void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& config) { - warningDependentFolders = true; - warningMultiFolderWriteAccess = true; - warningSignificantDifference = true; - warningNotEnoughDiskSpace = true; - warningUnresolvedConflicts = true; - warningSyncDatabase = true; - popupOnConfigChange = true; - showSummaryBeforeSync = true; + ::readConfig(getGlobalConfigFile(), XML_TYPE_GLOBAL, config); } -xmlAccess::XmlGuiConfig xmlAccess::convertBatchToGui(const xmlAccess::XmlBatchConfig& batchCfg) +//################################################################################################ +namespace { - XmlGuiConfig output; - output.mainCfg = batchCfg.mainCfg; - return output; +void writeConfig(const SyncConfig& syncCfg, XmlOut& out) +{ + out["Variant"](syncCfg.var); + + XmlOut outCustDir = out["CustomDirections"]; + outCustDir["LeftOnly" ](syncCfg.custom.exLeftSideOnly); + outCustDir["RightOnly" ](syncCfg.custom.exRightSideOnly); + outCustDir["LeftNewer" ](syncCfg.custom.leftNewer); + outCustDir["RightNewer"](syncCfg.custom.rightNewer); + outCustDir["Different" ](syncCfg.custom.different); + outCustDir["Conflict" ](syncCfg.custom.conflict); } -xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg) +void writeConfig(const FilterConfig& filter, XmlOut& out) { - XmlBatchConfig output; - output.mainCfg = guiCfg.mainCfg; + out["Include"](filter.includeFilter); + out["Exclude"](filter.excludeFilter); - switch (guiCfg.handleError) - { - case ON_GUIERROR_POPUP: - output.handleError = xmlAccess::ON_ERROR_POPUP; - break; - case ON_GUIERROR_IGNORE: - output.handleError = xmlAccess::ON_ERROR_IGNORE; - break; - } + out["TimeSpan" ](filter.timeSpan); + out["UnitTimeSpan"](filter.unitTimeSpan); - return output; + out["SizeMin" ](filter.sizeMin); + out["UnitSizeMin"](filter.unitSizeMin); + + out["SizeMax" ](filter.sizeMax); + out["UnitSizeMax"](filter.unitSizeMax); } -xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<wxString>& filenames) //throw () +void writeConfigFolderPair(const FolderPairEnh& enhPair, XmlOut& out) { - bool guiCfgExists = false; - bool batchCfgExists = false; + XmlOut outPair = out.ref().addChild("Pair"); - for (std::vector<wxString>::const_iterator i = filenames.begin(); i != filenames.end(); ++i) + //read folder pairs + outPair["Left" ](enhPair.leftDirectory); + outPair["Right"](enhPair.rightDirectory); + + //########################################################### + //alternate sync configuration (optional) + if (enhPair.altSyncConfig.get()) { - switch (xmlAccess::getXmlType(*i)) //throw() - { - case XML_TYPE_GUI: - guiCfgExists = true; - break; + XmlOut outAlt = outPair["AlternateSyncConfig"]; - case XML_TYPE_BATCH: - batchCfgExists = true; - break; + //read sync configuration + writeConfig(enhPair.altSyncConfig->syncConfiguration, outAlt); - case XML_TYPE_GLOBAL: - case XML_TYPE_OTHER: - return MERGE_OTHER; - } + outAlt["DeletionPolicy" ](enhPair.altSyncConfig->handleDeletion); + outAlt["CustomDeletionFolder"](enhPair.altSyncConfig->customDeletionDirectory); } - 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; + //########################################################### + //alternate filter configuration + XmlOut outFilter = outPair["LocalFilter"]; + writeConfig(enhPair.localFilter, outFilter); } -namespace -{ -template <class XmlCfg> -XmlCfg loadCfgImpl(const wxString& filename, std::auto_ptr<xmlAccess::XmlError>& exeption) //throw (xmlAccess::XmlError) +void writeConfig(const MainConfiguration& mainCfg, XmlOut& out) { - XmlCfg cfg; - try - { - xmlAccess::readConfig(filename, cfg); //throw (xmlAccess::XmlError); - } - catch (const xmlAccess::XmlError& e) - { - if (e.getSeverity() == xmlAccess::XmlError::FATAL) - throw; - else - exeption.reset(new xmlAccess::XmlError(e)); - } - return cfg; -} + XmlOut outCmp = out["MainConfig"]["Comparison"]; + //write compare variant + outCmp["Variant"](mainCfg.compareVar); -template <class XmlCfg> -void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config) //throw (xmlAccess::XmlError) -{ - using namespace xmlAccess; - - assert(!filenames.empty()); - if (filenames.empty()) - return; + //include symbolic links at all? + outCmp["HandleSymlinks"](mainCfg.handleSymlinks); - std::vector<zen::MainConfiguration> mainCfgs; - std::auto_ptr<XmlError> savedException; + //########################################################### + XmlOut outSync = out["MainConfig"]["SyncConfig"]; - for (std::vector<wxString>::const_iterator i = filenames.begin(); i != filenames.end(); ++i) - { - switch (getXmlType(*i)) - { - case XML_TYPE_GUI: - mainCfgs.push_back(loadCfgImpl<XmlGuiConfig>(*i, savedException).mainCfg); //throw (xmlAccess::XmlError) - break; + //write sync configuration + writeConfig(mainCfg.syncConfiguration, outSync); + //########################################################### - case XML_TYPE_BATCH: - mainCfgs.push_back(loadCfgImpl<XmlBatchConfig>(*i, savedException).mainCfg); //throw (xmlAccess::XmlError) - break; + //misc + outSync["DeletionPolicy" ](mainCfg.handleDeletion); + outSync["CustomDeletionFolder"](mainCfg.customDeletionDirectory); + //########################################################### - case XML_TYPE_GLOBAL: - case XML_TYPE_OTHER: - break; - } - } + XmlOut outFilter = out["MainConfig"]["GlobalFilter"]; + //write filter settings + writeConfig(mainCfg.globalFilter, outFilter); - if (mainCfgs.empty()) - throw XmlError(_("Invalid FreeFileSync config file!")); + //########################################################### + //write all folder pairs - try //...to init all non-"mainCfg" settings with first config file - { - xmlAccess::readConfig(filenames[0], config); //throw (xmlAccess::XmlError); - } - catch (...) {} + XmlOut outFp = out["MainConfig"]["FolderPairs"]; - config.mainCfg = merge(mainCfgs); + //write first folder pair + writeConfigFolderPair(mainCfg.firstPair, outFp); - if (savedException.get()) //"re-throw" exception - throw* savedException; -} + //write additional folder pairs + std::for_each(mainCfg.additionalPairs.begin(), mainCfg.additionalPairs.end(), + [&](const FolderPairEnh& fp) { writeConfigFolderPair(fp, outFp); }); } -void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlGuiConfig& config) //throw (xmlAccess::XmlError) +void writeConfig(const XmlGuiConfig& config, XmlOut& out) { - mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::XmlError) -} + writeConfig(config.mainCfg, out); //write main config + //write GUI specific config data + XmlOut outGuiCfg = out["GuiConfig"]; -void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlBatchConfig& config) //throw (xmlAccess::XmlError); -{ - mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::XmlError) + 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 xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlGuiConfig& config) +void writeConfig(const XmlGlobalSettings& config, XmlOut& out) { - //load XML - if (!fileExists(wxToZ(filename))) - throw XmlError(wxString(_("File does not exist:")) + wxT("\n\"") + filename + wxT("\"")); + XmlOut outShared = out["Shared"]; - TiXmlDocument doc; - loadXmlDocument(filename, doc); //throw (XmlError) + //write program language setting + outShared["Language"](config.programLanguage); - if (getXmlType(doc) != XML_TYPE_GUI) //throw() - throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"")); + //copy locked files using VSS + outShared["CopyLockedFiles"](config.copyLockedFiles); - FfsXmlErrorLogger logger; - logger.readConfig(doc.RootElement(), config); //read GUI layout configuration + //file permissions + outShared["CopyFilePermissions"](config.copyFilePermissions); - if (logger.errorsOccurred()) - throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"\n\n") + - logger.getErrorMessageFormatted(), XmlError::WARNING); -} + //verify file copying + 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["PopupOnConfigChange"](config.optDialogs.popupOnConfigChange); + outOpt["SummaryBeforeSync" ](config.optDialogs.showSummaryBeforeSync); -void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlBatchConfig& config) -{ - //load XML - if (!fileExists(wxToZ(filename))) - throw XmlError(wxString(_("File does not exist:")) + wxT("\n\"") + filename + wxT("\"")); + //gui specific global settings (optional) + XmlOut outGui = out["Gui"]; + XmlOut outWnd = outGui["Windows"]["Main"]; - TiXmlDocument doc; - loadXmlDocument(filename, doc); //throw (XmlError) + //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); - if (getXmlType(doc) != XML_TYPE_BATCH) //throw() - throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"")); + outWnd["ManualDeletionOnBothSides"](config.gui.deleteOnBothSides); + outWnd["ManualDeletionUseRecycler"](config.gui.useRecyclerForManualDeletion); + outWnd["RespectCaseOnSearch" ](config.gui.textSearchRespectCase); - FfsXmlErrorLogger logger; - logger.readConfig(doc.RootElement(), config); //read GUI layout configuration + //########################################################### - if (logger.errorsOccurred()) - throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"\n\n") + - logger.getErrorMessageFormatted(), XmlError::WARNING); -} + //write column attributes + XmlOut outColLeft = outWnd["LeftColumns"]; + outColLeft.attribute("AutoAdjust", config.gui.autoAdjustColumnsLeft); + outColLeft.attribute("ShowFileIcons", config.gui.showFileIconsLeft); -void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& config) -{ - //load XML - if (!fileExists(wxToZ(getGlobalConfigFile()))) - throw XmlError(wxString(_("File does not exist:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"")); + outColLeft(config.gui.columnAttribLeft); - TiXmlDocument doc; - loadXmlDocument(getGlobalConfigFile(), doc); //throw (XmlError) + //########################################################### + XmlOut outColRight = outWnd["RightColumns"]; - if (getXmlType(doc) != XML_TYPE_GLOBAL) //throw() - throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"")); + outColRight.attribute("AutoAdjust", config.gui.autoAdjustColumnsRight); + outColRight.attribute("ShowFileIcons", config.gui.showFileIconsRight); - FfsXmlErrorLogger logger; - logger.readConfig(doc.RootElement(), config); //read GUI layout configuration + outColRight(config.gui.columnAttribRight); - if (logger.errorsOccurred()) - throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"\n\n") + - logger.getErrorMessageFormatted(), XmlError::WARNING); -} + 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); -void xmlAccess::writeConfig(const XmlGuiConfig& outputCfg, const wxString& filename) -{ - TiXmlDocument doc; - initXmlDocument(doc, XML_TYPE_GUI); //throw() + //load config file history + XmlOut outHist = outGui["ConfigHistory"]; - if (!doc.RootElement()) - throw XmlError(wxString(_("Error writing file:")) + wxT("\n\"") + filename + wxT("\"")); + outHist.attribute("LastUsed", config.gui.lastUsedConfigFile); + outHist(config.gui.cfgFileHistory); - writeConfig(outputCfg, *doc.RootElement()); //add GUI layout configuration settings + //last update check + outGui["LastUpdateCheck"](config.gui.lastUpdateCheck); - saveXmlDocument(filename, doc); //throw (XmlError) + //batch specific global settings + //XmlOut outBatch = out["Batch"]; } -void xmlAccess::writeConfig(const XmlBatchConfig& outputCfg, const wxString& filename) +template <class ConfigType> +void writeConfig(const ConfigType& config, XmlType type, const wxString& filename) { - TiXmlDocument doc; - initXmlDocument(doc, XML_TYPE_BATCH); //throw() + XmlDoc doc("FreeFileSync"); + setXmlType(doc, type); //throw() - if (!doc.RootElement()) - throw XmlError(wxString(_("Error writing file:")) + wxT("\n\"") + filename + wxT("\"")); + XmlOut out(doc); + writeConfig(config, out); - writeConfig(outputCfg, *doc.RootElement()); //add GUI layout configuration settings + saveXmlDocument(doc, filename); //throw (FfsXmlError) +} +} - saveXmlDocument(filename, doc); //throw (XmlError) +void xmlAccess::writeConfig(const XmlGuiConfig& config, const wxString& filename) +{ + ::writeConfig(config, XML_TYPE_GUI, filename); //throw (FfsXmlError) } -void xmlAccess::writeConfig(const XmlGlobalSettings& outputCfg) +void xmlAccess::writeConfig(const XmlBatchConfig& config, const wxString& filename) { - TiXmlDocument doc; - initXmlDocument(doc, XML_TYPE_GLOBAL); //throw() - - if (!doc.RootElement()) - throw XmlError(wxString(_("Error writing file:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"")); + ::writeConfig(config, XML_TYPE_BATCH, filename); //throw (FfsXmlError) +} - writeConfig(outputCfg, *doc.RootElement()); //add GUI layout configuration settings - saveXmlDocument(getGlobalConfigFile(), doc); //throw (XmlError) +void xmlAccess::writeConfig(const XmlGlobalSettings& config) +{ + ::writeConfig(config, XML_TYPE_GLOBAL, getGlobalConfigFile()); //throw (FfsXmlError) } |