Merge pull request #4787 from chennes/addThemeSupport
[GUI] Add Preference Pack support
This commit is contained in:
@@ -92,6 +92,7 @@ generate_from_xml(LinkBaseExtensionPy)
|
||||
generate_from_xml(DocumentObjectGroupPy)
|
||||
generate_from_xml(GeoFeaturePy)
|
||||
generate_from_xml(GeoFeatureGroupExtensionPy)
|
||||
generate_from_xml(MetadataPy)
|
||||
generate_from_xml(OriginGroupExtensionPy)
|
||||
generate_from_xml(PartPy)
|
||||
|
||||
@@ -108,6 +109,7 @@ SET(FreeCADApp_XML_SRCS
|
||||
DocumentObjectExtensionPy.xml
|
||||
GroupExtensionPy.xml
|
||||
LinkBaseExtensionPy.xml
|
||||
MetadataPy.xml
|
||||
DocumentObjectGroupPy.xml
|
||||
DocumentObjectPy.xml
|
||||
GeoFeaturePy.xml
|
||||
@@ -261,6 +263,8 @@ SET(FreeCADApp_CPP_SRCS
|
||||
Enumeration.cpp
|
||||
Material.cpp
|
||||
MaterialPyImp.cpp
|
||||
Metadata.cpp
|
||||
MetadataPyImp.cpp
|
||||
)
|
||||
|
||||
SET(FreeCADApp_HPP_SRCS
|
||||
@@ -273,6 +277,7 @@ SET(FreeCADApp_HPP_SRCS
|
||||
ComplexGeoData.h
|
||||
Enumeration.h
|
||||
Material.h
|
||||
Metadata.h
|
||||
)
|
||||
|
||||
SET(FreeCADApp_SRCS
|
||||
|
||||
750
src/App/Metadata.cpp
Normal file
750
src/App/Metadata.cpp
Normal file
@@ -0,0 +1,750 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
|
||||
* *
|
||||
* 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 LICENSE.html. 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 <memory>
|
||||
#endif
|
||||
|
||||
#include "Metadata.h"
|
||||
|
||||
#include <xercesc/sax/HandlerBase.hpp>
|
||||
#include <xercesc/util/XMLString.hpp>
|
||||
#include <xercesc/util/PlatformUtils.hpp>
|
||||
#include <xercesc/util/PlatformUtils.hpp>
|
||||
#include <xercesc/dom/DOM.hpp>
|
||||
#include <xercesc/parsers/XercesDOMParser.hpp>
|
||||
#include <xercesc/dom/DOMImplementation.hpp>
|
||||
#include <xercesc/dom/DOMImplementationLS.hpp>
|
||||
#include <xercesc/framework/LocalFileFormatTarget.hpp>
|
||||
|
||||
|
||||
#include "Base/XMLTools.h"
|
||||
#include "App/Expression.h"
|
||||
#include "App/Application.h"
|
||||
|
||||
/*
|
||||
*** From GCC: ***
|
||||
In the GNU C Library, "major" and "minor" are defined
|
||||
by <sys/sysmacros.h>. For historical compatibility, it is
|
||||
currently defined by <sys/types.h> as well, but we plan to
|
||||
remove this soon. To use "major", include <sys/sysmacros.h>
|
||||
directly. If you did not intend to use a system-defined macro
|
||||
"major", you should undefine it after including <sys/types.h>.
|
||||
*/
|
||||
#ifdef major
|
||||
#undef major
|
||||
#endif
|
||||
#ifdef minor
|
||||
#undef minor
|
||||
#endif
|
||||
|
||||
using namespace App;
|
||||
namespace fs = boost::filesystem;
|
||||
XERCES_CPP_NAMESPACE_USE
|
||||
|
||||
|
||||
Metadata::Metadata(const fs::path& metadataFile)
|
||||
{
|
||||
// Any exception thrown by the XML code propagates out and prevents object creation
|
||||
XMLPlatformUtils::Initialize();
|
||||
|
||||
_parser = std::make_shared<XercesDOMParser>();
|
||||
_parser->setValidationScheme(XercesDOMParser::Val_Never);
|
||||
_parser->setDoNamespaces(true);
|
||||
|
||||
auto errHandler = std::make_unique<HandlerBase>();
|
||||
_parser->setErrorHandler(errHandler.get());
|
||||
|
||||
_parser->parse(metadataFile.string().c_str());
|
||||
|
||||
auto doc = _parser->getDocument();
|
||||
_dom = doc->getDocumentElement();
|
||||
|
||||
auto rootTagName = StrXUTF8(_dom->getTagName()).str;
|
||||
if (rootTagName != "package")
|
||||
throw Base::XMLBaseException("Malformed package.xml document: Root <package> group not found");
|
||||
|
||||
auto formatVersion = XMLString::parseInt(_dom->getAttribute(XUTF8Str("format").unicodeForm()));
|
||||
switch (formatVersion) {
|
||||
case 1:
|
||||
parseVersion1(_dom);
|
||||
break;
|
||||
default:
|
||||
throw Base::XMLBaseException("package.xml format version is not supported by this version of FreeCAD");
|
||||
}
|
||||
}
|
||||
|
||||
Metadata::Metadata() : _dom(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Metadata::Metadata(const DOMNode* domNode, int format) : _dom(nullptr)
|
||||
{
|
||||
auto element = dynamic_cast<const DOMElement*>(domNode);
|
||||
if (element) {
|
||||
switch (format) {
|
||||
case 1:
|
||||
parseVersion1(element);
|
||||
break;
|
||||
default:
|
||||
throw Base::XMLBaseException("package.xml format version is not supported by this version of FreeCAD");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Metadata::~Metadata()
|
||||
{
|
||||
}
|
||||
|
||||
std::string Metadata::name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
Meta::Version Metadata::version() const
|
||||
{
|
||||
return _version;
|
||||
}
|
||||
|
||||
std::string Metadata::description() const
|
||||
{
|
||||
return _description;
|
||||
}
|
||||
|
||||
std::vector<Meta::Contact> Metadata::maintainer() const
|
||||
{
|
||||
return _maintainer;
|
||||
}
|
||||
|
||||
std::vector<Meta::License> Metadata::license() const
|
||||
{
|
||||
return _license;
|
||||
}
|
||||
|
||||
std::vector<Meta::Url> Metadata::url() const
|
||||
{
|
||||
return _url;
|
||||
}
|
||||
|
||||
std::vector<Meta::Contact> Metadata::author() const
|
||||
{
|
||||
return _author;
|
||||
}
|
||||
|
||||
std::vector<Meta::Dependency> Metadata::depend() const
|
||||
{
|
||||
return _depend;
|
||||
}
|
||||
|
||||
std::vector<Meta::Dependency> Metadata::conflict() const
|
||||
{
|
||||
return _conflict;
|
||||
}
|
||||
|
||||
std::vector<Meta::Dependency> Metadata::replace() const
|
||||
{
|
||||
return _replace;
|
||||
}
|
||||
|
||||
std::vector<std::string> Metadata::tag() const
|
||||
{
|
||||
return _tag;
|
||||
}
|
||||
|
||||
fs::path Metadata::icon() const
|
||||
{
|
||||
return _icon;
|
||||
}
|
||||
|
||||
std::string Metadata::classname() const
|
||||
{
|
||||
return _classname;
|
||||
}
|
||||
|
||||
std::vector<fs::path> Metadata::file() const
|
||||
{
|
||||
return _file;
|
||||
}
|
||||
|
||||
Meta::Version App::Metadata::freecadmin() const
|
||||
{
|
||||
return _freecadmin;
|
||||
}
|
||||
|
||||
Meta::Version App::Metadata::freecadmax() const
|
||||
{
|
||||
return _freecadmax;
|
||||
}
|
||||
|
||||
std::multimap<std::string, Metadata> Metadata::content() const
|
||||
{
|
||||
return _content;
|
||||
}
|
||||
|
||||
std::vector<Meta::GenericMetadata> Metadata::operator[](const std::string& tag) const
|
||||
{
|
||||
std::vector<Meta::GenericMetadata> returnValue;
|
||||
auto range = _genericMetadata.equal_range(tag);
|
||||
for (auto item = range.first; item != range.second; ++item)
|
||||
returnValue.push_back(item->second);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
XERCES_CPP_NAMESPACE::DOMElement* Metadata::dom() const
|
||||
{
|
||||
return _dom;
|
||||
}
|
||||
|
||||
void Metadata::setName(const std::string& name)
|
||||
{
|
||||
std::string invalidCharacters = "/\\?%*:|\"<>"; // Should cover all OSes
|
||||
if (_name.find_first_of(invalidCharacters) != std::string::npos)
|
||||
throw Base::RuntimeError("Name cannot contain any of: " + invalidCharacters);
|
||||
|
||||
_name = name;
|
||||
}
|
||||
|
||||
void Metadata::setVersion(const Meta::Version& version)
|
||||
{
|
||||
_version = version;
|
||||
}
|
||||
|
||||
void Metadata::setDescription(const std::string& description)
|
||||
{
|
||||
_description = description;
|
||||
}
|
||||
|
||||
void Metadata::addMaintainer(const Meta::Contact& maintainer)
|
||||
{
|
||||
_maintainer.push_back(maintainer);
|
||||
}
|
||||
|
||||
void Metadata::addLicense(const Meta::License& license)
|
||||
{
|
||||
_license.push_back(license);
|
||||
}
|
||||
|
||||
void Metadata::addUrl(const Meta::Url& url)
|
||||
{
|
||||
_url.push_back(url);
|
||||
}
|
||||
|
||||
void Metadata::addAuthor(const Meta::Contact& author)
|
||||
{
|
||||
_author.push_back(author);
|
||||
}
|
||||
|
||||
void Metadata::addDepend(const Meta::Dependency& dep)
|
||||
{
|
||||
_depend.push_back(dep);
|
||||
}
|
||||
|
||||
void Metadata::addConflict(const Meta::Dependency& dep)
|
||||
{
|
||||
_conflict.push_back(dep);
|
||||
}
|
||||
|
||||
void Metadata::addReplace(const Meta::Dependency& dep)
|
||||
{
|
||||
_replace.push_back(dep);
|
||||
}
|
||||
|
||||
void Metadata::addTag(const std::string& tag)
|
||||
{
|
||||
_tag.push_back(tag);
|
||||
}
|
||||
|
||||
void Metadata::setIcon(const fs::path& path)
|
||||
{
|
||||
_icon = path;
|
||||
}
|
||||
|
||||
void Metadata::setClassname(const std::string& name)
|
||||
{
|
||||
_classname = name;
|
||||
}
|
||||
|
||||
void Metadata::addFile(const fs::path& path)
|
||||
{
|
||||
_file.push_back(path);
|
||||
}
|
||||
|
||||
void Metadata::addContentItem(const std::string& tag, const Metadata& item)
|
||||
{
|
||||
_content.insert(std::make_pair(tag, item));
|
||||
}
|
||||
|
||||
void App::Metadata::setFreeCADMin(const Meta::Version& version)
|
||||
{
|
||||
_freecadmin = version;
|
||||
}
|
||||
|
||||
void App::Metadata::setFreeCADMax(const Meta::Version& version)
|
||||
{
|
||||
_freecadmax = version;
|
||||
}
|
||||
|
||||
void App::Metadata::addGenericMetadata(const std::string& tag, const Meta::GenericMetadata& genericMetadata)
|
||||
{
|
||||
_genericMetadata.insert(std::make_pair(tag, genericMetadata));
|
||||
}
|
||||
|
||||
|
||||
DOMElement* appendSimpleXMLNode(DOMElement* baseNode, const std::string& nodeName, const std::string& nodeContents)
|
||||
{
|
||||
// For convenience (and brevity of final output) don't create nodes that don't have contents
|
||||
if (nodeContents.empty())
|
||||
return nullptr;
|
||||
|
||||
auto doc = baseNode->getOwnerDocument();
|
||||
DOMElement* namedElement = doc->createElement(XUTF8Str(nodeName.c_str()).unicodeForm());
|
||||
baseNode->appendChild(namedElement);
|
||||
DOMText* namedNode = doc->createTextNode(XUTF8Str(nodeContents.c_str()).unicodeForm());
|
||||
namedElement->appendChild(namedNode);
|
||||
return namedElement;
|
||||
}
|
||||
|
||||
void addAttribute(DOMElement* node, const std::string& key, const std::string& value)
|
||||
{
|
||||
if (value.empty())
|
||||
return;
|
||||
|
||||
node->setAttribute(XUTF8Str(key.c_str()).unicodeForm(), XUTF8Str(value.c_str()).unicodeForm());
|
||||
}
|
||||
|
||||
void addDependencyNode(DOMElement* root, const std::string& name, const Meta::Dependency& depend)
|
||||
{
|
||||
auto element = appendSimpleXMLNode(root, name, depend.package);
|
||||
if (element) {
|
||||
addAttribute(element, "version_lt", depend.version_lt);
|
||||
addAttribute(element, "version_lte", depend.version_lte);
|
||||
addAttribute(element, "version_eq", depend.version_eq);
|
||||
addAttribute(element, "version_gte", depend.version_gte);
|
||||
addAttribute(element, "version_gt", depend.version_gt);
|
||||
addAttribute(element, "condition", depend.condition);
|
||||
}
|
||||
}
|
||||
|
||||
void Metadata::write(const fs::path& file) const
|
||||
{
|
||||
DOMImplementation* impl = DOMImplementationRegistry::getDOMImplementation(XUTF8Str("Core LS").unicodeForm());
|
||||
|
||||
DOMDocument* doc = impl->createDocument(nullptr, XUTF8Str("package").unicodeForm(), nullptr);
|
||||
DOMElement* root = doc->getDocumentElement();
|
||||
root->setAttribute(XUTF8Str("format").unicodeForm(), XUTF8Str("1").unicodeForm());
|
||||
|
||||
appendToElement(root);
|
||||
|
||||
DOMLSSerializer* theSerializer = ((DOMImplementationLS*)impl)->createLSSerializer();
|
||||
DOMConfiguration* config = theSerializer->getDomConfig();
|
||||
if (config->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true))
|
||||
config->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true);
|
||||
|
||||
// set feature if the serializer supports the feature/mode
|
||||
if (config->canSetParameter(XMLUni::fgDOMWRTSplitCdataSections, true))
|
||||
config->setParameter(XMLUni::fgDOMWRTSplitCdataSections, true);
|
||||
|
||||
if (config->canSetParameter(XMLUni::fgDOMWRTDiscardDefaultContent, true))
|
||||
config->setParameter(XMLUni::fgDOMWRTDiscardDefaultContent, true);
|
||||
|
||||
try {
|
||||
XMLFormatTarget* myFormTarget = new LocalFileFormatTarget(file.string().c_str());
|
||||
DOMLSOutput* theOutput = ((DOMImplementationLS*)impl)->createLSOutput();
|
||||
|
||||
theOutput->setByteStream(myFormTarget);
|
||||
theSerializer->write(doc, theOutput);
|
||||
|
||||
theOutput->release();
|
||||
theSerializer->release();
|
||||
delete myFormTarget;
|
||||
}
|
||||
catch (const XMLException& toCatch) {
|
||||
char* message = XMLString::transcode(toCatch.getMessage());
|
||||
std::string what = message;
|
||||
XMLString::release(&message);
|
||||
throw Base::XMLBaseException(what);
|
||||
}
|
||||
catch (const DOMException& toCatch) {
|
||||
char* message = XMLString::transcode(toCatch.getMessage());
|
||||
std::string what = message;
|
||||
XMLString::release(&message);
|
||||
throw Base::XMLBaseException(what);
|
||||
}
|
||||
|
||||
doc->release();
|
||||
}
|
||||
|
||||
bool Metadata::satisfies(const Meta::Dependency& dep)
|
||||
{
|
||||
if (dep.package != _name)
|
||||
return false;
|
||||
|
||||
// The "condition" attribute allows an expression to enable or disable this dependency check: it must contain a valid
|
||||
// FreeCAD Expression. If it evaluates to false, this dependency is bypassed (e.g. this function returns false).
|
||||
if (!dep.condition.empty()) {
|
||||
auto injectedString = dep.condition;
|
||||
std::map<std::string, std::string> replacements;
|
||||
std::map<std::string, std::string>& config = App::Application::Config();
|
||||
replacements.insert(std::make_pair("$BuildVersionMajor", config["BuildVersionMajor"]));
|
||||
replacements.insert(std::make_pair("$BuildVersionMinor", config["BuildVersionMinor"]));
|
||||
replacements.insert(std::make_pair("$BuildRevision", config["BuildRevision"]));
|
||||
for (const auto& replacement : replacements) {
|
||||
auto pos = injectedString.find(replacement.first);
|
||||
while (pos != std::string::npos) {
|
||||
injectedString.replace(pos, replacement.first.length(), replacement.second);
|
||||
pos = injectedString.find(replacement.first);
|
||||
}
|
||||
}
|
||||
auto parsedExpression = App::Expression::parse(nullptr, dep.condition);
|
||||
auto result = parsedExpression->eval();
|
||||
if (boost::any_cast<bool> (result->getValueAsAny()) == false)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dep.version_eq.empty())
|
||||
return _version == Meta::Version(dep.version_eq);
|
||||
|
||||
// Any of the others might be specified in pairs, so only return the "false" case
|
||||
|
||||
if (!dep.version_lt.empty())
|
||||
if (!(_version < Meta::Version(dep.version_lt)))
|
||||
return false;
|
||||
|
||||
if (!dep.version_lte.empty())
|
||||
if (!(_version <= Meta::Version(dep.version_lt)))
|
||||
return false;
|
||||
|
||||
if (!dep.version_gt.empty())
|
||||
if (!(_version > Meta::Version(dep.version_lt)))
|
||||
return false;
|
||||
|
||||
if (!dep.version_gte.empty())
|
||||
if (!(_version >= Meta::Version(dep.version_lt)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Metadata::appendToElement(DOMElement* root) const
|
||||
{
|
||||
appendSimpleXMLNode(root, "name", _name);
|
||||
appendSimpleXMLNode(root, "description", _description);
|
||||
appendSimpleXMLNode(root, "version", _version.str());
|
||||
|
||||
for (const auto& maintainer : _maintainer) {
|
||||
auto element = appendSimpleXMLNode(root, "maintainer", maintainer.name);
|
||||
if (element)
|
||||
addAttribute(element, "email", maintainer.email);
|
||||
}
|
||||
|
||||
for (const auto& license : _license) {
|
||||
auto element = appendSimpleXMLNode(root, "license", license.name);
|
||||
if (element)
|
||||
addAttribute(element, "file", license.file.string());
|
||||
}
|
||||
|
||||
if (_freecadmin != Meta::Version())
|
||||
appendSimpleXMLNode(root, "freecadmin", _freecadmin.str());
|
||||
|
||||
if (_freecadmax != Meta::Version())
|
||||
appendSimpleXMLNode(root, "freecadmax", _freecadmin.str());
|
||||
|
||||
for (const auto& url : _url) {
|
||||
auto element = appendSimpleXMLNode(root, "url", url.location);
|
||||
if (element) {
|
||||
std::string typeAsString("website");
|
||||
switch (url.type) {
|
||||
case Meta::UrlType::website: typeAsString = "website"; break;
|
||||
case Meta::UrlType::repository: typeAsString = "repository"; break;
|
||||
case Meta::UrlType::bugtracker: typeAsString = "bugtracker"; break;
|
||||
case Meta::UrlType::readme: typeAsString = "readme"; break;
|
||||
case Meta::UrlType::documentation: typeAsString = "documentation"; break;
|
||||
}
|
||||
addAttribute(element, "type", typeAsString);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& author : _author) {
|
||||
auto element = appendSimpleXMLNode(root, "author", author.name);
|
||||
if (element)
|
||||
addAttribute(element, "email", author.email);
|
||||
}
|
||||
|
||||
for (const auto& depend : _depend)
|
||||
addDependencyNode(root, "depend", depend);
|
||||
|
||||
for (const auto& conflict : _conflict)
|
||||
addDependencyNode(root, "conflict", conflict);
|
||||
|
||||
for (const auto& replace : _replace)
|
||||
addDependencyNode(root, "replace", replace);
|
||||
|
||||
for (const auto& tag : _tag)
|
||||
appendSimpleXMLNode(root, "tag", tag);
|
||||
|
||||
appendSimpleXMLNode(root, "icon", _icon.string());
|
||||
|
||||
appendSimpleXMLNode(root, "classname", _classname);
|
||||
|
||||
for (const auto& file : _file)
|
||||
appendSimpleXMLNode(root, "file", file.string());
|
||||
|
||||
for (const auto& md : _genericMetadata) {
|
||||
auto element = appendSimpleXMLNode(root, md.first, md.second.contents);
|
||||
for (const auto& attr : md.second.attributes)
|
||||
addAttribute(element, attr.first, attr.second);
|
||||
}
|
||||
|
||||
if (!_content.empty()) {
|
||||
auto doc = root->getOwnerDocument();
|
||||
DOMElement* contentRootElement = doc->createElement(XUTF8Str("content").unicodeForm());
|
||||
root->appendChild(contentRootElement);
|
||||
for (const auto& content : _content) {
|
||||
DOMElement* contentElement = doc->createElement(XUTF8Str(content.first.c_str()).unicodeForm());
|
||||
contentRootElement->appendChild(contentElement);
|
||||
content.second.appendToElement(contentElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Metadata::parseVersion1(const DOMNode* startNode)
|
||||
{
|
||||
auto children = startNode->getChildNodes();
|
||||
|
||||
for (XMLSize_t i = 0; i < children->getLength(); ++i) {
|
||||
auto child = children->item(i);
|
||||
auto element = dynamic_cast<const DOMElement*>(child);
|
||||
if (!element)
|
||||
continue;
|
||||
|
||||
auto tag = element->getNodeName();
|
||||
auto tagString = StrXUTF8(tag).str;
|
||||
|
||||
if (tagString == "name")
|
||||
_name = StrXUTF8(element->getTextContent()).str;
|
||||
else if (tagString == "version")
|
||||
_version = Meta::Version(StrXUTF8(element->getTextContent()).str);
|
||||
else if (tagString == "description")
|
||||
_description = StrXUTF8(element->getTextContent()).str;
|
||||
else if (tagString == "maintainer")
|
||||
_maintainer.emplace_back(element);
|
||||
else if (tagString == "license")
|
||||
_license.emplace_back(element);
|
||||
else if (tagString == "freecadmin")
|
||||
_freecadmin = Meta::Version(StrXUTF8(element->getTextContent()).str);
|
||||
else if (tagString == "freecadmax")
|
||||
_freecadmax = Meta::Version(StrXUTF8(element->getTextContent()).str);
|
||||
else if (tagString == "url")
|
||||
_url.emplace_back(element);
|
||||
else if (tagString == "author")
|
||||
_author.emplace_back(element);
|
||||
else if (tagString == "depend")
|
||||
_depend.emplace_back(element);
|
||||
else if (tagString == "conflict")
|
||||
_conflict.emplace_back(element);
|
||||
else if (tagString == "replace")
|
||||
_replace.emplace_back(element);
|
||||
else if (tagString == "tag")
|
||||
_tag.emplace_back(StrXUTF8(element->getTextContent()).str);
|
||||
else if (tagString == "file")
|
||||
_file.emplace_back(StrXUTF8(element->getTextContent()).str);
|
||||
else if (tagString == "classname")
|
||||
_classname = StrXUTF8(element->getTextContent()).str;
|
||||
else if (tagString == "icon")
|
||||
_icon = fs::path(StrXUTF8(element->getTextContent()).str);
|
||||
else if (tagString == "content")
|
||||
parseContentNodeVersion1(element); // Recursive call
|
||||
else {
|
||||
// If none of this node's children have children of their own, it is a simple element and we
|
||||
// can handle it as a GenericMetadata object
|
||||
auto children = element->getChildNodes();
|
||||
bool hasGrandchildren = false;
|
||||
for (XMLSize_t i = 0; i < children->getLength() && !hasGrandchildren; ++i)
|
||||
if (children->item(i)->getChildNodes()->getLength() > 0)
|
||||
hasGrandchildren = true;
|
||||
if (!hasGrandchildren)
|
||||
_genericMetadata.insert(std::make_pair(tagString, Meta::GenericMetadata(element)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Metadata::parseContentNodeVersion1(const DOMElement* contentNode)
|
||||
{
|
||||
auto children = contentNode->getChildNodes();
|
||||
for (XMLSize_t i = 0; i < children->getLength(); ++i) {
|
||||
auto child = dynamic_cast<const DOMElement*>(children->item(i));
|
||||
if (child) {
|
||||
auto tag = StrXUTF8(child->getTagName()).str;
|
||||
_content.insert(std::make_pair(tag, Metadata(child, 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Meta::Contact::Contact(const std::string& name, const std::string& email) :
|
||||
name(name),
|
||||
email(email)
|
||||
{
|
||||
// This has to be provided manually since we have another constructor
|
||||
}
|
||||
|
||||
Meta::Contact::Contact(const XERCES_CPP_NAMESPACE::DOMElement* e)
|
||||
{
|
||||
auto emailAttribute = e->getAttribute(XUTF8Str("email").unicodeForm());
|
||||
name = StrXUTF8(e->getTextContent()).str;
|
||||
email = StrXUTF8(emailAttribute).str;
|
||||
}
|
||||
|
||||
Meta::License::License(const std::string& name, fs::path file) :
|
||||
name(name),
|
||||
file(file)
|
||||
{
|
||||
// This has to be provided manually since we have another constructor
|
||||
}
|
||||
|
||||
Meta::License::License(const XERCES_CPP_NAMESPACE::DOMElement* e)
|
||||
{
|
||||
auto fileAttribute = e->getAttribute(XUTF8Str("file").unicodeForm());
|
||||
if (XMLString::stringLen(fileAttribute) > 0) {
|
||||
file = fs::path(StrXUTF8(fileAttribute).str);
|
||||
}
|
||||
name = StrXUTF8(e->getTextContent()).str;
|
||||
}
|
||||
|
||||
Meta::Url::Url(const std::string& location, UrlType type) :
|
||||
location(location),
|
||||
type(type)
|
||||
{
|
||||
// This has to be provided manually since we have another constructor
|
||||
}
|
||||
|
||||
Meta::Url::Url(const XERCES_CPP_NAMESPACE::DOMElement* e)
|
||||
{
|
||||
auto typeAttribute = StrXUTF8(e->getAttribute(XUTF8Str("type").unicodeForm())).str;
|
||||
if (typeAttribute.empty() || typeAttribute == "website")
|
||||
type = UrlType::website;
|
||||
else if (typeAttribute == "bugtracker")
|
||||
type = UrlType::bugtracker;
|
||||
else if (typeAttribute == "repository")
|
||||
type = UrlType::repository;
|
||||
else if (typeAttribute == "readme")
|
||||
type = UrlType::readme;
|
||||
else if (typeAttribute == "documentation")
|
||||
type = UrlType::documentation;
|
||||
location = StrXUTF8(e->getTextContent()).str;
|
||||
}
|
||||
|
||||
Meta::Dependency::Dependency(const XERCES_CPP_NAMESPACE::DOMElement* e)
|
||||
{
|
||||
version_lt = StrXUTF8(e->getAttribute(XUTF8Str("version_lt").unicodeForm())).str;
|
||||
version_lte = StrXUTF8(e->getAttribute(XUTF8Str("version_lte").unicodeForm())).str;
|
||||
version_eq = StrXUTF8(e->getAttribute(XUTF8Str("version_eq").unicodeForm())).str;
|
||||
version_gte = StrXUTF8(e->getAttribute(XUTF8Str("version_gte").unicodeForm())).str;
|
||||
version_gt = StrXUTF8(e->getAttribute(XUTF8Str("version_gt").unicodeForm())).str;
|
||||
condition = StrXUTF8(e->getAttribute(XUTF8Str("condition").unicodeForm())).str;
|
||||
|
||||
package = StrXUTF8(e->getTextContent()).str;
|
||||
}
|
||||
|
||||
Meta::Version::Version() :
|
||||
major(0),
|
||||
minor(0),
|
||||
patch(0)
|
||||
{
|
||||
}
|
||||
|
||||
Meta::Version::Version(int major, int minor, int patch, const std::string& suffix) :
|
||||
major(major),
|
||||
minor(minor),
|
||||
patch(patch),
|
||||
suffix(suffix)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Meta::Version::Version(const std::string& versionString) :
|
||||
minor(0),
|
||||
patch(0)
|
||||
{
|
||||
std::istringstream stream(versionString);
|
||||
char separator;
|
||||
stream >> major;
|
||||
if (stream) stream >> separator;
|
||||
if (stream) stream >> minor;
|
||||
if (stream) stream >> separator;
|
||||
if (stream) stream >> patch;
|
||||
if (stream) stream >> suffix;
|
||||
}
|
||||
|
||||
std::string Meta::Version::str() const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << major << "." << minor << "." << patch << suffix;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
bool Meta::Version::operator<(const Version& rhs) const
|
||||
{
|
||||
return std::tie(major, minor, patch, suffix) < std::tie(rhs.major, rhs.minor, rhs.patch, rhs.suffix);
|
||||
}
|
||||
|
||||
bool Meta::Version::operator>(const Version& rhs) const
|
||||
{
|
||||
return std::tie(major, minor, patch, suffix) > std::tie(rhs.major, rhs.minor, rhs.patch, rhs.suffix);
|
||||
}
|
||||
|
||||
bool Meta::Version::operator<=(const Version& rhs) const
|
||||
{
|
||||
return std::tie(major, minor, patch, suffix) <= std::tie(rhs.major, rhs.minor, rhs.patch, rhs.suffix);
|
||||
}
|
||||
|
||||
bool Meta::Version::operator>=(const Version& rhs) const
|
||||
{
|
||||
return std::tie(major, minor, patch, suffix) >= std::tie(rhs.major, rhs.minor, rhs.patch, rhs.suffix);
|
||||
}
|
||||
|
||||
bool Meta::Version::operator==(const Version& rhs) const
|
||||
{
|
||||
return std::tie(major, minor, patch, suffix) == std::tie(rhs.major, rhs.minor, rhs.patch, rhs.suffix);
|
||||
}
|
||||
|
||||
bool Meta::Version::operator!=(const Version& rhs) const
|
||||
{
|
||||
return std::tie(major, minor, patch, suffix) != std::tie(rhs.major, rhs.minor, rhs.patch, rhs.suffix);
|
||||
}
|
||||
|
||||
Meta::GenericMetadata::GenericMetadata(const XERCES_CPP_NAMESPACE::DOMElement* e)
|
||||
{
|
||||
contents = StrXUTF8(e->getTextContent()).str;
|
||||
for (XMLSize_t i = 0; i < e->getAttributes()->getLength(); ++i) {
|
||||
auto a = e->getAttributes()->item(i);
|
||||
attributes.insert(std::make_pair(StrXUTF8(a->getNodeName()).str,
|
||||
StrXUTF8(a->getTextContent()).str));
|
||||
}
|
||||
}
|
||||
|
||||
App::Meta::GenericMetadata::GenericMetadata(const std::string& contents) :
|
||||
contents(contents)
|
||||
{
|
||||
}
|
||||
299
src/App/Metadata.h
Normal file
299
src/App/Metadata.h
Normal file
@@ -0,0 +1,299 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
|
||||
* *
|
||||
* 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 LICENSE.html. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef BASE_METADATAREADER_H
|
||||
#define BASE_METADATAREADER_H
|
||||
|
||||
#include "FCConfig.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <xercesc/dom/DOM.hpp>
|
||||
#include <xercesc/parsers/XercesDOMParser.hpp>
|
||||
|
||||
|
||||
namespace App {
|
||||
|
||||
namespace Meta {
|
||||
|
||||
/**
|
||||
* \struct Contact
|
||||
* \brief A person or company representing a point of contact for the package (either author or maintainer).
|
||||
*/
|
||||
struct AppExport Contact {
|
||||
Contact() = default;
|
||||
Contact(const std::string& name, const std::string& email);
|
||||
explicit Contact(const XERCES_CPP_NAMESPACE::DOMElement* e);
|
||||
std::string name; //< Contact name - required
|
||||
std::string email; //< Contact email - may be optional
|
||||
};
|
||||
|
||||
/**
|
||||
* \struct License
|
||||
* \brief A license that covers some or all of this package.
|
||||
*
|
||||
* Many licenses also require the inclusion of the complete license text, specified in this struct
|
||||
* using the "file" member.
|
||||
*/
|
||||
struct AppExport License {
|
||||
License() = default;
|
||||
License(const std::string& name, boost::filesystem::path file);
|
||||
explicit License(const XERCES_CPP_NAMESPACE::DOMElement* e);
|
||||
std::string name; //< Short name of license, e.g. "LGPL2", "MIT", "Mozilla Public License", etc.
|
||||
boost::filesystem::path file; //< Optional path to the license file, relative to the XML file's location
|
||||
};
|
||||
|
||||
enum class UrlType {
|
||||
website,
|
||||
repository,
|
||||
bugtracker,
|
||||
readme,
|
||||
documentation
|
||||
};
|
||||
|
||||
/**
|
||||
* \struct Url
|
||||
* \brief A URL, including type information (e.g. website, repository, or bugtracker, in package.xml v3)
|
||||
*/
|
||||
struct AppExport Url {
|
||||
Url() = default;
|
||||
Url(const std::string& location, UrlType type);
|
||||
explicit Url(const XERCES_CPP_NAMESPACE::DOMElement* e);
|
||||
std::string location; //< The actual URL, including protocol
|
||||
UrlType type; //< What kind of URL this is
|
||||
};
|
||||
|
||||
/**
|
||||
* \struct Version
|
||||
* A semantic version structure providing comparison operators and conversion to and from std::string
|
||||
*/
|
||||
struct AppExport Version {
|
||||
Version();
|
||||
Version(int major, int minor = 0, int patch = 0, const std::string& suffix = std::string());
|
||||
explicit Version(const std::string& semanticString);
|
||||
|
||||
int major;
|
||||
int minor;
|
||||
int patch;
|
||||
std::string suffix;
|
||||
|
||||
std::string str() const;
|
||||
|
||||
bool operator<(const Version&) const;
|
||||
bool operator>(const Version&) const;
|
||||
bool operator<=(const Version&) const;
|
||||
bool operator>=(const Version&) const;
|
||||
bool operator==(const Version&) const;
|
||||
bool operator!=(const Version&) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* \struct Dependency
|
||||
* \brief Another package that this package depends on, conflicts with, or replaces
|
||||
*/
|
||||
struct AppExport Dependency {
|
||||
Dependency() = default;
|
||||
explicit Dependency(const XERCES_CPP_NAMESPACE::DOMElement* e);
|
||||
std::string package; //< Required: must exactly match the contents of the "name" element in the referenced package's package.xml file.
|
||||
std::string version_lt; //< Optional: The dependency to the package is restricted to versions less than the stated version number.
|
||||
std::string version_lte; //< Optional: The dependency to the package is restricted to versions less or equal than the stated version number.
|
||||
std::string version_eq; //< Optional: The dependency to the package is restricted to a version equal than the stated version number.
|
||||
std::string version_gte; //< Optional: The dependency to the package is restricted to versions greater or equal than the stated version number.
|
||||
std::string version_gt; //< Optional: The dependency to the package is restricted to versions greater than the stated version number.
|
||||
std::string condition; //< Optional: Conditional expression as documented in REP149.
|
||||
};
|
||||
|
||||
/**
|
||||
* \struct GenericMetadata
|
||||
* A structure to hold unrecognized single-level metadata.
|
||||
*
|
||||
* Most unrecognized metadata is simple: when parsing the XML, if the parser finds a tag it
|
||||
* does not recognize, and that tag has no children, it is parsed into this data structure
|
||||
* for convenient access by client code.
|
||||
*/
|
||||
struct AppExport GenericMetadata {
|
||||
GenericMetadata() = default;
|
||||
explicit GenericMetadata(const XERCES_CPP_NAMESPACE::DOMElement* e);
|
||||
explicit GenericMetadata(const std::string &contents);
|
||||
std::string contents; //< The contents of the tag
|
||||
std::map<std::string, std::string> attributes; //< The XML attributes of the tag
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* \class Metadata
|
||||
* \brief Reads data from a metadata file.
|
||||
*
|
||||
* The metadata format is based on https://ros.org/reps/rep-0149.html, modified for FreeCAD
|
||||
* use. Full format documentation is available at the FreeCAD Wiki:
|
||||
* https://wiki.freecadweb.org/Package_Metadata
|
||||
*/
|
||||
class AppExport Metadata {
|
||||
public:
|
||||
|
||||
Metadata();
|
||||
|
||||
/**
|
||||
* Read the data from a file on disk
|
||||
*
|
||||
* This constructor takes a path to an XML file and loads the XML from that file as
|
||||
* metadata.
|
||||
*/
|
||||
explicit Metadata(const boost::filesystem::path& metadataFile);
|
||||
|
||||
/**
|
||||
* Construct a Metadata object from a DOM node.
|
||||
*
|
||||
* This node may have any tag name: it is only accessed via its children, which are
|
||||
* expected to follow the standard Metadata format for the contents of the <package> element.
|
||||
*/
|
||||
Metadata(const XERCES_CPP_NAMESPACE::DOMNode* domNode, int format);
|
||||
|
||||
~Metadata();
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Recognized Metadata
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
std::string name() const; //< A short name for this package, often used as a menu entry.
|
||||
Meta::Version version() const; //< Version string in symantic triplet format, e.g. "1.2.3".
|
||||
std::string description() const; //< Text-only description of the package. No markup.
|
||||
std::vector<Meta::Contact> maintainer() const; //< Must be at least one, and must specify an email address.
|
||||
std::vector<Meta::License> license() const; //< Must be at least one, and most licenses require including a license file.
|
||||
std::vector<Meta::Url> url() const; //< Any number of URLs may be specified, but at least one repository URL must be included at the package level.
|
||||
std::vector<Meta::Contact> author() const; //< Any number of authors may be specified, and email addresses are optional.
|
||||
std::vector<Meta::Dependency> depend() const; //< Zero or more packages this package requires prior to use.
|
||||
std::vector<Meta::Dependency> conflict() const; //< Zero of more packages this package conflicts with.
|
||||
std::vector<Meta::Dependency> replace() const; //< Zero or more packages this package is intended to replace.
|
||||
std::vector<std::string> tag() const; //< Zero or more text tags related to this package.
|
||||
boost::filesystem::path icon() const; //< Path to an icon file.
|
||||
std::string classname() const; //< Recognized for convenience -- generally only used by Workbenches.
|
||||
std::vector<boost::filesystem::path> file() const; //< Arbitrary files associated with this package or content item.
|
||||
Meta::Version freecadmin() const; //< The minimum FreeCAD version.
|
||||
Meta::Version freecadmax() const; //< The maximum FreeCAD version.
|
||||
|
||||
/**
|
||||
* Access the metadata for the content elements of this package
|
||||
*
|
||||
* In addition to the overall package metadata, this class reads in metadata contained in a
|
||||
* <content> element. Each entry in the content element is an element representing some
|
||||
* type of package content (e.g. add-on, macro, theme, etc.). This class places no restriction
|
||||
* on the types, it is up to client code to place requirements on the metadata included
|
||||
* here.
|
||||
*
|
||||
* For example, themes might be specified:
|
||||
* <content>
|
||||
* <theme>
|
||||
* <name>High Contrast</name>
|
||||
* </theme>
|
||||
* </content>
|
||||
*/
|
||||
std::multimap<std::string, Metadata> content() const;
|
||||
|
||||
/**
|
||||
* Convenience accessor for unrecognized simple metadata.
|
||||
*
|
||||
* If the XML parser encounters tags that it does not recognize, and those tags have
|
||||
* no children, a GenericMetadata object is created. Those objects can be accessed using
|
||||
* operator[], which returns a (potentially empty) vector containing all instances of the
|
||||
* given tag. It cannot be used to *create* a new tag, however. See addGenericMetadata().
|
||||
*/
|
||||
std::vector<Meta::GenericMetadata> operator[] (const std::string& tag) const;
|
||||
|
||||
/**
|
||||
* Directly access the DOM tree to support unrecognized multi-level metadata
|
||||
*/
|
||||
XERCES_CPP_NAMESPACE::DOMElement* dom() const;
|
||||
|
||||
|
||||
// Setters
|
||||
void setName(const std::string& name);
|
||||
void setVersion(const Meta::Version& version);
|
||||
void setDescription(const std::string& description);
|
||||
void addMaintainer(const Meta::Contact& maintainer);
|
||||
void addLicense(const Meta::License& license);
|
||||
void addUrl(const Meta::Url& url);
|
||||
void addAuthor(const Meta::Contact& author);
|
||||
void addDepend(const Meta::Dependency& dep);
|
||||
void addConflict(const Meta::Dependency& dep);
|
||||
void addReplace(const Meta::Dependency& dep);
|
||||
void addTag(const std::string& tag);
|
||||
void setIcon(const boost::filesystem::path& path);
|
||||
void setClassname(const std::string& name);
|
||||
void addFile(const boost::filesystem::path& path);
|
||||
void addContentItem(const std::string& tag, const Metadata& item);
|
||||
void setFreeCADMin(const Meta::Version& version);
|
||||
void setFreeCADMax(const Meta::Version& version);
|
||||
void addGenericMetadata(const std::string& tag, const Meta::GenericMetadata& genericMetadata);
|
||||
|
||||
/**
|
||||
* Write the metadata to an XML file
|
||||
*/
|
||||
void write(const boost::filesystem::path& file) const;
|
||||
|
||||
/**
|
||||
* Determine whether this package satisfies the given dependency
|
||||
*/
|
||||
bool satisfies(const Meta::Dependency&);
|
||||
|
||||
private:
|
||||
|
||||
std::string _name;
|
||||
Meta::Version _version;
|
||||
std::string _description;
|
||||
std::vector<Meta::Contact> _maintainer;
|
||||
std::vector<Meta::License> _license;
|
||||
std::vector<Meta::Url> _url;
|
||||
std::vector<Meta::Contact> _author;
|
||||
std::vector<Meta::Dependency> _depend;
|
||||
std::vector<Meta::Dependency> _conflict;
|
||||
std::vector<Meta::Dependency> _replace;
|
||||
std::vector<std::string> _tag;
|
||||
boost::filesystem::path _icon;
|
||||
std::string _classname;
|
||||
std::vector<boost::filesystem::path> _file;
|
||||
Meta::Version _freecadmin;
|
||||
Meta::Version _freecadmax;
|
||||
|
||||
std::multimap<std::string, Metadata> _content;
|
||||
|
||||
std::multimap<std::string, Meta::GenericMetadata> _genericMetadata;
|
||||
|
||||
XERCES_CPP_NAMESPACE::DOMElement* _dom;
|
||||
std::shared_ptr<XERCES_CPP_NAMESPACE::XercesDOMParser> _parser;
|
||||
|
||||
void parseVersion1(const XERCES_CPP_NAMESPACE::DOMNode* startNode);
|
||||
void parseContentNodeVersion1(const XERCES_CPP_NAMESPACE::DOMElement* contentNode);
|
||||
|
||||
void appendToElement(XERCES_CPP_NAMESPACE::DOMElement* root) const;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
156
src/App/MetadataPy.xml
Normal file
156
src/App/MetadataPy.xml
Normal file
@@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
|
||||
<PythonExport
|
||||
Father="PyObjectBase"
|
||||
Name="MetadataPy"
|
||||
Twin="Metadata"
|
||||
TwinPointer="Metadata"
|
||||
Include="App/Metadata.h"
|
||||
FatherInclude="Base/PyObjectBase.h"
|
||||
Namespace="App"
|
||||
Constructor="true"
|
||||
Delete="true"
|
||||
NumberProtocol="false"
|
||||
RichCompare="false"
|
||||
FatherNamespace="Base">
|
||||
|
||||
<Documentation>
|
||||
<Author Licence="LGPL" Name="Chris Hennes" EMail="chennes@pioneerlibrarysystem.org" />
|
||||
<UserDocu>
|
||||
Metadata
|
||||
A Metadata object reads an XML-formatted package metadata file and provides read-only access to its contents.
|
||||
|
||||
A single constructor is supported:
|
||||
Metadata(file) -- Reads the XML file and provides access to the metadata it specifies.
|
||||
</UserDocu>
|
||||
<DeveloperDocu>Metadata</DeveloperDocu>
|
||||
</Documentation>
|
||||
|
||||
<Attribute Name="Name" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>String: the name of this item</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Name" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="Version" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>String: the version of this item in semantic triplet format</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Version" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="Description" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>String: the description of this item (text only, no markup allowed)</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Description" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="Maintainer" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>List of maintainer objects with 'name' and 'email' string attributes</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Maintainer" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="License" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>List of applicable licenses as objects with 'name' and 'file' string attributes</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="License" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="Url" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
List of URLs as objects with 'location' and 'urltype' string attributes, where urltype is one of:
|
||||
* website
|
||||
* repository
|
||||
* bugtracker
|
||||
* readme
|
||||
* documentation
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Url" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="Author" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>List of author objects, each with a 'name' and a (potentially empty) 'email' string attribute</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Author" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="Depend" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
List of dependencies, as objects with the following attributes:
|
||||
* package -- Required: must exactly match the contents of the 'name' element in the referenced package's package.xml file
|
||||
* version_lt -- Optional: The dependency to the package is restricted to versions less than the stated version number
|
||||
* version_lte -- Optional: The dependency to the package is restricted to versions less or equal than the stated version number
|
||||
* version_eq -- Optional: The dependency to the package is restricted to a version equal than the stated version number
|
||||
* version_gte -- Optional: The dependency to the package is restricted to versions greater or equal than the stated version number
|
||||
* version_gt -- Optional: The dependency to the package is restricted to versions greater than the stated version number
|
||||
* condition -- Optional: Conditional expression as documented in REP149
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Depend" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="Conflict" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>List of conflicts, format identical to dependencies</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Conflict" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="Replace" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>List of things this item is considered by its author to replace: format identical to dependencies</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Replace" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="Tag" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>List of strings</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Tag" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="Icon" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Relative path to an icon file</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Icon" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="Classname" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>String: the name of the main Python class this item creates/represents</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Classname" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="File" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>A list of files associated with this item -- the meaning of each file is implementation-defined</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="File" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="Content" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>A dictionary of lists of content items: defined recursively, each item is itself a Metadata object -- see package.xml file format documentation for details</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Content" Type="Object" />
|
||||
</Attribute>
|
||||
|
||||
|
||||
<Methode Name="getGenericMetadata">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
getGenericMetadata(name)
|
||||
Get the list of GenericMetadata objects with key 'name'. Generic metadata objects are Python objects with
|
||||
a string 'contents' and a dictionary of strings, 'attributes'. They represent unrecognized simple XML tags
|
||||
in the metadata file.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
|
||||
<ClassDeclarations>
|
||||
public:
|
||||
MetadataPy(const Metadata & pla, PyTypeObject *T = &Type)
|
||||
:PyObjectBase(new Metadata(pla),T){}
|
||||
Metadata value() const
|
||||
{ return *(getMetadataPtr()); }
|
||||
</ClassDeclarations>
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
285
src/App/MetadataPyImp.cpp
Normal file
285
src/App/MetadataPyImp.cpp
Normal file
@@ -0,0 +1,285 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
|
||||
* *
|
||||
* 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 LICENSE.html. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#include "Metadata.h"
|
||||
|
||||
// inclusion of the generated files (generated out of MetadataPy.xml)
|
||||
#include "MetadataPy.h"
|
||||
#include "MetadataPy.cpp"
|
||||
|
||||
using namespace Base;
|
||||
|
||||
// Returns a string which represents the object e.g. when printed in Python
|
||||
std::string MetadataPy::representation(void) const
|
||||
{
|
||||
MetadataPy::PointerType ptr = reinterpret_cast<MetadataPy::PointerType>(_pcTwinPointer);
|
||||
std::stringstream str;
|
||||
str << "Metadata [Name=(";
|
||||
str << ptr->name();
|
||||
str << "), Description=(";
|
||||
str << ptr->description();
|
||||
str << "), Maintainer=(";
|
||||
str << ptr->maintainer().front().name << ")]";
|
||||
|
||||
return str.str();
|
||||
}
|
||||
|
||||
PyObject* MetadataPy::PyMake(struct _typeobject*, PyObject* args, PyObject*) // Python wrapper
|
||||
{
|
||||
// create a new instance of MetadataPy and the Twin object
|
||||
const char* filename;
|
||||
if (!PyArg_ParseTuple(args, "s!", &filename))
|
||||
return nullptr;
|
||||
|
||||
return new MetadataPy(new Metadata(filename));
|
||||
}
|
||||
|
||||
// constructor method
|
||||
int MetadataPy::PyInit(PyObject* args, PyObject* /*kwd*/)
|
||||
{
|
||||
if (PyArg_ParseTuple(args, "")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Main class constructor -- takes a file path, loads the metadata from it
|
||||
PyErr_Clear();
|
||||
const char* file;
|
||||
if (PyArg_ParseTuple(args, "s!", &file)) {
|
||||
App::Metadata* a = new Metadata(file);
|
||||
*(getMetadataPtr()) = *a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy constructor
|
||||
PyErr_Clear();
|
||||
PyObject* o;
|
||||
if (PyArg_ParseTuple(args, "O!", &(App::MetadataPy::Type), &o)) {
|
||||
App::Metadata* a = static_cast<App::MetadataPy*>(o)->getMetadataPtr();
|
||||
*(getMetadataPtr()) = *a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyErr_SetString(Base::BaseExceptionFreeCADError, "path to metadata file expected");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py::Object MetadataPy::getName(void) const
|
||||
{
|
||||
return Py::String(getMetadataPtr()->name());
|
||||
}
|
||||
|
||||
Py::Object MetadataPy::getVersion(void) const
|
||||
{
|
||||
return Py::String(getMetadataPtr()->version().str());
|
||||
}
|
||||
|
||||
Py::Object MetadataPy::getDescription(void) const
|
||||
{
|
||||
return Py::String(getMetadataPtr()->description());
|
||||
}
|
||||
|
||||
Py::Object MetadataPy::getMaintainer(void) const
|
||||
{
|
||||
auto maintainers = getMetadataPtr()->maintainer();
|
||||
Py::List pyMaintainers;
|
||||
for (const auto& m : maintainers) {
|
||||
Py::Object pyMaintainer;
|
||||
pyMaintainer.setAttr("name", Py::String(m.name));
|
||||
pyMaintainer.setAttr("email", Py::String(m.email));
|
||||
pyMaintainers.append(pyMaintainer);
|
||||
}
|
||||
return pyMaintainers;
|
||||
}
|
||||
|
||||
Py::Object MetadataPy::getAuthor(void) const
|
||||
{
|
||||
auto authors = getMetadataPtr()->author();
|
||||
Py::List pyAuthors;
|
||||
for (const auto& a : authors) {
|
||||
Py::Object pyAuthor;
|
||||
pyAuthor.setAttr("name", Py::String(a.name));
|
||||
pyAuthor.setAttr("email", Py::String(a.email));
|
||||
pyAuthors.append(pyAuthor);
|
||||
}
|
||||
return pyAuthors;
|
||||
}
|
||||
|
||||
Py::Object MetadataPy::getLicense(void) const
|
||||
{
|
||||
auto licenses = getMetadataPtr()->license();
|
||||
Py::List pyLicenses;
|
||||
for (const auto& lic : licenses) {
|
||||
Py::Object pyLicense;
|
||||
pyLicense.setAttr("name", Py::String(lic.name));
|
||||
pyLicense.setAttr("file", Py::String(lic.file.string()));
|
||||
pyLicenses.append(pyLicense);
|
||||
}
|
||||
return pyLicenses;
|
||||
}
|
||||
|
||||
Py::Object MetadataPy::getUrl(void) const
|
||||
{
|
||||
auto urls = getMetadataPtr()->url ();
|
||||
Py::List pyUrls;
|
||||
for (const auto& url : urls) {
|
||||
Py::Object pyUrl;
|
||||
pyUrl.setAttr("location", Py::String(url.location));
|
||||
switch (url.type) {
|
||||
case Meta::UrlType::website: pyUrl.setAttr("type", Py::String("website")); break;
|
||||
case Meta::UrlType::repository: pyUrl.setAttr("type", Py::String("repository")); break;
|
||||
case Meta::UrlType::bugtracker: pyUrl.setAttr("type", Py::String("bugtracker")); break;
|
||||
case Meta::UrlType::readme: pyUrl.setAttr("type", Py::String("readme")); break;
|
||||
case Meta::UrlType::documentation: pyUrl.setAttr("type", Py::String("documentation")); break;
|
||||
}
|
||||
pyUrls.append(pyUrl);
|
||||
}
|
||||
return pyUrls;
|
||||
}
|
||||
|
||||
Py::Object dependencyToPyObject(const Meta::Dependency& d)
|
||||
{
|
||||
Py::Object pyDependency;
|
||||
pyDependency.setAttr("package",Py::String(d.package));
|
||||
pyDependency.setAttr("version_lt", Py::String(d.version_lt));
|
||||
pyDependency.setAttr("version_lte", Py::String(d.version_lte));
|
||||
pyDependency.setAttr("version_eq", Py::String(d.version_eq));
|
||||
pyDependency.setAttr("version_gt", Py::String(d.version_gt));
|
||||
pyDependency.setAttr("version_gte", Py::String(d.version_gte));
|
||||
pyDependency.setAttr("condition", Py::String(d.condition));
|
||||
return pyDependency;
|
||||
}
|
||||
|
||||
Py::Object MetadataPy::getDepend(void) const
|
||||
{
|
||||
auto dependencies = getMetadataPtr()->depend();
|
||||
Py::List pyDependencies;
|
||||
for (const auto& d : dependencies) {
|
||||
pyDependencies.append(dependencyToPyObject(d));
|
||||
}
|
||||
return pyDependencies;
|
||||
}
|
||||
|
||||
Py::Object MetadataPy::getConflict(void) const
|
||||
{
|
||||
auto dependencies = getMetadataPtr()->conflict();
|
||||
Py::List pyDependencies;
|
||||
for (const auto& d : dependencies) {
|
||||
pyDependencies.append(dependencyToPyObject(d));
|
||||
}
|
||||
return pyDependencies;
|
||||
}
|
||||
|
||||
Py::Object MetadataPy::getReplace(void) const
|
||||
{
|
||||
auto dependencies = getMetadataPtr()->replace();
|
||||
Py::List pyDependencies;
|
||||
for (const auto& d : dependencies) {
|
||||
pyDependencies.append(dependencyToPyObject(d));
|
||||
}
|
||||
return pyDependencies;
|
||||
}
|
||||
|
||||
// Tag, icon, classname, file
|
||||
|
||||
Py::Object MetadataPy::getTag(void) const
|
||||
{
|
||||
auto tags = getMetadataPtr()->tag();
|
||||
Py::List pyTags;
|
||||
for (const auto& t : tags) {
|
||||
pyTags.append(Py::String(t));
|
||||
}
|
||||
return pyTags;
|
||||
}
|
||||
|
||||
Py::Object MetadataPy::getIcon(void) const
|
||||
{
|
||||
return Py::String(getMetadataPtr()->icon().string());
|
||||
}
|
||||
|
||||
Py::Object MetadataPy::getClassname(void) const
|
||||
{
|
||||
return Py::String(getMetadataPtr()->classname());
|
||||
}
|
||||
|
||||
Py::Object MetadataPy::getFile(void) const
|
||||
{
|
||||
auto files = getMetadataPtr()->file();
|
||||
Py::List pyFiles;
|
||||
for (const auto& f : files) {
|
||||
pyFiles.append(Py::String(f.string()));
|
||||
}
|
||||
return pyFiles;
|
||||
}
|
||||
|
||||
Py::Object MetadataPy::getContent(void) const
|
||||
{
|
||||
auto content = getMetadataPtr()->content();
|
||||
std::set<std::string> keys;
|
||||
for (const auto& item : content) {
|
||||
keys.insert(item.first);
|
||||
}
|
||||
|
||||
// For the Python, we'll use a dictionary of lists to store the content components:
|
||||
Py::Dict pyContent;
|
||||
for (const auto& key : keys) {
|
||||
Py::List pyContentForKey;
|
||||
auto elements = content.equal_range(key);
|
||||
for (auto element = elements.first; element != elements.second; ++element) {
|
||||
auto contentMetadataItem = new MetadataPy(new Metadata(element->second));
|
||||
pyContentForKey.append(Py::asObject(contentMetadataItem));
|
||||
}
|
||||
pyContent[key] = pyContentForKey;
|
||||
}
|
||||
return pyContent;
|
||||
}
|
||||
|
||||
PyObject* MetadataPy::getGenericMetadata(PyObject* args)
|
||||
{
|
||||
const char* name;
|
||||
if (!PyArg_ParseTuple(args, "s!", &name))
|
||||
return NULL;
|
||||
auto gm = (*getMetadataPtr())[name];
|
||||
auto pyGenericMetadata = new Py::List;
|
||||
for (const auto& item : gm) {
|
||||
Py::Object pyItem;
|
||||
pyItem.setAttr("contents", Py::String(item.contents));
|
||||
Py::Dict pyAttributes;
|
||||
for (const auto& attribute : item.attributes) {
|
||||
pyAttributes[attribute.first] = Py::String(attribute.second);
|
||||
}
|
||||
pyItem.setAttr("attributes", pyAttributes);
|
||||
pyGenericMetadata->append(pyItem);
|
||||
}
|
||||
return pyGenericMetadata->ptr();
|
||||
}
|
||||
|
||||
PyObject* MetadataPy::getCustomAttributes(const char* /*attr*/) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MetadataPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -754,6 +754,8 @@ std::vector<std::string> ParameterGrp::GetASCIIs(const char * sFilter) const
|
||||
DOMNode *pcElem2 = pcTemp->getFirstChild();
|
||||
if (pcElem2)
|
||||
vrValues.emplace_back(StrXUTF8(pcElem2->getNodeValue()).c_str() );
|
||||
else
|
||||
vrValues.emplace_back(""); // For a string, an empty value is possible and allowed
|
||||
}
|
||||
pcTemp = FindNextElement(pcTemp,"FCText");
|
||||
}
|
||||
@@ -776,6 +778,8 @@ std::vector<std::pair<std::string,std::string> > ParameterGrp::GetASCIIMap(const
|
||||
DOMNode *pcElem2 = pcTemp->getFirstChild();
|
||||
if (pcElem2)
|
||||
vrValues.emplace_back(Name, std::string(StrXUTF8(pcElem2->getNodeValue()).c_str()));
|
||||
else
|
||||
vrValues.emplace_back(Name, std::string()); // For a string, an empty value is possible and allowed
|
||||
}
|
||||
pcTemp = FindNextElement(pcTemp,"FCText");
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#endif
|
||||
|
||||
#include <boost/interprocess/sync/file_lock.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <QtOpenGL.h>
|
||||
#if defined(HAVE_QT5_OPENGL)
|
||||
#include <QWindow>
|
||||
@@ -73,6 +74,7 @@
|
||||
#include "WidgetFactory.h"
|
||||
#include "Command.h"
|
||||
#include "Macro.h"
|
||||
#include "PreferencePackManager.h"
|
||||
#include "ProgressBar.h"
|
||||
#include "Workbench.h"
|
||||
#include "WorkbenchManager.h"
|
||||
@@ -160,6 +162,9 @@ struct ApplicationP
|
||||
macroMngr = new MacroManager();
|
||||
else
|
||||
macroMngr = nullptr;
|
||||
|
||||
// Create the Theme Manager
|
||||
prefPackManager = new PreferencePackManager();
|
||||
}
|
||||
|
||||
~ApplicationP()
|
||||
@@ -173,6 +178,7 @@ struct ApplicationP
|
||||
Gui::Document* activeDocument;
|
||||
Gui::Document* editDocument;
|
||||
MacroManager* macroMngr;
|
||||
PreferencePackManager* prefPackManager;
|
||||
/// List of all registered views
|
||||
std::list<Gui::BaseView*> passive;
|
||||
bool isClosing;
|
||||
@@ -1661,6 +1667,12 @@ CommandManager &Application::commandManager(void)
|
||||
return d->commandManager;
|
||||
}
|
||||
|
||||
Gui::PreferencePackManager* Application::prefPackManager(void)
|
||||
{
|
||||
return d->prefPackManager;
|
||||
}
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// Init, Destruct and singleton
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ class MacroManager;
|
||||
class MDIView;
|
||||
class MainWindow;
|
||||
class MenuItem;
|
||||
class PreferencePackManager;
|
||||
class ViewProvider;
|
||||
class ViewProviderDocumentObject;
|
||||
|
||||
@@ -219,6 +220,8 @@ public:
|
||||
void createStandardOperations();
|
||||
//@}
|
||||
|
||||
Gui::PreferencePackManager* prefPackManager(void);
|
||||
|
||||
/** @name Init, Destruct an Access methods */
|
||||
//@{
|
||||
/// some kind of singelton
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#add_subdirectory(Icons)
|
||||
add_subdirectory(Stylesheets)
|
||||
add_subdirectory(PreferencePacks)
|
||||
add_subdirectory(PreferencePackTemplates)
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-DFCGui -DQIIS_MAKEDLL -DQSINT_MAKEDLL -DOVR_OS_WIN32 -DQUARTER_INTERNAL -DQUARTER_MAKE_DLL -DCOIN_DLL)
|
||||
@@ -306,6 +308,7 @@ SET(Gui_UIC_SRCS
|
||||
DlgAuthorization.ui
|
||||
DlgChooseIcon.ui
|
||||
DlgCommands.ui
|
||||
DlgCreateNewPreferencePack.ui
|
||||
DlgCustomizeSpNavSettings.ui
|
||||
DlgDisplayProperties.ui
|
||||
DlgEditor.ui
|
||||
@@ -403,6 +406,7 @@ SET(Dialog_CPP_SRCS
|
||||
Clipping.cpp
|
||||
DemoMode.cpp
|
||||
DlgActivateWindowImp.cpp
|
||||
DlgCreateNewPreferencePackImp.cpp
|
||||
DlgUnitsCalculatorImp.cpp
|
||||
DlgDisplayPropertiesImp.cpp
|
||||
DlgInputDialogImp.cpp
|
||||
@@ -440,6 +444,7 @@ SET(Dialog_HPP_SRCS
|
||||
Clipping.h
|
||||
DemoMode.h
|
||||
DlgActivateWindowImp.h
|
||||
DlgCreateNewPreferencePackImp.h
|
||||
DlgUnitsCalculatorImp.h
|
||||
DlgDisplayPropertiesImp.h
|
||||
DlgInputDialogImp.h
|
||||
@@ -499,6 +504,7 @@ SET(Dialog_SRCS
|
||||
DlgCheckableMessageBox.ui
|
||||
DlgTreeWidget.ui
|
||||
DlgExpressionInput.ui
|
||||
DlgCreateNewPreferencePack.ui
|
||||
DownloadManager.ui
|
||||
DownloadItem.ui
|
||||
DocumentRecovery.ui
|
||||
@@ -1143,6 +1149,7 @@ SET(FreeCADGui_CPP_SRCS
|
||||
resource.cpp
|
||||
Control.cpp
|
||||
SpaceballEvent.cpp
|
||||
PreferencePackManager.cpp
|
||||
Thumbnail.cpp
|
||||
Utilities.cpp
|
||||
WaitCursor.cpp
|
||||
@@ -1175,6 +1182,7 @@ SET(FreeCADGui_SRCS
|
||||
Qt4All.h
|
||||
Control.h
|
||||
SpaceballEvent.h
|
||||
PreferencePackManager.h
|
||||
Thumbnail.h
|
||||
Utilities.h
|
||||
WaitCursor.h
|
||||
|
||||
105
src/Gui/DlgCreateNewPreferencePack.ui
Normal file
105
src/Gui/DlgCreateNewPreferencePack.ui
Normal file
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Gui::Dialog::DlgCreateNewPreferencePack</class>
|
||||
<widget class="QDialog" name="Gui::Dialog::DlgCreateNewPreferencePack">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>580</width>
|
||||
<height>520</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Create New Preference Pack</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="treeWidget">
|
||||
<property name="columnCount">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<attribute name="headerMinimumSectionSize">
|
||||
<number>50</number>
|
||||
</attribute>
|
||||
<attribute name="headerDefaultSectionSize">
|
||||
<number>250</number>
|
||||
</attribute>
|
||||
<attribute name="headerShowSortIndicator" stdset="0">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Property group templates</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Gui::Dialog::DlgCreateNewPreferencePack</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Gui::Dialog::DlgCreateNewPreferencePack</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
168
src/Gui/DlgCreateNewPreferencePackImp.cpp
Normal file
168
src/Gui/DlgCreateNewPreferencePackImp.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
|
||||
* *
|
||||
* 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_
|
||||
#endif
|
||||
|
||||
#include "DlgCreateNewPreferencePackImp.h"
|
||||
#include "ui_DlgCreateNewPreferencePack.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
|
||||
using namespace Gui::Dialog;
|
||||
|
||||
const auto TemplateRole = Qt::UserRole;
|
||||
|
||||
/* TRANSLATOR Gui::Dialog::DlgCreateNewPreferencePackImp */
|
||||
|
||||
/**
|
||||
* Constructs a Gui::Dialog::DlgCreateNewPreferencePackImp as a child of 'parent'
|
||||
*/
|
||||
DlgCreateNewPreferencePackImp::DlgCreateNewPreferencePackImp(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui_DlgCreateNewPreferencePack)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
QRegExp validNames(QString::fromUtf8("[^/\\\\?%*:|\"<>]+"));
|
||||
_nameValidator.setRegExp(validNames);
|
||||
ui->lineEdit->setValidator(&_nameValidator);
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
connect(ui->treeWidget, &QTreeWidget::itemChanged, this, &DlgCreateNewPreferencePackImp::onItemChanged);
|
||||
}
|
||||
|
||||
|
||||
DlgCreateNewPreferencePackImp::~DlgCreateNewPreferencePackImp()
|
||||
{
|
||||
}
|
||||
|
||||
void DlgCreateNewPreferencePackImp::setPreferencePackTemplates(const std::vector<Gui::PreferencePackManager::TemplateFile>& availableTemplates)
|
||||
{
|
||||
ui->treeWidget->clear();
|
||||
_groups.clear();
|
||||
|
||||
ui->treeWidget->header()->setDefaultSectionSize(250);
|
||||
|
||||
_templates = availableTemplates;
|
||||
for (const auto &t : _templates) {
|
||||
|
||||
QTreeWidgetItem* group;
|
||||
if (auto foundGroup = _groups.find(t.group); foundGroup != _groups.end()) {
|
||||
group = foundGroup->second;
|
||||
}
|
||||
else {
|
||||
group = new QTreeWidgetItem(ui->treeWidget, QStringList(QString::fromStdString(t.group)));
|
||||
group->setCheckState(0, Qt::Checked);
|
||||
group->setExpanded(true);
|
||||
_groups.insert(std::make_pair(t.group, group));
|
||||
}
|
||||
|
||||
QStringList itemColumns;
|
||||
itemColumns.push_back(QString::fromStdString(t.name));
|
||||
auto newItem = new QTreeWidgetItem(group, itemColumns);
|
||||
newItem->setCheckState(0, Qt::Checked);
|
||||
if (group->checkState(0) != newItem->checkState(0))
|
||||
group->setCheckState(0, Qt::PartiallyChecked);
|
||||
newItem->setData(0, TemplateRole, QVariant::fromValue(t));
|
||||
group->addChild(newItem);
|
||||
}
|
||||
}
|
||||
|
||||
void Gui::Dialog::DlgCreateNewPreferencePackImp::setPreferencePackNames(const std::vector<std::string>& usedNames)
|
||||
{
|
||||
_existingPackNames = usedNames;
|
||||
}
|
||||
|
||||
std::vector<Gui::PreferencePackManager::TemplateFile> DlgCreateNewPreferencePackImp::selectedTemplates() const
|
||||
{
|
||||
std::vector<Gui::PreferencePackManager::TemplateFile> results;
|
||||
|
||||
for (const auto& group : _groups)
|
||||
for (int childIndex = 0; childIndex < group.second->childCount(); ++childIndex)
|
||||
if (auto child = group.second->child(childIndex); child->checkState(0) == Qt::Checked)
|
||||
if (child->data(0, TemplateRole).canConvert<Gui::PreferencePackManager::TemplateFile>())
|
||||
results.push_back(child->data(0, TemplateRole).value<Gui::PreferencePackManager::TemplateFile>());
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
std::string DlgCreateNewPreferencePackImp::preferencePackName() const
|
||||
{
|
||||
return ui->lineEdit->text().toStdString();
|
||||
}
|
||||
|
||||
void DlgCreateNewPreferencePackImp::onItemChanged(QTreeWidgetItem* item, int column)
|
||||
{
|
||||
Q_UNUSED(column);
|
||||
const QSignalBlocker blocker(ui->treeWidget);
|
||||
if (auto group = item->parent(); group) {
|
||||
// Child clicked
|
||||
bool firstItemChecked = false;
|
||||
for (int childIndex = 0; childIndex < group->childCount(); ++childIndex) {
|
||||
auto child = group->child(childIndex);
|
||||
if (childIndex == 0) {
|
||||
firstItemChecked = child->checkState(0) == Qt::Checked;
|
||||
}
|
||||
else {
|
||||
bool thisItemChecked = child->checkState(0) == Qt::Checked;
|
||||
if (firstItemChecked != thisItemChecked) {
|
||||
group->setCheckState(0, Qt::PartiallyChecked);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
group->setCheckState(0, firstItemChecked ? Qt::Checked : Qt::Unchecked);
|
||||
}
|
||||
else {
|
||||
// Group clicked:
|
||||
auto groupCheckState = item->checkState(0);
|
||||
for (int childIndex = 0; childIndex < item->childCount(); ++childIndex) {
|
||||
auto child = item->child(childIndex);
|
||||
child->setCheckState(0, groupCheckState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DlgCreateNewPreferencePackImp::on_lineEdit_textEdited(const QString& text)
|
||||
{
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(text.isEmpty());
|
||||
}
|
||||
|
||||
void Gui::Dialog::DlgCreateNewPreferencePackImp::accept()
|
||||
{
|
||||
// Ensure that the chosen name is either unique, or that the user actually wants to overwrite the old one
|
||||
if (auto chosenName = ui->lineEdit->text().toStdString();
|
||||
std::find(_existingPackNames.begin(), _existingPackNames.end(), chosenName) != _existingPackNames.end()) {
|
||||
auto result = QMessageBox::warning(this, tr("Pack already exists"),
|
||||
tr("A preference pack with that name already exists. Do you want to overwrite it?"),
|
||||
QMessageBox::Yes | QMessageBox::Cancel);
|
||||
if (result == QMessageBox::Cancel)
|
||||
return;
|
||||
}
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
|
||||
#include "moc_DlgCreateNewPreferencePackImp.cpp"
|
||||
82
src/Gui/DlgCreateNewPreferencePackImp.h
Normal file
82
src/Gui/DlgCreateNewPreferencePackImp.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
|
||||
* *
|
||||
* 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 GUI_DIALOG_DLGCREATENEWTHEMEIMP_H
|
||||
#define GUI_DIALOG_DLGCREATENEWTHEMEIMP_H
|
||||
|
||||
#include <memory>
|
||||
#include <QDialog>
|
||||
#include <QRegExpValidator>
|
||||
|
||||
#include "PreferencePackManager.h"
|
||||
|
||||
class QTreeWidgetItem;
|
||||
|
||||
namespace Gui {
|
||||
|
||||
namespace Dialog {
|
||||
|
||||
class Ui_DlgCreateNewPreferencePack;
|
||||
|
||||
/**
|
||||
* \class DlgCreateNewPreferencePackImp
|
||||
*
|
||||
* A dialog to request a preferencePack name and a set of preferencePack templates.
|
||||
*
|
||||
* \author Chris Hennes
|
||||
*/
|
||||
class GuiExport DlgCreateNewPreferencePackImp : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
DlgCreateNewPreferencePackImp(QWidget* parent = nullptr);
|
||||
~DlgCreateNewPreferencePackImp();
|
||||
|
||||
void setPreferencePackTemplates(const std::vector<PreferencePackManager::TemplateFile> &availableTemplates);
|
||||
void setPreferencePackNames(const std::vector<std::string>& usedNames);
|
||||
|
||||
std::vector<PreferencePackManager::TemplateFile> selectedTemplates() const;
|
||||
std::string preferencePackName() const;
|
||||
|
||||
protected Q_SLOTS:
|
||||
|
||||
void onItemChanged(QTreeWidgetItem* item, int column);
|
||||
|
||||
void on_lineEdit_textEdited(const QString &text);
|
||||
|
||||
void accept() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui_DlgCreateNewPreferencePack> ui;
|
||||
std::map<std::string, QTreeWidgetItem*> _groups;
|
||||
std::vector<PreferencePackManager::TemplateFile> _templates;
|
||||
QRegExpValidator _nameValidator;
|
||||
std::vector<std::string> _existingPackNames;
|
||||
};
|
||||
|
||||
} // namespace Dialog
|
||||
} // namespace Gui
|
||||
|
||||
#endif // GUI_DIALOG_DLGCREATENEWTHEMEIMP_H
|
||||
@@ -14,7 +14,16 @@
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
@@ -32,7 +41,16 @@
|
||||
<string>Language</string>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
@@ -43,7 +61,16 @@
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
@@ -65,6 +92,144 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Preference Packs</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QTableWidget" name="PreferencePacks">
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="dragDropOverwriteMode">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderCascadingSectionResizes">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderMinimumSectionSize">
|
||||
<number>30</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>100</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderMinimumSectionSize">
|
||||
<number>16</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>24</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Load</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="SaveNewPreferencePack">
|
||||
<property name="text">
|
||||
<string>Save new...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ManagePreferencePacks">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Manage...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="GroupBox3">
|
||||
<property name="title">
|
||||
@@ -74,7 +239,16 @@
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<item>
|
||||
@@ -82,7 +256,16 @@
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
@@ -131,7 +314,16 @@
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
@@ -152,7 +344,16 @@ See the FreeCAD Wiki for details about the image.</string>
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
@@ -176,7 +377,16 @@ See the FreeCAD Wiki for details about the image.</string>
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
@@ -201,7 +411,16 @@ this according to your screen size or personal taste</string>
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
@@ -214,7 +433,7 @@ this according to your screen size or personal taste</string>
|
||||
<item>
|
||||
<widget class="QComboBox" name="treeMode">
|
||||
<property name="toolTip">
|
||||
<string>Customize how tree view is shown in the panel (restart required).
|
||||
<string>Customize how tree view is shown in the panel (restart required).
|
||||
|
||||
'ComboView': combine tree view and property view into one panel.
|
||||
'TreeView and PropertyView': split tree view and property view into separate panel.
|
||||
@@ -233,7 +452,16 @@ this according to your screen size or personal taste</string>
|
||||
<string>Start up</string>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
@@ -262,7 +490,16 @@ display the splash screen</string>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QGridLayout">
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
@@ -294,7 +531,16 @@ after FreeCAD launches</string>
|
||||
<string>Python console</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="_4">
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
@@ -323,19 +569,6 @@ horizontal space in Python console</string>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>352</width>
|
||||
<height>221</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -353,11 +586,6 @@ horizontal space in Python console</string>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>Gui/PrefWidgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Gui::PrefComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>Gui/PrefWidgets.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>Languages</tabstop>
|
||||
|
||||
@@ -27,17 +27,28 @@
|
||||
# include <QLocale>
|
||||
# include <QStyleFactory>
|
||||
# include <QTextStream>
|
||||
# include <QDesktopServices>
|
||||
#endif
|
||||
|
||||
#include "DlgGeneralImp.h"
|
||||
#include "ui_DlgGeneral.h"
|
||||
#include "Action.h"
|
||||
#include "Application.h"
|
||||
#include "Command.h"
|
||||
#include "DockWindowManager.h"
|
||||
#include "MainWindow.h"
|
||||
#include "PrefWidgets.h"
|
||||
#include "PythonConsole.h"
|
||||
#include "Language/Translator.h"
|
||||
#include "Gui/PreferencePackManager.h"
|
||||
#include "DlgPreferencesImp.h"
|
||||
|
||||
#include "DlgCreateNewPreferencePackImp.h"
|
||||
|
||||
// Only needed until PreferencePacks can be managed from the AddonManager:
|
||||
#include <boost/filesystem.hpp>
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
|
||||
using namespace Gui::Dialog;
|
||||
|
||||
@@ -82,6 +93,23 @@ DlgGeneralImp::DlgGeneralImp( QWidget* parent )
|
||||
else
|
||||
ui->AutoloadModuleCombo->addItem(px, it.key(), QVariant(it.value()));
|
||||
}
|
||||
|
||||
recreatePreferencePackMenu();
|
||||
connect(ui->SaveNewPreferencePack, &QPushButton::clicked, this, &DlgGeneralImp::saveAsNewPreferencePack);
|
||||
|
||||
// Future work: the Add-On Manager will be modified to include a section for Preference Packs, at which point this
|
||||
// button will be modified to open the Add-On Manager to that tab.
|
||||
auto savedPreferencePacksDirectory = fs::path(App::Application::getUserAppDataDir()) / "SavedPreferencePacks";
|
||||
|
||||
// If that directory hasn't been created yet, just send the user to the preferences directory
|
||||
if (!(fs::exists(savedPreferencePacksDirectory) && fs::is_directory(savedPreferencePacksDirectory))) {
|
||||
savedPreferencePacksDirectory = fs::path(App::Application::getUserAppDataDir());
|
||||
ui->ManagePreferencePacks->hide();
|
||||
}
|
||||
|
||||
QString pathToSavedPacks(QString::fromStdString(savedPreferencePacksDirectory.string()));
|
||||
ui->ManagePreferencePacks->setToolTip(tr("Open the directory of saved user preference packs"));
|
||||
connect(ui->ManagePreferencePacks, &QPushButton::clicked, this, [pathToSavedPacks]() { QDesktopServices::openUrl(QUrl::fromLocalFile(pathToSavedPacks)); });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,4 +327,87 @@ void DlgGeneralImp::changeEvent(QEvent *e)
|
||||
}
|
||||
}
|
||||
|
||||
void DlgGeneralImp::recreatePreferencePackMenu()
|
||||
{
|
||||
ui->PreferencePacks->setRowCount(0); // Begin by clearing whatever is there
|
||||
ui->PreferencePacks->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
|
||||
ui->PreferencePacks->setColumnCount(3);
|
||||
ui->PreferencePacks->setSelectionMode(QAbstractItemView::SelectionMode::NoSelection);
|
||||
ui->PreferencePacks->horizontalHeader()->setStretchLastSection(false);
|
||||
ui->PreferencePacks->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
|
||||
ui->PreferencePacks->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch);
|
||||
ui->PreferencePacks->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeMode::ResizeToContents);
|
||||
QStringList columnHeaders;
|
||||
columnHeaders << tr("Preference Pack Name")
|
||||
<< tr("Tags")
|
||||
<< QString(); // for the "Load" buttons
|
||||
ui->PreferencePacks->setHorizontalHeaderLabels(columnHeaders);
|
||||
|
||||
// Populate the Preference Packs list
|
||||
auto packs = Application::Instance->prefPackManager()->preferencePacks();
|
||||
|
||||
ui->PreferencePacks->setRowCount(packs.size());
|
||||
|
||||
int row = 0;
|
||||
QIcon icon = style()->standardIcon(QStyle::SP_DialogApplyButton);
|
||||
for (const auto& pack : packs) {
|
||||
auto name = new QTableWidgetItem(QString::fromStdString(pack.first));
|
||||
name->setToolTip(QString::fromStdString(pack.second.metadata().description()));
|
||||
ui->PreferencePacks->setItem(row, 0, name);
|
||||
auto tags = pack.second.metadata().tag();
|
||||
QString tagString;
|
||||
for (const auto& tag : tags) {
|
||||
if (tagString.isEmpty())
|
||||
tagString.append(QString::fromStdString(tag));
|
||||
else
|
||||
tagString.append(QStringLiteral(", ") + QString::fromStdString(tag));
|
||||
}
|
||||
QTableWidgetItem* kind = new QTableWidgetItem(tagString);
|
||||
ui->PreferencePacks->setItem(row, 1, kind);
|
||||
auto button = new QPushButton(icon, tr("Apply"));
|
||||
button->setToolTip(tr("Apply the %1 preference pack").arg(QString::fromStdString(pack.first)));
|
||||
connect(button, &QPushButton::clicked, this, [this, pack]() { onLoadPreferencePackClicked(pack.first); });
|
||||
ui->PreferencePacks->setCellWidget(row, 2, button);
|
||||
++row;
|
||||
}
|
||||
}
|
||||
|
||||
void DlgGeneralImp::saveAsNewPreferencePack()
|
||||
{
|
||||
// Create and run a modal New PreferencePack dialog box
|
||||
auto packs = Application::Instance->prefPackManager()->preferencePackNames();
|
||||
newPreferencePackDialog = std::make_unique<DlgCreateNewPreferencePackImp>(this);
|
||||
newPreferencePackDialog->setPreferencePackTemplates(Application::Instance->prefPackManager()->templateFiles());
|
||||
newPreferencePackDialog->setPreferencePackNames(packs);
|
||||
connect(newPreferencePackDialog.get(), &DlgCreateNewPreferencePackImp::accepted, this, &DlgGeneralImp::newPreferencePackDialogAccepted);
|
||||
newPreferencePackDialog->open();
|
||||
}
|
||||
|
||||
void DlgGeneralImp::newPreferencePackDialogAccepted()
|
||||
{
|
||||
auto preferencePackTemplates = Application::Instance->prefPackManager()->templateFiles();
|
||||
auto selection = newPreferencePackDialog->selectedTemplates();
|
||||
std::vector<PreferencePackManager::TemplateFile> selectedTemplates;
|
||||
std::copy_if(preferencePackTemplates.begin(), preferencePackTemplates.end(), std::back_inserter(selectedTemplates), [selection](PreferencePackManager::TemplateFile& t) {
|
||||
for (const auto& item : selection)
|
||||
if (item.group == t.group && item.name == t.name)
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
auto preferencePackName = newPreferencePackDialog->preferencePackName();
|
||||
Application::Instance->prefPackManager()->save(preferencePackName, selectedTemplates);
|
||||
Application::Instance->prefPackManager()->rescan();
|
||||
recreatePreferencePackMenu();
|
||||
}
|
||||
|
||||
void DlgGeneralImp::onLoadPreferencePackClicked(const std::string& packName)
|
||||
{
|
||||
if (Application::Instance->prefPackManager()->apply(packName)) {
|
||||
auto parentDialog = qobject_cast<DlgPreferencesImp*> (this->window());
|
||||
if (parentDialog)
|
||||
parentDialog->reload();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#include "moc_DlgGeneralImp.cpp"
|
||||
|
||||
@@ -32,6 +32,7 @@ class QTabWidget;
|
||||
namespace Gui {
|
||||
namespace Dialog {
|
||||
class Ui_DlgGeneral;
|
||||
class DlgCreateNewPreferencePackImp;
|
||||
|
||||
/** This class implements the settings for the application.
|
||||
* You can change window style, size of pixmaps, size of recent file list and so on
|
||||
@@ -51,11 +52,18 @@ public:
|
||||
protected:
|
||||
void changeEvent(QEvent *e);
|
||||
|
||||
protected Q_SLOTS:
|
||||
void onLoadPreferencePackClicked(const std::string &packName);
|
||||
void recreatePreferencePackMenu();
|
||||
void newPreferencePackDialogAccepted();
|
||||
|
||||
private:
|
||||
void setRecentFileSize();
|
||||
void saveAsNewPreferencePack();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui_DlgGeneral> ui;
|
||||
std::unique_ptr<DlgCreateNewPreferencePackImp> newPreferencePackDialog;
|
||||
};
|
||||
|
||||
} // namespace Dialog
|
||||
|
||||
@@ -466,4 +466,17 @@ void DlgPreferencesImp::changeEvent(QEvent *e)
|
||||
}
|
||||
}
|
||||
|
||||
void DlgPreferencesImp::reload()
|
||||
{
|
||||
for (int i = 0; i < ui->tabWidgetStack->count(); i++) {
|
||||
QTabWidget* tabWidget = (QTabWidget*)ui->tabWidgetStack->widget(i);
|
||||
for (int j = 0; j < tabWidget->count(); j++) {
|
||||
PreferencePage* page = qobject_cast<PreferencePage*>(tabWidget->widget(j));
|
||||
if (page)
|
||||
page->loadSettings();
|
||||
}
|
||||
}
|
||||
applyChanges();
|
||||
}
|
||||
|
||||
#include "moc_DlgPreferencesImp.cpp"
|
||||
|
||||
@@ -114,11 +114,13 @@ class GuiExport DlgPreferencesImp : public QDialog
|
||||
public:
|
||||
static void addPage(const std::string& className, const std::string& group);
|
||||
static void removePage(const std::string& className, const std::string& group);
|
||||
static void reloadSettings();
|
||||
|
||||
DlgPreferencesImp(QWidget* parent = 0, Qt::WindowFlags fl = Qt::WindowFlags());
|
||||
~DlgPreferencesImp();
|
||||
|
||||
void accept();
|
||||
void reload();
|
||||
void activateGroupPage(const QString& group, int id);
|
||||
|
||||
protected:
|
||||
@@ -136,11 +138,11 @@ private:
|
||||
/** @name for internal use only */
|
||||
//@{
|
||||
void setupPages();
|
||||
void reloadPages();
|
||||
QTabWidget* createTabForGroup(const std::string& groupName);
|
||||
void createPageInGroup(QTabWidget* tabWidget, const std::string& pageName);
|
||||
void applyChanges();
|
||||
void restoreDefaults();
|
||||
void reloadPages();
|
||||
//@}
|
||||
|
||||
private:
|
||||
|
||||
@@ -381,6 +381,21 @@ void DockWindowManager::saveState()
|
||||
}
|
||||
}
|
||||
|
||||
void DockWindowManager::loadState()
|
||||
{
|
||||
ParameterGrp::handle hPref = App::GetApplication().GetUserParameter().GetGroup("BaseApp")
|
||||
->GetGroup("MainWindow")->GetGroup("DockWindows");
|
||||
const QList<DockWindowItem>& dockItems = d->_dockWindowItems.dockWidgets();
|
||||
for (QList<DockWindowItem>::ConstIterator it = dockItems.begin(); it != dockItems.end(); ++it) {
|
||||
QDockWidget* dw = findDockWidget(d->_dockedWindows, it->name);
|
||||
if (dw) {
|
||||
QByteArray dockName = it->name.toLatin1();
|
||||
bool visible = hPref->GetBool(dockName.constData(), it->visibility);
|
||||
dw->setVisible(visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QDockWidget* DockWindowManager::findDockWidget(const QList<QDockWidget*>& dw, const QString& name) const
|
||||
{
|
||||
for (QList<QDockWidget*>::ConstIterator it = dw.begin(); it != dw.end(); ++it) {
|
||||
|
||||
@@ -88,6 +88,7 @@ public:
|
||||
QList<QWidget*> getDockWindows() const;
|
||||
|
||||
void saveState();
|
||||
void loadState();
|
||||
void retranslate();
|
||||
|
||||
private Q_SLOTS:
|
||||
|
||||
447
src/Gui/PreferencePackManager.cpp
Normal file
447
src/Gui/PreferencePackManager.cpp
Normal file
@@ -0,0 +1,447 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
|
||||
* *
|
||||
* 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 <memory>
|
||||
# include <string_view>
|
||||
# include <mutex>
|
||||
#endif
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <QDir>
|
||||
|
||||
#include "PreferencePackManager.h"
|
||||
#include "App/Metadata.h"
|
||||
#include "Base/Parameter.h"
|
||||
#include "Base/Interpreter.h"
|
||||
#include "Base/Console.h"
|
||||
#include "DockWindowManager.h"
|
||||
#include "ToolBarManager.h"
|
||||
|
||||
#include <App/Application.h>
|
||||
|
||||
#include <ctime> // For generating a timestamped filename
|
||||
|
||||
|
||||
using namespace Gui;
|
||||
using namespace xercesc;
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
PreferencePack::PreferencePack(const fs::path& path, const App::Metadata& metadata) :
|
||||
_path(path), _metadata(metadata)
|
||||
{
|
||||
if (!fs::exists(_path)) {
|
||||
throw std::runtime_error{ "Cannot access " + path.string() };
|
||||
}
|
||||
|
||||
auto qssPaths = QDir::searchPaths(QString::fromUtf8("qss"));
|
||||
auto cssPaths = QDir::searchPaths(QString::fromUtf8("css"));
|
||||
|
||||
qssPaths.append(QString::fromStdString(_path.string()));
|
||||
cssPaths.append(QString::fromStdString(_path.string()));
|
||||
|
||||
QDir::setSearchPaths(QString::fromUtf8("qss"), qssPaths);
|
||||
QDir::setSearchPaths(QString::fromUtf8("css"), cssPaths);
|
||||
}
|
||||
|
||||
std::string PreferencePack::name() const
|
||||
{
|
||||
return _metadata.name();
|
||||
}
|
||||
|
||||
bool PreferencePack::apply() const
|
||||
{
|
||||
// Run the pre.FCMacro, if it exists: if it raises an exception, abort the process
|
||||
auto preMacroPath = _path / "pre.FCMacro";
|
||||
if (fs::exists(preMacroPath)) {
|
||||
try {
|
||||
Base::Interpreter().runFile(preMacroPath.string().c_str(), false);
|
||||
}
|
||||
catch (...) {
|
||||
Base::Console().Message("PreferencePack application aborted by the preferencePack's pre.FCMacro");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Back up the old config file
|
||||
auto savedPreferencePacksDirectory = fs::path(App::Application::getUserAppDataDir()) / "SavedPreferencePacks";
|
||||
auto backupFile = savedPreferencePacksDirectory / "user.cfg.backup";
|
||||
try {
|
||||
fs::remove(backupFile);
|
||||
}
|
||||
catch (...) {}
|
||||
App::GetApplication().GetUserParameter().SaveDocument(backupFile.string().c_str());
|
||||
|
||||
// Apply the config settings
|
||||
applyConfigChanges();
|
||||
|
||||
// Run the Post.FCMacro, if it exists
|
||||
auto postMacroPath = _path / "post.FCMacro";
|
||||
if (fs::exists(postMacroPath)) {
|
||||
try {
|
||||
Base::Interpreter().runFile(postMacroPath.string().c_str(), false);
|
||||
}
|
||||
catch (...) {
|
||||
Base::Console().Message("PreferencePack application reverted by the preferencePack's post.FCMacro");
|
||||
App::GetApplication().GetUserParameter().LoadDocument(backupFile.string().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
App::Metadata Gui::PreferencePack::metadata() const
|
||||
{
|
||||
return _metadata;
|
||||
}
|
||||
|
||||
void PreferencePack::applyConfigChanges() const
|
||||
{
|
||||
auto configFile = _path / (_metadata.name() + ".cfg");
|
||||
if (fs::exists(configFile)) {
|
||||
ParameterManager newParameters;
|
||||
newParameters.LoadDocument(configFile.string().c_str());
|
||||
auto baseAppGroup = App::GetApplication().GetUserParameter().GetGroup("BaseApp");
|
||||
newParameters.GetGroup("BaseApp")->insertTo(baseAppGroup);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
PreferencePackManager::PreferencePackManager()
|
||||
{
|
||||
auto modPath = fs::path(App::Application::getUserAppDataDir()) / "Mod";
|
||||
auto savedPath = fs::path(App::Application::getUserAppDataDir()) / "SavedPreferencePacks";
|
||||
auto resourcePath = fs::path(App::Application::getResourceDir()) / "Gui" / "PreferencePacks";
|
||||
_preferencePackPaths.push_back(resourcePath);
|
||||
_preferencePackPaths.push_back(modPath);
|
||||
_preferencePackPaths.push_back(savedPath);
|
||||
rescan();
|
||||
|
||||
// Housekeeping:
|
||||
DeleteOldBackups();
|
||||
}
|
||||
|
||||
void PreferencePackManager::rescan()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
for (const auto& path : _preferencePackPaths) {
|
||||
if (fs::exists(path) && fs::is_directory(path)) {
|
||||
FindPreferencePacksInPackage(path);
|
||||
for (const auto& mod : fs::directory_iterator(path)) {
|
||||
if (fs::is_directory(mod)) {
|
||||
FindPreferencePacksInPackage(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Gui::PreferencePackManager::FindPreferencePacksInPackage(const fs::path& mod)
|
||||
{
|
||||
auto packageMetadataFile = mod / "package.xml";
|
||||
if (fs::exists(packageMetadataFile) && fs::is_regular_file(packageMetadataFile)) {
|
||||
try {
|
||||
App::Metadata metadata(packageMetadataFile);
|
||||
auto content = metadata.content();
|
||||
for (const auto& item : content) {
|
||||
if (item.first == "preferencepack") {
|
||||
PreferencePack newPreferencePack(mod / item.second.name(), item.second);
|
||||
_preferencePacks.insert(std::make_pair(newPreferencePack.name(), newPreferencePack));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
// Failed to read the metadata, or to create the preferencePack based on it...
|
||||
Base::Console().Error(("Failed to read " + packageMetadataFile.string()).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> PreferencePackManager::preferencePackNames() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
std::vector<std::string> names;
|
||||
for (const auto& preferencePack : _preferencePacks)
|
||||
names.push_back(preferencePack.first);
|
||||
return names;
|
||||
}
|
||||
|
||||
std::map<std::string, PreferencePack> Gui::PreferencePackManager::preferencePacks() const
|
||||
{
|
||||
return _preferencePacks;
|
||||
}
|
||||
|
||||
bool PreferencePackManager::apply(const std::string& preferencePackName) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
if (auto preferencePack = _preferencePacks.find(preferencePackName); preferencePack != _preferencePacks.end()) {
|
||||
BackupCurrentConfig();
|
||||
bool wasApplied = preferencePack->second.apply();
|
||||
if (wasApplied) {
|
||||
// If the visibility state of the dock windows was changed we have to manually reload their state
|
||||
Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
|
||||
pDockMgr->loadState();
|
||||
|
||||
// Same goes for toolbars:
|
||||
Gui::ToolBarManager* pToolbarMgr = Gui::ToolBarManager::getInstance();
|
||||
pToolbarMgr->restoreState();
|
||||
|
||||
// TODO: Are there other things that have to be manually triggered?
|
||||
}
|
||||
return wasApplied;
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("No such Preference Pack: " + preferencePackName);
|
||||
}
|
||||
}
|
||||
|
||||
void copyTemplateParameters(Base::Reference<ParameterGrp> templateGroup, const std::string& path, Base::Reference<ParameterGrp> outputGroup)
|
||||
{
|
||||
auto userParameterHandle = App::GetApplication().GetParameterGroupByPath(path.c_str());
|
||||
|
||||
// Ensure that the DockWindowManager has saved its current state:
|
||||
Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
|
||||
pDockMgr->saveState();
|
||||
|
||||
// Do the same for ToolBars
|
||||
Gui::ToolBarManager* pToolbarMgr = Gui::ToolBarManager::getInstance();
|
||||
pToolbarMgr->saveState();
|
||||
|
||||
auto boolMap = templateGroup->GetBoolMap();
|
||||
for (const auto& kv : boolMap) {
|
||||
auto currentValue = userParameterHandle->GetBool(kv.first.c_str(), kv.second);
|
||||
outputGroup->SetBool(kv.first.c_str(), currentValue);
|
||||
}
|
||||
|
||||
auto intMap = templateGroup->GetIntMap();
|
||||
for (const auto& kv : intMap) {
|
||||
auto currentValue = userParameterHandle->GetInt(kv.first.c_str(), kv.second);
|
||||
outputGroup->SetInt(kv.first.c_str(), currentValue);
|
||||
}
|
||||
|
||||
auto uintMap = templateGroup->GetUnsignedMap();
|
||||
for (const auto& kv : uintMap) {
|
||||
auto currentValue = userParameterHandle->GetUnsigned(kv.first.c_str(), kv.second);
|
||||
outputGroup->SetUnsigned(kv.first.c_str(), currentValue);
|
||||
}
|
||||
|
||||
auto floatMap = templateGroup->GetFloatMap();
|
||||
for (const auto& kv : floatMap) {
|
||||
auto currentValue = userParameterHandle->GetFloat(kv.first.c_str(), kv.second);
|
||||
outputGroup->SetFloat(kv.first.c_str(), currentValue);
|
||||
}
|
||||
|
||||
auto asciiMap = templateGroup->GetASCIIMap();
|
||||
for (const auto& kv : asciiMap) {
|
||||
auto currentValue = userParameterHandle->GetASCII(kv.first.c_str(), kv.second.c_str());
|
||||
outputGroup->SetASCII(kv.first.c_str(), currentValue.c_str());
|
||||
}
|
||||
|
||||
// Recurse...
|
||||
auto templateSubgroups = templateGroup->GetGroups();
|
||||
for (auto& templateSubgroup : templateSubgroups) {
|
||||
std::string sgName = templateSubgroup->GetGroupName();
|
||||
auto outputSubgroupHandle = outputGroup->GetGroup(sgName.c_str());
|
||||
copyTemplateParameters(templateSubgroup, path + "/" + sgName, outputSubgroupHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void copyTemplateParameters(/*const*/ ParameterManager& templateParameterManager, ParameterManager& outputParameterManager)
|
||||
{
|
||||
auto groups = templateParameterManager.GetGroups();
|
||||
for (auto& group : groups) {
|
||||
std::string name = group->GetGroupName();
|
||||
auto groupHandle = outputParameterManager.GetGroup(name.c_str());
|
||||
copyTemplateParameters(group, "User parameter:" + name, groupHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void PreferencePackManager::save(const std::string& name, const std::vector<TemplateFile>& templates)
|
||||
{
|
||||
if (templates.empty())
|
||||
return;
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
auto savedPreferencePacksDirectory = fs::path(App::Application::getUserAppDataDir()) / "SavedPreferencePacks";
|
||||
fs::path preferencePackDirectory(savedPreferencePacksDirectory / name);
|
||||
if (fs::exists(preferencePackDirectory) && !fs::is_directory(preferencePackDirectory))
|
||||
throw std::runtime_error("Cannot create " + savedPreferencePacksDirectory.string() + ": file with that name exists already");
|
||||
|
||||
if (!fs::exists(preferencePackDirectory))
|
||||
fs::create_directories(preferencePackDirectory);
|
||||
|
||||
// Create or update the saved user preferencePacks package.xml metadata file
|
||||
std::unique_ptr<App::Metadata> metadata;
|
||||
if (fs::exists(savedPreferencePacksDirectory / "package.xml")) {
|
||||
metadata = std::make_unique<App::Metadata>(savedPreferencePacksDirectory / "package.xml");
|
||||
}
|
||||
else {
|
||||
// Create and set all of the required metadata to make it easier for PreferencePack authors to copy this
|
||||
// file into their preferencePack distributions.
|
||||
metadata = std::make_unique<App::Metadata>();
|
||||
metadata->setName("User-Saved PreferencePacks");
|
||||
metadata->setDescription("Generated automatically -- edits may be lost when saving new preferencePacks");
|
||||
metadata->setVersion(1);
|
||||
metadata->addMaintainer(App::Meta::Contact("No Maintainer", "email@freecadweb.org"));
|
||||
metadata->addLicense(App::Meta::License("(Unspecified)", "(Unspecified)"));
|
||||
metadata->addUrl(App::Meta::Url("https://github.com/FreeCAD/FreeCAD", App::Meta::UrlType::repository));
|
||||
}
|
||||
App::Metadata newPreferencePackMetadata;
|
||||
newPreferencePackMetadata.setName(name);
|
||||
newPreferencePackMetadata.setVersion(1);
|
||||
|
||||
metadata->addContentItem("preferencepack", newPreferencePackMetadata);
|
||||
metadata->write(savedPreferencePacksDirectory / "package.xml");
|
||||
|
||||
// Create the config file
|
||||
ParameterManager outputParameterManager;
|
||||
outputParameterManager.CreateDocument();
|
||||
for (const auto& t : templates) {
|
||||
ParameterManager templateParameterManager;
|
||||
templateParameterManager.LoadDocument(t.path.string().c_str());
|
||||
copyTemplateParameters(templateParameterManager, outputParameterManager);
|
||||
}
|
||||
auto cfgFilename = savedPreferencePacksDirectory / name / (name + ".cfg");
|
||||
outputParameterManager.SaveDocument(cfgFilename.string().c_str());
|
||||
}
|
||||
|
||||
// Needed until we support only C++20 and above and can use std::string's built-in ends_with()
|
||||
bool fc_ends_with(std::string_view str, std::string_view suffix)
|
||||
{
|
||||
return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
|
||||
}
|
||||
|
||||
std::vector<fs::path> scanForTemplateFolders(const std::string& groupName, const fs::path& entry)
|
||||
{
|
||||
// From this location, find the folder(s) called "PreferencePackTemplates"
|
||||
std::vector<fs::path> templateFolders;
|
||||
if (fs::exists(entry)) {
|
||||
if (fs::is_directory(entry)) {
|
||||
if (entry.filename() == "PreferencePackTemplates" ||
|
||||
entry.filename() == "preference_pack_templates") {
|
||||
templateFolders.push_back(entry);
|
||||
}
|
||||
else {
|
||||
std::string subgroupName = groupName + "/" + entry.filename().string();
|
||||
for (const auto& subentry : fs::directory_iterator(entry)) {
|
||||
auto contents = scanForTemplateFolders(subgroupName, subentry);
|
||||
std::copy(contents.begin(), contents.end(), std::back_inserter(templateFolders));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return templateFolders;
|
||||
}
|
||||
|
||||
std::vector<PreferencePackManager::TemplateFile> scanForTemplateFiles(const std::string& groupName, const fs::path& entry)
|
||||
{
|
||||
auto templateFolders = scanForTemplateFolders(groupName, entry);
|
||||
|
||||
std::vector<PreferencePackManager::TemplateFile> templateFiles;
|
||||
for (const auto& templateDir : templateFolders) {
|
||||
if (!fs::exists(templateDir) || !fs::is_directory(templateDir))
|
||||
continue;
|
||||
for (const auto& entry : fs::directory_iterator(templateDir)) {
|
||||
if (entry.path().extension() == ".cfg") {
|
||||
auto name = entry.path().filename().stem().string();
|
||||
std::replace(name.begin(), name.end(), '_', ' ');
|
||||
// Make sure we don't insert the same thing twice...
|
||||
if (std::find_if(templateFiles.begin(), templateFiles.end(), [groupName, name](const auto &rhs)->bool {
|
||||
return groupName == rhs.group && name == rhs.name;
|
||||
} ) != templateFiles.end())
|
||||
continue;
|
||||
templateFiles.push_back({ groupName, name, entry });
|
||||
}
|
||||
}
|
||||
}
|
||||
return templateFiles;
|
||||
}
|
||||
|
||||
std::vector<PreferencePackManager::TemplateFile> PreferencePackManager::templateFiles(bool rescan)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
if (!_templateFiles.empty() && !rescan)
|
||||
return _templateFiles;
|
||||
|
||||
// Locate all of the template files available on this system
|
||||
// Template files end in ".cfg" -- They are located in:
|
||||
// * $INSTALL_DIR/data/Gui/PreferencePackTemplates/(Appearance|Behavior)/*
|
||||
// * $DATA_DIR/Mod/**/PreferencePackTemplates/(Appearance|Behavior)/*
|
||||
// (alternate spellings are provided for packages using CamelCase and snake_case, and both major English dialects)
|
||||
|
||||
auto resourcePath = fs::path(App::Application::getResourceDir()) / "Gui";
|
||||
auto modPath = fs::path(App::Application::getUserAppDataDir()) / "Mod";
|
||||
|
||||
std::string group = "Built-In";
|
||||
if (fs::exists(resourcePath) && fs::is_directory(resourcePath)) {
|
||||
const auto localFiles = scanForTemplateFiles(group, resourcePath);
|
||||
std::copy(localFiles.begin(), localFiles.end(), std::back_inserter(_templateFiles));
|
||||
}
|
||||
|
||||
if (fs::exists(modPath) && fs::is_directory(modPath)) {
|
||||
for (const auto& mod : fs::directory_iterator(modPath)) {
|
||||
group = mod.path().filename().string();
|
||||
const auto localFiles = scanForTemplateFiles(group, mod);
|
||||
std::copy(localFiles.begin(), localFiles.end(), std::back_inserter(_templateFiles));
|
||||
}
|
||||
}
|
||||
|
||||
return _templateFiles;
|
||||
}
|
||||
|
||||
void Gui::PreferencePackManager::BackupCurrentConfig() const
|
||||
{
|
||||
auto backupDirectory = fs::path(App::Application::getUserAppDataDir()) / "SavedPreferencePacks" / "Backups";
|
||||
fs::create_directories(backupDirectory);
|
||||
|
||||
// Create a timestamped filename:
|
||||
auto time = std::time(nullptr);
|
||||
std::ostringstream timestampStream;
|
||||
timestampStream << "user." << time << ".cfg";
|
||||
auto filename = backupDirectory / timestampStream.str();
|
||||
|
||||
// Save the current config:
|
||||
App::GetApplication().GetUserParameter().SaveDocument(filename.string().c_str());
|
||||
}
|
||||
|
||||
void Gui::PreferencePackManager::DeleteOldBackups() const
|
||||
{
|
||||
constexpr auto oneWeek = 60.0 * 60.0 * 24.0 * 7.0;
|
||||
const auto now = std::time(nullptr);
|
||||
auto backupDirectory = fs::path(App::Application::getUserAppDataDir()) / "SavedPreferencePacks" / "Backups";
|
||||
if (fs::exists(backupDirectory) && fs::is_directory(backupDirectory)) {
|
||||
for (const auto& backup : fs::directory_iterator(backupDirectory)) {
|
||||
if (std::difftime(now, fs::last_write_time(backup)) > oneWeek) {
|
||||
try {
|
||||
fs::remove(backup);
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
188
src/Gui/PreferencePackManager.h
Normal file
188
src/Gui/PreferencePackManager.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
|
||||
* *
|
||||
* 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 BASE_PREFERENCEPACKMANAGER_H
|
||||
#define BASE_PREFERENCEPACKMANAGER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
#include "App/Metadata.h"
|
||||
|
||||
namespace Gui {
|
||||
|
||||
/**
|
||||
* \class PreferencePack A collection of user preferences stored in files on disk
|
||||
*/
|
||||
class PreferencePack {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a preferencePack from a directory
|
||||
*
|
||||
* \param path A path to a mod directory that contains a preferencePack
|
||||
* \param metadata The metadata from the package.xml file describing this preferencePack
|
||||
*/
|
||||
PreferencePack(const boost::filesystem::path& path, const App::Metadata& metadata);
|
||||
|
||||
~PreferencePack() = default;
|
||||
|
||||
/**
|
||||
* Get the name of the PreferencePack
|
||||
*/
|
||||
std::string name() const;
|
||||
|
||||
/**
|
||||
* Apply the PreferencePack over the top of the current preferences set
|
||||
* \returns True if the preferencePack was applied, or false if not
|
||||
*/
|
||||
bool apply() const;
|
||||
|
||||
/**
|
||||
* Get the complete metadata object for this preference pack
|
||||
*/
|
||||
App::Metadata metadata() const;
|
||||
|
||||
private:
|
||||
|
||||
void applyConfigChanges() const;
|
||||
|
||||
boost::filesystem::path _path;
|
||||
App::Metadata _metadata;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \class PreferencePackManager handles storable and loadable collections of user preferences
|
||||
*
|
||||
* This class provides some additional utility functions for allowing users to save their current
|
||||
* preferences as a PreferencePack based on a set of template files provided either in the main
|
||||
* FreeCAD distribution, or inside various installed mods.
|
||||
*/
|
||||
class PreferencePackManager {
|
||||
public:
|
||||
PreferencePackManager();
|
||||
~PreferencePackManager() = default;
|
||||
|
||||
/**
|
||||
* Rescan the preferencePack directory and update the available PreferencePacks
|
||||
*/
|
||||
void rescan();
|
||||
|
||||
/**
|
||||
* Get an alphabetical list of names of all installed PreferencePacks
|
||||
*/
|
||||
std::vector<std::string> preferencePackNames() const;
|
||||
|
||||
/**
|
||||
* Get a map of all installed PreferencePack names and their associated packs
|
||||
*/
|
||||
std::map<std::string, PreferencePack> preferencePacks() const;
|
||||
|
||||
/**
|
||||
* Apply the named preferencePack
|
||||
* \return True if the preferencePack was applied, or false if it was not
|
||||
*/
|
||||
bool apply(const std::string& preferencePackName) const;
|
||||
|
||||
/**
|
||||
* \struct TemplateFile A file containing a set of preferences that can be saved into
|
||||
* a PreferencePack
|
||||
*
|
||||
* PreferencePacks can contain any parameters at all, but inside FreeCAD there is no
|
||||
* centralized list of all of these parameters, and at any given time the user.cfg file
|
||||
* usually does not store a value for all possible parameters. Instead, it simply allows
|
||||
* calling code to use whatever default values that code sets. This is all completely
|
||||
* hidden from the users: FreeCAD behaves as though those values exist in the config file.
|
||||
*
|
||||
* When a user saves their current configuration as a pack, they likely expect that saved
|
||||
* configuration to include those default values, so that if they later apply their saved
|
||||
* configuration those defaults are restored. To enable this a set of template files
|
||||
* listing default values for various types of parameters can be used. Each file is
|
||||
* presented to the user as a checkable box when saving a new preferences pack, and the
|
||||
* intention is that these files will list out the various user-facing parameters that
|
||||
* someone might want to save into a preferences pack.
|
||||
*
|
||||
* These files are themselves valid user.cfg files, that if loaded and applied will result
|
||||
* in the default values of their contained variables being set. For this to work, these
|
||||
* files should be kept up-to-date with the default values set in the code. They are not
|
||||
* required to contain an exhaustive listing of all possible parameters: in most cases it
|
||||
* is enough that they list the variables that a user would expect for a given name. For
|
||||
* example, "Sketcher Colors.cfg" should list out all of the default colors used in
|
||||
* Sketcher that a user can set in Preferences, but it is not necessary that it contain any
|
||||
* color that is only used internally, and it should not include things like fonts, or
|
||||
* behavior information.
|
||||
*
|
||||
* Template files are always located in a directory hierarchy that differentiates between
|
||||
* templates that only affect appearance, and those that affect behavior.
|
||||
*
|
||||
* The base FreeCAD installation includes default templates in:
|
||||
* $INSTALL_DIR/data/Gui/PreferencePackTemplates/
|
||||
*
|
||||
* External add-ons are also searched for any directory called PreferencePackTemplates or
|
||||
* preference_pack_templates -- either of which is expected to contain appearance and/or
|
||||
* behavior subdirectories. In this way external add-ons can allow a user to easily save
|
||||
* their preferences to a PreferencePack, or even to add additional templates representing
|
||||
* sets of core FreeCAD preferences.
|
||||
*/
|
||||
struct TemplateFile {
|
||||
std::string group; // Generally the Add-On/Mod/Package name
|
||||
std::string name;
|
||||
boost::filesystem::path path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Save current settings as a (possibly new) preferencePack
|
||||
*
|
||||
* If the named preferencePack does not exist, this creates it on disk. If it does exist, this overwrites the original.
|
||||
*/
|
||||
void save(const std::string& name, const std::vector<TemplateFile>& templates);
|
||||
|
||||
|
||||
std::vector<TemplateFile> templateFiles(bool rescan = false);
|
||||
|
||||
private:
|
||||
|
||||
void FindPreferencePacksInPackage(const boost::filesystem::path& mod);
|
||||
|
||||
void BackupCurrentConfig() const;
|
||||
|
||||
void DeleteOldBackups() const;
|
||||
|
||||
std::vector<boost::filesystem::path> _preferencePackPaths;
|
||||
std::vector<TemplateFile> _templateFiles;
|
||||
std::map<std::string, PreferencePack> _preferencePacks;
|
||||
mutable std::mutex _mutex;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(Gui::PreferencePackManager::TemplateFile) // So it can be used with QVariant
|
||||
|
||||
|
||||
#endif
|
||||
21
src/Gui/PreferencePackTemplates/Arch_Colors.cfg
Normal file
21
src/Gui/PreferencePackTemplates/Arch_Colors.cfg
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<FCParameters>
|
||||
<FCParamGroup Name="Root">
|
||||
<FCParamGroup Name="BaseApp">
|
||||
<FCParamGroup Name="Preferences">
|
||||
<FCParamGroup Name="Mod">
|
||||
<FCParamGroup Name="Arch">
|
||||
<FCUInt Name="WallColor" Value="3604403967"/>
|
||||
<FCUInt Name="StructureColor" Value="2527705855"/>
|
||||
<FCUInt Name="RebarColor" Value="3111475967"/>
|
||||
<FCUInt Name="WindowColor" Value="556614399"/>
|
||||
<FCUInt Name="WindowGlassColor" Value="1572326399"/>
|
||||
<FCUInt Name="PanelColor" Value="3416289279"/>
|
||||
<FCUInt Name="ColorHelpers" Value="674321151"/>
|
||||
<FCUInt Name="defaultSpaceColor" Value="4280090879"/>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParameters>
|
||||
28
src/Gui/PreferencePackTemplates/CMakeLists.txt
Normal file
28
src/Gui/PreferencePackTemplates/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
SET(PreferencePackTemplates_Files
|
||||
Arch_Colors.cfg
|
||||
Console_Colors.cfg
|
||||
Draft_Colors.cfg
|
||||
Editor_Colors.cfg
|
||||
Editor_Font.cfg
|
||||
Main_window_layout.cfg
|
||||
Path_Colors.cfg
|
||||
Sketcher_Colors.cfg
|
||||
Start_Colors.cfg
|
||||
TechDraw_Colors.cfg
|
||||
Window_Colors.cfg
|
||||
)
|
||||
|
||||
ADD_CUSTOM_TARGET(PreferencePackTemplates_data ALL
|
||||
SOURCES ${PreferencePackTemplates_Files}
|
||||
)
|
||||
|
||||
fc_copy_sources(PreferencePackTemplates_data "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Gui/PreferencePackTemplates"
|
||||
${PreferencePackTemplates_Files})
|
||||
|
||||
INSTALL(
|
||||
FILES
|
||||
${PreferencePackTemplates_Files}
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_DATADIR}/Gui/PreferencePackTemplates
|
||||
)
|
||||
15
src/Gui/PreferencePackTemplates/Console_Colors.cfg
Normal file
15
src/Gui/PreferencePackTemplates/Console_Colors.cfg
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<FCParameters>
|
||||
<FCParamGroup Name="Root">
|
||||
<FCParamGroup Name="BaseApp">
|
||||
<FCParamGroup Name="Preferences">
|
||||
<FCParamGroup Name="OutputWindow">
|
||||
<FCUInt Name="colorText" Value="4294967295"/>
|
||||
<FCUInt Name="colorLogging" Value="3117350911"/>
|
||||
<FCUInt Name="colorWarning" Value="4290999551"/>
|
||||
<FCUInt Name="colorError" Value="4287006463"/>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParameters>
|
||||
17
src/Gui/PreferencePackTemplates/Draft_Colors.cfg
Normal file
17
src/Gui/PreferencePackTemplates/Draft_Colors.cfg
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<FCParameters>
|
||||
<FCParamGroup Name="Root">
|
||||
<FCParamGroup Name="BaseApp">
|
||||
<FCParamGroup Name="Preferences">
|
||||
<FCParamGroup Name="Mod">
|
||||
<FCParamGroup Name="Draft">
|
||||
<FCUInt Name="constructioncolor" Value="746455039"/>
|
||||
<FCInt Name="gridTransparency" Value="0"/>
|
||||
<FCUInt Name="gridColor" Value="842157055"/>
|
||||
<FCUInt Name="snapcolor" Value="4294967295"/>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParameters>
|
||||
26
src/Gui/PreferencePackTemplates/Editor_Colors.cfg
Normal file
26
src/Gui/PreferencePackTemplates/Editor_Colors.cfg
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<FCParameters>
|
||||
<FCParamGroup Name="Root">
|
||||
<FCParamGroup Name="BaseApp">
|
||||
<FCParamGroup Name="Preferences">
|
||||
<FCParamGroup Name="Editor">
|
||||
<FCUInt Name="Text" Value="4294967040"/>
|
||||
<FCUInt Name="Bookmark" Value="15921664"/>
|
||||
<FCUInt Name="Breakpoint" Value="3992977408"/>
|
||||
<FCUInt Name="Keyword" Value="195493632"/>
|
||||
<FCUInt Name="Comment" Value="14483456"/>
|
||||
<FCUInt Name="Block comment" Value="2526452224"/>
|
||||
<FCUInt Name="Number" Value="1625816832"/>
|
||||
<FCUInt Name="String" Value="4283782400"/>
|
||||
<FCUInt Name="Character" Value="4281611264"/>
|
||||
<FCUInt Name="Class name" Value="4003397632"/>
|
||||
<FCUInt Name="Define name" Value="3784704000"/>
|
||||
<FCUInt Name="Operator" Value="3553875968"/>
|
||||
<FCUInt Name="Python output" Value="3705447680"/>
|
||||
<FCUInt Name="Python error" Value="4287203584"/>
|
||||
<FCUInt Name="Current line highlight" Value="774778368"/>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParameters>
|
||||
13
src/Gui/PreferencePackTemplates/Editor_Font.cfg
Normal file
13
src/Gui/PreferencePackTemplates/Editor_Font.cfg
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<FCParameters>
|
||||
<FCParamGroup Name="Root">
|
||||
<FCParamGroup Name="BaseApp">
|
||||
<FCParamGroup Name="Preferences">
|
||||
<FCParamGroup Name="Editor">
|
||||
<FCInt Name="FontSize" Value="10"/>
|
||||
<FCText Name="Font">MS Shell Dlg 2</FCText>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParameters>
|
||||
26
src/Gui/PreferencePackTemplates/Main_window_layout.cfg
Normal file
26
src/Gui/PreferencePackTemplates/Main_window_layout.cfg
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<FCParameters>
|
||||
<FCParamGroup Name="Root">
|
||||
<FCParamGroup Name="BaseApp">
|
||||
<FCParamGroup Name="MainWindow">
|
||||
<FCParamGroup Name="DockWindows">
|
||||
<FCBool Name="Std_SelectionView" Value="0"/>
|
||||
<FCBool Name="Std_ComboView" Value="1"/>
|
||||
<FCBool Name="Std_ReportView" Value="0"/>
|
||||
<FCBool Name="Std_PythonView" Value="0"/>
|
||||
<FCBool Name="Std_TreeView" Value="0"/>
|
||||
<FCBool Name="Std_PropertyView" Value="0"/>
|
||||
<FCBool Name="Std_DAGView" Value="0"/>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="Toolbars">
|
||||
<FCBool Name="File" Value="1"/>
|
||||
<FCBool Name="Workbench" Value="1"/>
|
||||
<FCBool Name="Macro" Value="1"/>
|
||||
<FCBool Name="View" Value="1"/>
|
||||
<FCBool Name="Structure" Value="1"/>
|
||||
<FCBool Name="Navigation" Value="1"/>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParameters>
|
||||
21
src/Gui/PreferencePackTemplates/Path_Colors.cfg
Normal file
21
src/Gui/PreferencePackTemplates/Path_Colors.cfg
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<FCParameters>
|
||||
<FCParamGroup Name="Root">
|
||||
<FCParamGroup Name="BaseApp">
|
||||
<FCParamGroup Name="Preferences">
|
||||
<FCParamGroup Name="Mod">
|
||||
<FCParamGroup Name="Path">
|
||||
<FCUInt Name="DefaultNormalPathColor" Value="11141375"/>
|
||||
<FCUInt Name="DefaultRapidPathColor" Value="2852126975"/>
|
||||
<FCUInt Name="DefaultPathMarkerColor" Value="1442775295"/>
|
||||
<FCUInt Name="DefaultExtentsColor" Value="3823363071"/>
|
||||
<FCUInt Name="DefaultProbePathColor" Value="4294903295"/>
|
||||
<FCUInt Name="DefaultHighlightPathColor" Value="4286382335"/>
|
||||
<FCUInt Name="DefaultBBoxSelectionColor" Value="3372220415"/>
|
||||
<FCUInt Name="DefaultBBoxNormalColor" Value="4294967295"/>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParameters>
|
||||
32
src/Gui/PreferencePackTemplates/Sketcher_Colors.cfg
Normal file
32
src/Gui/PreferencePackTemplates/Sketcher_Colors.cfg
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<FCParameters>
|
||||
<FCParamGroup Name="Root">
|
||||
<FCParamGroup Name="BaseApp">
|
||||
<FCParamGroup Name="Preferences">
|
||||
<FCParamGroup Name="View">
|
||||
<FCUInt Name="SketchEdgeColor" Value="4294967295"/>
|
||||
<FCUInt Name="SketchVertexColor" Value="4294967295"/>
|
||||
<FCUInt Name="EditedEdgeColor" Value="4294967295"/>
|
||||
<FCUInt Name="EditedVertexColor" Value="4280680703"/>
|
||||
<FCUInt Name="ConstructionColor" Value="56575"/>
|
||||
<FCUInt Name="ExternalColor" Value="3425924095"/>
|
||||
<FCUInt Name="InvalidSketchColor" Value="4285333759"/>
|
||||
<FCUInt Name="FullyConstrainedColor" Value="16711935"/>
|
||||
<FCUInt Name="InternalAlignedGeoColor" Value="2998042623"/>
|
||||
<FCUInt Name="FullyConstraintElementColor" Value="2161156351"/>
|
||||
<FCUInt Name="FullyConstraintConstructionElementColor" Value="2410282495"/>
|
||||
<FCUInt Name="FullyConstraintInternalAlignmentColor" Value="3739142399"/>
|
||||
<FCUInt Name="FullyConstraintConstructionPointColor" Value="4287987967"/>
|
||||
<FCUInt Name="ConstrainedIcoColor" Value="4280680703"/>
|
||||
<FCUInt Name="NonDrivingConstrDimColor" Value="2555903"/>
|
||||
<FCUInt Name="ConstrainedDimColor" Value="4280680703"/>
|
||||
<FCUInt Name="ExprBasedConstrDimColor" Value="4286523135"/>
|
||||
<FCUInt Name="DeactivatedConstrDimColor" Value="2139062271"/>
|
||||
<FCUInt Name="CursorTextColor" Value="65535"/>
|
||||
<FCUInt Name="CursorCrosshairColor" Value="4294967295"/>
|
||||
<FCUInt Name="CreateLineColor" Value="3435973887"/>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParameters>
|
||||
20
src/Gui/PreferencePackTemplates/Start_Colors.cfg
Normal file
20
src/Gui/PreferencePackTemplates/Start_Colors.cfg
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<FCParameters>
|
||||
<FCParamGroup Name="Root">
|
||||
<FCParamGroup Name="BaseApp">
|
||||
<FCParamGroup Name="Preferences">
|
||||
<FCParamGroup Name="Mod">
|
||||
<FCParamGroup Name="Start">
|
||||
<FCUInt Name="BackgroundColor1" Value="1414812927"/>
|
||||
<FCUInt Name="BackgroundTextColor" Value="4294967295"/>
|
||||
<FCUInt Name="PageColor" Value="690563583"/>
|
||||
<FCUInt Name="PageTextColor" Value="3789677055"/>
|
||||
<FCUInt Name="BoxColor" Value="1296911871"/>
|
||||
<FCUInt Name="LinkColor" Value="3099197439"/>
|
||||
<FCUInt Name="BackgroundColor2" Value="2141107711"/>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParameters>
|
||||
31
src/Gui/PreferencePackTemplates/TechDraw_Colors.cfg
Normal file
31
src/Gui/PreferencePackTemplates/TechDraw_Colors.cfg
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<FCParameters>
|
||||
<FCParamGroup Name="Root">
|
||||
<FCParamGroup Name="BaseApp">
|
||||
<FCParamGroup Name="Preferences">
|
||||
<FCParamGroup Name="Mod">
|
||||
<FCParamGroup Name="TechDraw">
|
||||
<FCParamGroup Name="Decorations">
|
||||
<FCUInt Name="SectionColor" Value="255"/>
|
||||
<FCUInt Name="CenterColor" Value="255"/>
|
||||
<FCUInt Name="VertexColor" Value="255"/>
|
||||
<FCUInt Name="HighlightColor" Value="255"/>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="Colors">
|
||||
<FCUInt Name="Hatch" Value="255"/>
|
||||
<FCUInt Name="Background" Value="3553874943"/>
|
||||
<FCUInt Name="PreSelectColor" Value="4294902015"/>
|
||||
<FCUInt Name="HiddenColor" Value="255"/>
|
||||
<FCUInt Name="SelectColor" Value="16711935"/>
|
||||
<FCUInt Name="NormalColor" Value="255"/>
|
||||
<FCUInt Name="CutSurfaceColor" Value="3553874943"/>
|
||||
<FCUInt Name="GeomHatch" Value="255"/>
|
||||
<FCUInt Name="FaceColor" Value="4294967295"/>
|
||||
<FCBool Name="ClearFace" Value="1"/>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParameters>
|
||||
31
src/Gui/PreferencePackTemplates/Window_Colors.cfg
Normal file
31
src/Gui/PreferencePackTemplates/Window_Colors.cfg
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<FCParameters>
|
||||
<FCParamGroup Name="Root">
|
||||
<FCParamGroup Name="BaseApp">
|
||||
<FCParamGroup Name="Preferences">
|
||||
<FCParamGroup Name="View">
|
||||
<FCUInt Name="BacklightColor" Value="3755991039"/>
|
||||
<FCUInt Name="BackgroundColor" Value="336897023"/>
|
||||
<FCUInt Name="BackgroundColor2" Value="1701144063"/>
|
||||
<FCUInt Name="BackgroundColor3" Value="757935615"/>
|
||||
<FCUInt Name="BackgroundColor4" Value="1869583359"/>
|
||||
<FCBool Name="Simple" Value="0"/>
|
||||
<FCBool Name="Gradient" Value="1"/>
|
||||
<FCBool Name="UseBackgroundColorMid" Value="0"/>
|
||||
<FCUInt Name="HighlightColor" Value="4294907903"/>
|
||||
<FCUInt Name="SelectionColor" Value="704588287"/>
|
||||
<FCUInt Name="DefaultShapeColor" Value="3435973887"/>
|
||||
<FCBool Name="RandomColor" Value="0"/>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="TreeView">
|
||||
<FCUInt Name="TreeEditColor" Value="4042260735"/>
|
||||
<FCUInt Name="TreeActiveColor" Value="3537037823"/>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="MainWindow">
|
||||
<FCBool Name="TiledBackground" Value="0"/>
|
||||
<FCText Name="StyleSheet"></FCText>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParameters>
|
||||
28
src/Gui/PreferencePacks/CMakeLists.txt
Normal file
28
src/Gui/PreferencePacks/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
SET(PreferencePacks_Files
|
||||
"package.xml"
|
||||
)
|
||||
|
||||
SET(PreferencePacks_Directories
|
||||
"FreeCAD Classic Colors"
|
||||
)
|
||||
|
||||
ADD_CUSTOM_TARGET(PreferencePacks_data ALL
|
||||
SOURCES ${PreferencePacks_Files} ${PreferencePacks_Directories}
|
||||
)
|
||||
|
||||
FILE(COPY ${PreferencePacks_Files} ${PreferencePacks_Directories} DESTINATION "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Gui/PreferencePacks")
|
||||
|
||||
INSTALL(
|
||||
FILES
|
||||
${PreferencePacks_Files}
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_DATADIR}/Gui/PreferencePacks
|
||||
)
|
||||
|
||||
INSTALL(
|
||||
DIRECTORY
|
||||
${PreferencePacks_Directories}
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_DATADIR}/Gui/PreferencePacks
|
||||
)
|
||||
@@ -0,0 +1,134 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<FCParameters>
|
||||
|
||||
<FCParamGroup Name="Root">
|
||||
<FCParamGroup Name="BaseApp">
|
||||
<FCParamGroup Name="Preferences">
|
||||
<FCParamGroup Name="Mod">
|
||||
<FCParamGroup Name="Arch">
|
||||
<FCUInt Name="WallColor" Value="3604403967"/>
|
||||
<FCUInt Name="StructureColor" Value="2527705855"/>
|
||||
<FCUInt Name="RebarColor" Value="3111475967"/>
|
||||
<FCUInt Name="WindowColor" Value="556614399"/>
|
||||
<FCUInt Name="WindowGlassColor" Value="1572326399"/>
|
||||
<FCUInt Name="PanelColor" Value="3416289279"/>
|
||||
<FCUInt Name="ColorHelpers" Value="674321151"/>
|
||||
<FCUInt Name="defaultSpaceColor" Value="4280090879"/>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="Draft">
|
||||
<FCInt Name="gridTransparency" Value="0"/>
|
||||
<FCUInt Name="constructioncolor" Value="746455039"/>
|
||||
<FCUInt Name="gridColor" Value="842157055"/>
|
||||
<FCUInt Name="snapcolor" Value="4294967295"/>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="Path">
|
||||
<FCUInt Name="DefaultNormalPathColor" Value="11141375"/>
|
||||
<FCUInt Name="DefaultRapidPathColor" Value="2852126975"/>
|
||||
<FCUInt Name="DefaultPathMarkerColor" Value="1442775295"/>
|
||||
<FCUInt Name="DefaultExtentsColor" Value="3823363071"/>
|
||||
<FCUInt Name="DefaultProbePathColor" Value="4294903295"/>
|
||||
<FCUInt Name="DefaultHighlightPathColor" Value="4286382335"/>
|
||||
<FCUInt Name="DefaultBBoxSelectionColor" Value="3372220415"/>
|
||||
<FCUInt Name="DefaultBBoxNormalColor" Value="4294967295"/>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="Start">
|
||||
<FCUInt Name="BackgroundColor1" Value="1331197183"/>
|
||||
<FCUInt Name="BackgroundTextColor" Value="4294703103"/>
|
||||
<FCUInt Name="PageColor" Value="4294967295"/>
|
||||
<FCUInt Name="PageTextColor" Value="255"/>
|
||||
<FCUInt Name="BoxColor" Value="3722305023"/>
|
||||
<FCUInt Name="LinkColor" Value="65535"/>
|
||||
<FCUInt Name="BackgroundColor2" Value="2141107711"/>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="TechDraw">
|
||||
<FCParamGroup Name="Decorations">
|
||||
<FCUInt Name="SectionColor" Value="255"/>
|
||||
<FCUInt Name="CenterColor" Value="255"/>
|
||||
<FCUInt Name="VertexColor" Value="255"/>
|
||||
<FCUInt Name="HighlightColor" Value="255"/>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="Colors">
|
||||
<FCBool Name="ClearFace" Value="1"/>
|
||||
<FCUInt Name="Hatch" Value="255"/>
|
||||
<FCUInt Name="Background" Value="3553874943"/>
|
||||
<FCUInt Name="PreSelectColor" Value="4294902015"/>
|
||||
<FCUInt Name="HiddenColor" Value="255"/>
|
||||
<FCUInt Name="SelectColor" Value="16711935"/>
|
||||
<FCUInt Name="NormalColor" Value="255"/>
|
||||
<FCUInt Name="CutSurfaceColor" Value="3553874943"/>
|
||||
<FCUInt Name="GeomHatch" Value="255"/>
|
||||
<FCUInt Name="FaceColor" Value="4294967295"/>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="OutputWindow">
|
||||
<FCUInt Name="colorText" Value="255"/>
|
||||
<FCUInt Name="colorLogging" Value="65535"/>
|
||||
<FCUInt Name="colorWarning" Value="4289331455"/>
|
||||
<FCUInt Name="colorError" Value="4278190335"/>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="Editor">
|
||||
<FCUInt Name="Text" Value="0"/>
|
||||
<FCUInt Name="Bookmark" Value="16776960"/>
|
||||
<FCUInt Name="Breakpoint" Value="4278190080"/>
|
||||
<FCUInt Name="Keyword" Value="65280"/>
|
||||
<FCUInt Name="Comment" Value="11141120"/>
|
||||
<FCUInt Name="Block comment" Value="2694882304"/>
|
||||
<FCUInt Name="Number" Value="65280"/>
|
||||
<FCUInt Name="String" Value="4278190080"/>
|
||||
<FCUInt Name="Character" Value="4278190080"/>
|
||||
<FCUInt Name="Class name" Value="4289331200"/>
|
||||
<FCUInt Name="Define name" Value="4289331200"/>
|
||||
<FCUInt Name="Operator" Value="2694882304"/>
|
||||
<FCUInt Name="Python output" Value="2863300352"/>
|
||||
<FCUInt Name="Python error" Value="4278190080"/>
|
||||
<FCUInt Name="Current line highlight" Value="3772833792"/>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="View">
|
||||
<FCUInt Name="SketchEdgeColor" Value="4294967295"/>
|
||||
<FCUInt Name="SketchVertexColor" Value="4294967295"/>
|
||||
<FCUInt Name="EditedEdgeColor" Value="4294967295"/>
|
||||
<FCUInt Name="EditedVertexColor" Value="4280680703"/>
|
||||
<FCUInt Name="ConstructionColor" Value="56575"/>
|
||||
<FCUInt Name="ExternalColor" Value="3425924095"/>
|
||||
<FCUInt Name="InvalidSketchColor" Value="4285333759"/>
|
||||
<FCUInt Name="FullyConstrainedColor" Value="16711935"/>
|
||||
<FCUInt Name="InternalAlignedGeoColor" Value="2998042623"/>
|
||||
<FCUInt Name="FullyConstraintElementColor" Value="2161156351"/>
|
||||
<FCUInt Name="FullyConstraintConstructionElementColor" Value="2410282495"/>
|
||||
<FCUInt Name="FullyConstraintInternalAlignmentColor" Value="3739142399"/>
|
||||
<FCUInt Name="FullyConstraintConstructionPointColor" Value="4287987967"/>
|
||||
<FCUInt Name="ConstrainedIcoColor" Value="4280680703"/>
|
||||
<FCUInt Name="NonDrivingConstrDimColor" Value="2555903"/>
|
||||
<FCUInt Name="ConstrainedDimColor" Value="4280680703"/>
|
||||
<FCUInt Name="ExprBasedConstrDimColor" Value="4286523135"/>
|
||||
<FCUInt Name="DeactivatedConstrDimColor" Value="2139062271"/>
|
||||
<FCUInt Name="CursorTextColor" Value="65535"/>
|
||||
<FCUInt Name="CursorCrosshairColor" Value="4294967295"/>
|
||||
<FCUInt Name="CreateLineColor" Value="3435973887"/>
|
||||
<FCBool Name="Simple" Value="0"/>
|
||||
<FCBool Name="Gradient" Value="1"/>
|
||||
<FCBool Name="UseBackgroundColorMid" Value="0"/>
|
||||
<FCBool Name="RandomColor" Value="0"/>
|
||||
<FCUInt Name="BacklightColor" Value="4294967295"/>
|
||||
<FCUInt Name="BackgroundColor" Value="336897023"/>
|
||||
<FCUInt Name="BackgroundColor2" Value="859006463"/>
|
||||
<FCUInt Name="BackgroundColor3" Value="2543299327"/>
|
||||
<FCUInt Name="BackgroundColor4" Value="1869583359"/>
|
||||
<FCUInt Name="HighlightColor" Value="3789624575"/>
|
||||
<FCUInt Name="SelectionColor" Value="481107199"/>
|
||||
<FCUInt Name="DefaultShapeColor" Value="3435973887"/>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="TreeView">
|
||||
<FCUInt Name="TreeEditColor" Value="4294902015"/>
|
||||
<FCUInt Name="TreeActiveColor" Value="3873898495"/>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="MainWindow">
|
||||
<FCBool Name="TiledBackground" Value="0"/>
|
||||
<FCText Name="StyleSheet"></FCText>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
</FCParamGroup>
|
||||
|
||||
</FCParameters>
|
||||
20
src/Gui/PreferencePacks/package.xml
Normal file
20
src/Gui/PreferencePacks/package.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<package format="1">
|
||||
<name>Built-In Preference Packs</name>
|
||||
<description>Preference Packs included with the FreeCAD distribution</description>
|
||||
<version>1.0.0</version>
|
||||
<maintainer email="email@freecadweb.org">No Maintainer</maintainer>
|
||||
<license file="../../LICENSE">LGPL2</license>
|
||||
<url type="repository">https://github.com/FreeCAD/FreeCAD</url>
|
||||
|
||||
<content>
|
||||
<preferencepack>
|
||||
<name>FreeCAD Classic Colors</name>
|
||||
<description>FreeCAD default colors for core app and included Mods.</description>
|
||||
<version>1.0.0</version>
|
||||
<tag>built-in</tag>
|
||||
<tag>colors</tag>
|
||||
</preferencepack>
|
||||
</content>
|
||||
|
||||
</package>
|
||||
Reference in New Issue
Block a user