From 6fb167582a80acd340a2de3399b0e5d1f2e064a6 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 18 Mar 2023 14:48:03 +0100 Subject: [PATCH] TD: move all XML query handling to a single function --- src/Mod/TechDraw/App/CMakeLists.txt | 2 + src/Mod/TechDraw/App/DrawSVGTemplate.cpp | 123 ++++++++++------------- src/Mod/TechDraw/App/DrawViewSymbol.cpp | 58 ++++------- src/Mod/TechDraw/App/QDomNodeModel.cpp | 2 + src/Mod/TechDraw/App/XMLQuery.cpp | 75 ++++++++++++++ src/Mod/TechDraw/App/XMLQuery.h | 49 +++++++++ src/Mod/TechDraw/Gui/QGISVGTemplate.cpp | 32 ++---- 7 files changed, 213 insertions(+), 128 deletions(-) create mode 100644 src/Mod/TechDraw/App/XMLQuery.cpp create mode 100644 src/Mod/TechDraw/App/XMLQuery.h diff --git a/src/Mod/TechDraw/App/CMakeLists.txt b/src/Mod/TechDraw/App/CMakeLists.txt index 8eb26da5f9..52e8f1c43c 100644 --- a/src/Mod/TechDraw/App/CMakeLists.txt +++ b/src/Mod/TechDraw/App/CMakeLists.txt @@ -175,6 +175,8 @@ SET(TechDraw_SRCS TechDrawExport.h ProjectionAlgos.cpp ProjectionAlgos.h + XMLQuery.cpp + XMLQuery.h ) SET(Geometry_SRCS diff --git a/src/Mod/TechDraw/App/DrawSVGTemplate.cpp b/src/Mod/TechDraw/App/DrawSVGTemplate.cpp index f9e58f5704..f6ff7edfb7 100644 --- a/src/Mod/TechDraw/App/DrawSVGTemplate.cpp +++ b/src/Mod/TechDraw/App/DrawSVGTemplate.cpp @@ -26,10 +26,7 @@ #ifndef _PreComp_ # include # include -# include # include -# include -# include #endif #include @@ -44,6 +41,7 @@ #include "DrawSVGTemplate.h" #include "DrawSVGTemplatePy.h" #include "DrawUtil.h" +#include "XMLQuery.h" using namespace TechDraw; @@ -113,72 +111,64 @@ QString DrawSVGTemplate::processTemplate() return QString(); } - QDomDocument templateDocument; - if (!templateDocument.setContent(&templateFile)) { + QDomDocument templateDocument; + if (!templateDocument.setContent(&templateFile)) { Base::Console().Error("DrawSVGTemplate::processTemplate - failed to parse file: %s\n", PageResult.getValue()); - return QString(); - } + return QString(); + } - QXmlQuery query(QXmlQuery::XQuery10); - QDomNodeModel model(query.namePool(), templateDocument); - query.setFocus(QXmlItem(model.fromDomNode(templateDocument.documentElement()))); + XMLQuery query(templateDocument); + std::map substitutions = EditableTexts.getValues(); - // XPath query to select all nodes whose parent - // has "freecad:editable" attribute - query.setQuery(QString::fromUtf8( - "declare default element namespace \"" SVG_NS_URI "\"; " - "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " - "//text[@freecad:editable]/tspan")); + // XPath query to select all nodes whose parent + // has "freecad:editable" attribute + query.processItems(QString::fromUtf8( + "declare default element namespace \"" SVG_NS_URI "\"; " + "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " + "//text[@freecad:editable]/tspan"), + [&substitutions, &templateDocument](QDomElement& tspan) -> bool { + // Replace the editable text spans with new nodes holding actual values + QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable")); + std::map::iterator item = + substitutions.find(editableName.toStdString()); + if (item != substitutions.end()) { + // Keep all spaces in the text node + tspan.setAttribute(QString::fromUtf8("xml:space"), QString::fromUtf8("preserve")); - QXmlResultItems queryResult; - query.evaluateTo(&queryResult); + // Remove all child nodes and append text node with editable replacement as the only descendant + while (!tspan.lastChild().isNull()) { + tspan.removeChild(tspan.lastChild()); + } + tspan.appendChild(templateDocument.createTextNode(QString::fromUtf8(item->second.c_str()))); + } + return true; + }); - std::map substitutions = EditableTexts.getValues(); - while (!queryResult.next().isNull()) - { - QDomElement tspan = model.toDomNode(queryResult.current().toNodeModelIndex()).toElement(); + // Calculate the dimensions of the page and store for retrieval + // Obtain the size of the SVG document by reading the document attributes + QDomElement docElement = templateDocument.documentElement(); + Base::Quantity quantity; - // Replace the editable text spans with new nodes holding actual values - QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable")); - std::map::iterator item = - substitutions.find(std::string(editableName.toUtf8().constData())); - if (item != substitutions.end()) { - // Keep all spaces in the text node - tspan.setAttribute(QString::fromUtf8("xml:space"), QString::fromUtf8("preserve")); + // Obtain the width + QString str = docElement.attribute(QString::fromLatin1("width")); + quantity = Base::Quantity::parse(str); + quantity.setUnit(Base::Unit::Length); - // Remove all child nodes and append text node with editable replacement as the only descendant - while (!tspan.lastChild().isNull()) { - tspan.removeChild(tspan.lastChild()); - } - tspan.appendChild(templateDocument.createTextNode(QString::fromUtf8(item->second.c_str()))); - } - } + Width.setValue(quantity.getValue()); - // Calculate the dimensions of the page and store for retrieval - // Obtain the size of the SVG document by reading the document attributes - QDomElement docElement = templateDocument.documentElement(); - Base::Quantity quantity; + str = docElement.attribute(QString::fromLatin1("height")); + quantity = Base::Quantity::parse(str); + quantity.setUnit(Base::Unit::Length); - // Obtain the width - QString str = docElement.attribute(QString::fromLatin1("width")); - quantity = Base::Quantity::parse(str); - quantity.setUnit(Base::Unit::Length); + Height.setValue(quantity.getValue()); - Width.setValue(quantity.getValue()); + bool isLandscape = getWidth() / getHeight() >= 1.; - str = docElement.attribute(QString::fromLatin1("height")); - quantity = Base::Quantity::parse(str); - quantity.setUnit(Base::Unit::Length); + Orientation.setValue(isLandscape ? 1 : 0); - Height.setValue(quantity.getValue()); - - bool isLandscape = getWidth() / getHeight() >= 1.; - - Orientation.setValue(isLandscape ? 1 : 0); - - //all Qt holds on files should be released on exit #4085 - return templateDocument.toString(); + //all Qt holds on files should be released on exit #4085 + return templateDocument.toString(); } double DrawSVGTemplate::getWidth() const @@ -218,7 +208,7 @@ std::map DrawSVGTemplate::getEditableTextsFromTemplate Base::FileInfo tfi(templateFilename); if (!tfi.isReadable()) { - // if there is a old absolute template file set use a redirect + // if there is an old absolute template file set use a redirect tfi.setFile(App::Application::getResourceDir() + "Mod/Drawing/Templates/" + tfi.fileName()); // try the redirect if (!tfi.isReadable()) { @@ -240,29 +230,22 @@ std::map DrawSVGTemplate::getEditableTextsFromTemplate return editables; } - QXmlQuery query(QXmlQuery::XQuery10); - QDomNodeModel model(query.namePool(), templateDocument, true); - query.setFocus(QXmlItem(model.fromDomNode(templateDocument.documentElement()))); + XMLQuery query(templateDocument); // XPath query to select all nodes whose parent // has "freecad:editable" attribute - query.setQuery(QString::fromUtf8( + query.processItems(QString::fromUtf8( "declare default element namespace \"" SVG_NS_URI "\"; " "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " - "//text[@freecad:editable]/tspan")); - - QXmlResultItems queryResult; - query.evaluateTo(&queryResult); - - while (!queryResult.next().isNull()) { - QDomElement tspan = model.toDomNode(queryResult.current().toNodeModelIndex()).toElement(); - + "//text[@freecad:editable]/tspan"), + [&editables](QDomElement& tspan) -> bool { QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable")); QString editableValue = tspan.firstChild().nodeValue(); editables[std::string(editableName.toUtf8().constData())] = std::string(editableValue.toUtf8().constData()); - } + return true; + }); return editables; } diff --git a/src/Mod/TechDraw/App/DrawViewSymbol.cpp b/src/Mod/TechDraw/App/DrawViewSymbol.cpp index 90dc8aa606..4b83b34b77 100644 --- a/src/Mod/TechDraw/App/DrawViewSymbol.cpp +++ b/src/Mod/TechDraw/App/DrawViewSymbol.cpp @@ -23,11 +23,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ # include - -# include "QDomNodeModel.h" # include -# include -# include #endif #include @@ -36,6 +32,7 @@ #include "DrawViewSymbolPy.h" // generated from DrawViewSymbolPy.xml #include "DrawPage.h" #include "DrawUtil.h" +#include "XMLQuery.h" using namespace TechDraw; @@ -105,30 +102,22 @@ bool DrawViewSymbol::checkFit(TechDraw::DrawPage* p) const std::vector DrawViewSymbol::getEditableFields() { QDomDocument symbolDocument; - QXmlResultItems queryResult; std::vector editables; bool rc = loadQDomDocument(symbolDocument); if (rc) { - QDomElement symbolDocElem = symbolDocument.documentElement(); - QXmlQuery query(QXmlQuery::XQuery10); - QDomNodeModel model(query.namePool(), symbolDocument); - query.setFocus(QXmlItem(model.fromDomNode(symbolDocument.documentElement()))); + XMLQuery query(symbolDocument); // XPath query to select all nodes whose parent // has "freecad:editable" attribute - query.setQuery(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; " - "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " - "//text[@freecad:editable]/tspan")); - - query.evaluateTo(&queryResult); - - while (!queryResult.next().isNull()) { - QDomElement tspan = - model.toDomNode(queryResult.current().toNodeModelIndex()).toElement(); + query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; " + "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " + "//text[@freecad:editable]/tspan"), + [&editables](QDomElement& tspan) -> bool { QString editableValue = tspan.firstChild().nodeValue(); - editables.emplace_back(editableValue.toUtf8().constData()); - } + editables.emplace_back(editableValue.toStdString()); + return true; + }); } return editables; } @@ -142,27 +131,22 @@ void DrawViewSymbol::updateFieldsInSymbol() } QDomDocument symbolDocument; - QXmlResultItems queryResult; bool rc = loadQDomDocument(symbolDocument); if (rc) { - QDomElement symbolDocElem = symbolDocument.documentElement(); - QXmlQuery query(QXmlQuery::XQuery10); - QDomNodeModel model(query.namePool(), symbolDocument); - query.setFocus(QXmlItem(model.fromDomNode(symbolDocElem))); + XMLQuery query(symbolDocument); + std::size_t count = 0; // XPath query to select all nodes whose parent // has "freecad:editable" attribute - query.setQuery(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; " - "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " - "//text[@freecad:editable]/tspan")); - query.evaluateTo(&queryResult); - - unsigned int count = 0; - while (!queryResult.next().isNull()) { - QDomElement tspanElement = - model.toDomNode(queryResult.current().toNodeModelIndex()).toElement(); + query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; " + "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " + "//text[@freecad:editable]/tspan"), + [&symbolDocument, &editText, &count](QDomElement& tspanElement) -> bool { + if (count >= editText.size()) { + return false; + } // Keep all spaces in the text node tspanElement.setAttribute(QString::fromUtf8("xml:space"), QString::fromUtf8("preserve")); @@ -174,9 +158,11 @@ void DrawViewSymbol::updateFieldsInSymbol() // Finally append text node with editable replacement as the only descendant tspanElement.appendChild( - symbolDocument.createTextNode(QString::fromUtf8(editText[count].c_str()))); + symbolDocument.createTextNode(QString::fromStdString(editText[count]))); ++count; - } + return true; + }); + Symbol.setValue(symbolDocument.toString(1).toStdString()); } } diff --git a/src/Mod/TechDraw/App/QDomNodeModel.cpp b/src/Mod/TechDraw/App/QDomNodeModel.cpp index 901aeadb40..702902ff34 100644 --- a/src/Mod/TechDraw/App/QDomNodeModel.cpp +++ b/src/Mod/TechDraw/App/QDomNodeModel.cpp @@ -28,6 +28,7 @@ #include #include +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) #include "QDomNodeModel.h" #include #include @@ -359,3 +360,4 @@ QXmlNodeModelIndex QDomNodeModel::nextFromSimpleAxis ( SimpleAxis axis, const QX return QXmlNodeModelIndex(); } +#endif diff --git a/src/Mod/TechDraw/App/XMLQuery.cpp b/src/Mod/TechDraw/App/XMLQuery.cpp new file mode 100644 index 0000000000..9090907c1a --- /dev/null +++ b/src/Mod/TechDraw/App/XMLQuery.cpp @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (c) 2023 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) +# include "QDomNodeModel.h" +# include +# include +#endif +#endif + +#include "XMLQuery.h" + + +using namespace TechDraw; + +XMLQuery::XMLQuery(QDomDocument& dom) + : domDocument(dom) +{ + +} + +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) +bool XMLQuery::processItems(const QString& queryStr, const std::function& process) +{ + QXmlQuery query(QXmlQuery::XQuery10); + QDomNodeModel model(query.namePool(), domDocument); + QDomElement symbolDocElem = domDocument.documentElement(); + query.setFocus(QXmlItem(model.fromDomNode(symbolDocElem))); + + query.setQuery(queryStr); + QXmlResultItems queryResult; + query.evaluateTo(&queryResult); + + while (!queryResult.next().isNull()) { + QDomElement tspanElement = + model.toDomNode(queryResult.current().toNodeModelIndex()).toElement(); + if (!process(tspanElement)) { + return false; + } + } + + return true; +} +#else +bool XMLQuery::processItems(const QString& queryStr, const std::function& process) +{ + //TODO: Port to Qt6 + Q_UNUSED(queryStr) + Q_UNUSED(process) + return false; +} +#endif diff --git a/src/Mod/TechDraw/App/XMLQuery.h b/src/Mod/TechDraw/App/XMLQuery.h new file mode 100644 index 0000000000..309fe4c17e --- /dev/null +++ b/src/Mod/TechDraw/App/XMLQuery.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (c) 2023 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef TECHDRAW_XMLQuery_h_ +#define TECHDRAW_XMLQuery_h_ + +#include +#include + +QT_BEGIN_NAMESPACE +class QDomDocument; +class QDomElement; +QT_END_NAMESPACE + +namespace TechDraw +{ + +class TechDrawExport XMLQuery +{ +public: + XMLQuery(QDomDocument&); + bool processItems(const QString& queryStr, const std::function& process); + +private: + QDomDocument& domDocument; +}; + +} //namespace TechDraw + +#endif //TECHDRAW_XMLQuery_h_ diff --git a/src/Mod/TechDraw/Gui/QGISVGTemplate.cpp b/src/Mod/TechDraw/Gui/QGISVGTemplate.cpp index 2cdf7f8bca..298e3ab972 100644 --- a/src/Mod/TechDraw/Gui/QGISVGTemplate.cpp +++ b/src/Mod/TechDraw/Gui/QGISVGTemplate.cpp @@ -29,8 +29,6 @@ # include # include # include -# include -# include #endif// #ifndef _PreComp_ #include @@ -39,7 +37,7 @@ #include #include -#include +#include #include "QGISVGTemplate.h" #include "PreferencesGui.h" @@ -165,20 +163,6 @@ void QGISVGTemplate::createClickHandles() } file.close(); - QDomElement templateDocElem = templateDocument.documentElement(); - - QXmlQuery query(QXmlQuery::XQuery10); - QDomNodeModel model(query.namePool(), templateDocument); - query.setFocus(QXmlItem(model.fromDomNode(templateDocElem))); - - // XPath query to select all nodes with "freecad:editable" attribute - query.setQuery(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; " - "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " - "//text[@freecad:editable]")); - - QXmlResultItems queryResult; - query.evaluateTo(&queryResult); - //TODO: Find location of special fields (first/third angle) and make graphics items for them Base::Reference hGrp = App::GetApplication() @@ -194,10 +178,13 @@ void QGISVGTemplate::createClickHandles() double width = editClickBoxSize; double height = editClickBoxSize; - while (!queryResult.next().isNull()) { - QDomElement textElement = - model.toDomNode(queryResult.current().toNodeModelIndex()).toElement(); + TechDraw::XMLQuery query(templateDocument); + // XPath query to select all nodes with "freecad:editable" attribute + query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; " + "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " + "//text[@freecad:editable]"), + [&](QDomElement& textElement) -> bool { QString name = textElement.attribute(QString::fromUtf8("freecad:editable")); double x = Rez::guiX( textElement.attribute(QString::fromUtf8("x"), QString::fromUtf8("0.0")).toDouble()); @@ -207,7 +194,7 @@ void QGISVGTemplate::createClickHandles() if (name.isEmpty()) { Base::Console().Warning( "QGISVGTemplate::createClickHandles - no name for editable text at %f, %f\n", x, y); - continue; + return true; } auto item(new TemplateTextField(this, svgTemplate, name.toStdString())); @@ -229,7 +216,8 @@ void QGISVGTemplate::createClickHandles() addToGroup(item); textFields.push_back(item); - } + return true; + }); } #include