Merge branch 'main' into bim-cleanup-license

This commit is contained in:
marcuspollio
2025-04-11 15:13:45 +02:00
committed by GitHub
699 changed files with 6809 additions and 155717 deletions
+3
View File
@@ -7,3 +7,6 @@
[submodule "src/3rdParty/GSL"]
path = src/3rdParty/GSL
url = https://github.com/microsoft/GSL
[submodule "src/Mod/AddonManager"]
path = src/Mod/AddonManager
url = https://github.com/FreeCAD/AddonManager.git
-1
View File
@@ -9,7 +9,6 @@ files: |
src/Main|
src/Tools|
tests/src|
src/Mod/AddonManager|
src/Mod/Assembly|
src/Mod/CAM|
src/Mod/Cloud|
+4
View File
@@ -13,6 +13,10 @@
"CMAKE_EXPORT_COMPILE_COMMANDS": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_POLICY_VERSION_MINIMUM": {
"type": "STRING",
"value": "3.5"
}
}
},
+3 -3
View File
@@ -130,7 +130,7 @@ if (OCC_FOUND)
else ()
list(APPEND OCC_LIBRARIES TKDESTEP TKDEIGES TKDEGLTF TKDESTL)
endif ()
message(STATUS "-- Found OpenCASCADE version: ${OCC_VERSION_STRING}")
message(STATUS "-- OpenCASCADE include directory: ${OCC_INCLUDE_DIR}")
message(STATUS "-- OpenCASCADE shared libraries directory: ${OCC_LIBRARY_DIR}")
message(STATUS "Found OpenCASCADE version: ${OCC_VERSION_STRING}")
message(STATUS " OpenCASCADE include directory: ${OCC_INCLUDE_DIR}")
message(STATUS " OpenCASCADE shared libraries directory: ${OCC_LIBRARY_DIR}")
endif ()
@@ -24,13 +24,15 @@ macro(SetGlobalCompilerAndLinkerSettings)
add_definitions(-D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR -DNOMINMAX)
add_compile_options(/Zm150 /bigobj)
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DFC_DEBUG")
# set default libs
set (CMAKE_C_STANDARD_LIBRARIES "kernel32.lib user32.lib gdi32.lib winspool.lib SHFolder.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib winmm.lib comsupp.lib Ws2_32.lib dbghelp.lib ")
set (CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES}")
# set linker flag /nodefaultlib
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB")
set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /NODEFAULTLIB")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB")
if (MSVC_VERSION LESS 1930) # Anything before VS 2022
# set default libs
set (CMAKE_C_STANDARD_LIBRARIES "kernel32.lib user32.lib gdi32.lib winspool.lib SHFolder.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib winmm.lib comsupp.lib Ws2_32.lib dbghelp.lib ")
set (CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES}")
# set linker flag /nodefaultlib
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB")
set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /NODEFAULTLIB")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB")
endif()
if(FREECAD_RELEASE_PDB)
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi")
set (CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG")
+112 -109
View File
@@ -177,8 +177,8 @@ namespace sp = std::placeholders;
// Application
//==========================================================================
Base::Reference<ParameterManager> App::Application::_pcSysParamMngr;
Base::Reference<ParameterManager> App::Application::_pcUserParamMngr;
Base::Reference<ParameterManager> Application::_pcSysParamMngr;
Base::Reference<ParameterManager> Application::_pcUserParamMngr;
Base::ConsoleObserverStd *Application::_pConsoleObserverStd = nullptr;
Base::ConsoleObserverFile *Application::_pConsoleObserverFile = nullptr;
@@ -475,25 +475,25 @@ Document* Application::newDocument(const char * proposedName, const char * propo
//NOLINTBEGIN
// clang-format off
// connect the signals to the application for the new document
doc->signalBeforeChange.connect(std::bind(&App::Application::slotBeforeChangeDocument, this, sp::_1, sp::_2));
doc->signalChanged.connect(std::bind(&App::Application::slotChangedDocument, this, sp::_1, sp::_2));
doc->signalNewObject.connect(std::bind(&App::Application::slotNewObject, this, sp::_1));
doc->signalDeletedObject.connect(std::bind(&App::Application::slotDeletedObject, this, sp::_1));
doc->signalBeforeChangeObject.connect(std::bind(&App::Application::slotBeforeChangeObject, this, sp::_1, sp::_2));
doc->signalChangedObject.connect(std::bind(&App::Application::slotChangedObject, this, sp::_1, sp::_2));
doc->signalRelabelObject.connect(std::bind(&App::Application::slotRelabelObject, this, sp::_1));
doc->signalActivatedObject.connect(std::bind(&App::Application::slotActivatedObject, this, sp::_1));
doc->signalUndo.connect(std::bind(&App::Application::slotUndoDocument, this, sp::_1));
doc->signalRedo.connect(std::bind(&App::Application::slotRedoDocument, this, sp::_1));
doc->signalRecomputedObject.connect(std::bind(&App::Application::slotRecomputedObject, this, sp::_1));
doc->signalRecomputed.connect(std::bind(&App::Application::slotRecomputed, this, sp::_1));
doc->signalBeforeRecompute.connect(std::bind(&App::Application::slotBeforeRecompute, this, sp::_1));
doc->signalOpenTransaction.connect(std::bind(&App::Application::slotOpenTransaction, this, sp::_1, sp::_2));
doc->signalCommitTransaction.connect(std::bind(&App::Application::slotCommitTransaction, this, sp::_1));
doc->signalAbortTransaction.connect(std::bind(&App::Application::slotAbortTransaction, this, sp::_1));
doc->signalStartSave.connect(std::bind(&App::Application::slotStartSaveDocument, this, sp::_1, sp::_2));
doc->signalFinishSave.connect(std::bind(&App::Application::slotFinishSaveDocument, this, sp::_1, sp::_2));
doc->signalChangePropertyEditor.connect(std::bind(&App::Application::slotChangePropertyEditor, this, sp::_1, sp::_2));
doc->signalBeforeChange.connect(std::bind(&Application::slotBeforeChangeDocument, this, sp::_1, sp::_2));
doc->signalChanged.connect(std::bind(&Application::slotChangedDocument, this, sp::_1, sp::_2));
doc->signalNewObject.connect(std::bind(&Application::slotNewObject, this, sp::_1));
doc->signalDeletedObject.connect(std::bind(&Application::slotDeletedObject, this, sp::_1));
doc->signalBeforeChangeObject.connect(std::bind(&Application::slotBeforeChangeObject, this, sp::_1, sp::_2));
doc->signalChangedObject.connect(std::bind(&Application::slotChangedObject, this, sp::_1, sp::_2));
doc->signalRelabelObject.connect(std::bind(&Application::slotRelabelObject, this, sp::_1));
doc->signalActivatedObject.connect(std::bind(&Application::slotActivatedObject, this, sp::_1));
doc->signalUndo.connect(std::bind(&Application::slotUndoDocument, this, sp::_1));
doc->signalRedo.connect(std::bind(&Application::slotRedoDocument, this, sp::_1));
doc->signalRecomputedObject.connect(std::bind(&Application::slotRecomputedObject, this, sp::_1));
doc->signalRecomputed.connect(std::bind(&Application::slotRecomputed, this, sp::_1));
doc->signalBeforeRecompute.connect(std::bind(&Application::slotBeforeRecompute, this, sp::_1));
doc->signalOpenTransaction.connect(std::bind(&Application::slotOpenTransaction, this, sp::_1, sp::_2));
doc->signalCommitTransaction.connect(std::bind(&Application::slotCommitTransaction, this, sp::_1));
doc->signalAbortTransaction.connect(std::bind(&Application::slotAbortTransaction, this, sp::_1));
doc->signalStartSave.connect(std::bind(&Application::slotStartSaveDocument, this, sp::_1, sp::_2));
doc->signalFinishSave.connect(std::bind(&Application::slotFinishSaveDocument, this, sp::_1, sp::_2));
doc->signalChangePropertyEditor.connect(std::bind(&Application::slotChangePropertyEditor, this, sp::_1, sp::_2));
// clang-format on
//NOLINTEND
@@ -514,7 +514,7 @@ Document* Application::newDocument(const char * proposedName, const char * propo
bool Application::closeDocument(const char* name)
{
auto pos = DocMap.find( name );
const auto pos = DocMap.find( name );
if (pos == DocMap.end()) // no such document
return false;
@@ -525,9 +525,10 @@ bool Application::closeDocument(const char* name)
signalDeleteDocument(*pos->second);
// For exception-safety use a smart pointer
if (_pActiveDoc == pos->second)
if (_pActiveDoc == pos->second) {
setActiveDocument(static_cast<Document*>(nullptr));
std::unique_ptr<Document> delDoc (pos->second);
}
const std::unique_ptr<Document> delDoc (pos->second);
DocMap.erase( pos );
DocFileMap.erase(Base::FileInfo(delDoc->FileName.getValue()).filePath());
@@ -547,7 +548,7 @@ void Application::closeAllDocuments()
closeDocument(pos->first.c_str());
}
App::Document* Application::getDocument(const char *Name) const
Document* Application::getDocument(const char *Name) const
{
const auto pos = DocMap.find(Name);
@@ -558,7 +559,7 @@ App::Document* Application::getDocument(const char *Name) const
return pos->second;
}
const char * Application::getDocumentName(const App::Document* doc) const
const char * Application::getDocumentName(const Document* doc) const
{
for (const auto & it : DocMap) {
if (it.second == doc) {
@@ -569,9 +570,9 @@ const char * Application::getDocumentName(const App::Document* doc) const
return nullptr;
}
std::vector<App::Document*> Application::getDocuments() const
std::vector<Document*> Application::getDocuments() const
{
std::vector<App::Document*> docs;
std::vector<Document*> docs;
docs.reserve(DocMap.size());
for (const auto & it : DocMap)
docs.push_back(it.second);
@@ -615,7 +616,7 @@ int Application::addPendingDocument(const char *FileName, const char *objName, b
assert(!Base::Tools::isNullOrEmpty(objName));
if(!_docReloadAttempts[FileName].emplace(objName).second)
return -1;
auto ret = _pendingDocMap.emplace(FileName,std::vector<std::string>());
const auto ret = _pendingDocMap.emplace(FileName,std::vector<std::string>());
ret.first->second.emplace_back(objName);
if(ret.second) {
_pendingDocs.emplace_back(ret.first->first.c_str());
@@ -675,28 +676,30 @@ Document* Application::openDocument(const char * FileName, DocumentInitFlags ini
Document *Application::getDocumentByPath(const char *path, PathMatchMode checkCanonical) const {
if(Base::Tools::isNullOrEmpty(path))
return nullptr;
if(DocFileMap.empty()) {
if (DocFileMap.empty()) {
for(const auto &v : DocMap) {
const auto &file = v.second->FileName.getStrValue();
if(!file.empty())
DocFileMap[Base::FileInfo(file.c_str()).filePath()] = v.second;
}
}
auto it = DocFileMap.find(Base::FileInfo(path).filePath());
const auto it = DocFileMap.find(Base::FileInfo(path).filePath());
if(it != DocFileMap.end())
return it->second;
if (checkCanonical == PathMatchMode::MatchAbsolute)
if (checkCanonical == PathMatchMode::MatchAbsolute) {
return nullptr;
}
std::string filepath = Base::FileInfo(path).filePath();
QString canonicalPath = QFileInfo(QString::fromUtf8(path)).canonicalFilePath();
const std::string filepath = Base::FileInfo(path).filePath();
const QString canonicalPath = QFileInfo(QString::fromUtf8(path)).canonicalFilePath();
for (const auto &v : DocMap) {
QFileInfo fi(QString::fromUtf8(v.second->FileName.getValue()));
if (canonicalPath == fi.canonicalFilePath()) {
if (checkCanonical == PathMatchMode::MatchCanonical)
if (checkCanonical == PathMatchMode::MatchCanonical) {
return v.second;
bool samePath = (canonicalPath == QString::fromUtf8(filepath.c_str()));
}
const bool samePath = (canonicalPath == QString::fromUtf8(filepath.c_str()));
FC_WARN("Identical physical path '" << canonicalPath.toUtf8().constData() << "'\n"
<< (samePath?"":" for file '") << (samePath?"":filepath.c_str()) << (samePath?"":"'\n")
<< " with existing document '" << v.second->Label.getValue()
@@ -742,7 +745,7 @@ std::vector<Document*> Application::openDocuments(const std::vector<std::string>
int pass = 0;
do {
std::set<App::DocumentT> newDocs;
std::set<DocumentT> newDocs;
for (std::size_t count=0;; ++count) {
std::string name = std::move(_pendingDocs.front());
_pendingDocs.pop_front();
@@ -928,8 +931,8 @@ Document* Application::openDocumentPrivate(const char * FileName,
// Before creating a new document we check whether the document is already open
auto doc = getDocumentByPath(File.filePath().c_str(), PathMatchMode::MatchCanonicalWarning);
if(doc) {
if(doc->testStatus(App::Document::PartialDoc)
|| doc->testStatus(App::Document::PartialRestore)) {
if(doc->testStatus(Document::PartialDoc)
|| doc->testStatus(Document::PartialRestore)) {
// Here means a document is already partially loaded, but the document
// is requested again, either partial or not. We must check if the
// document contains the required object
@@ -942,7 +945,7 @@ Document* Application::openDocumentPrivate(const char * FileName,
bool reopen = false;
for(const auto &name : objNames) {
auto obj = doc->getObject(name.c_str());
if(!obj || obj->testStatus(App::PartialObject)) {
if(!obj || obj->testStatus(PartialObject)) {
reopen = true;
// NOTE: We are about to reload this document with
// extra objects. However, it is possible to repeat
@@ -1038,7 +1041,7 @@ void Application::setActiveDocumentNoSignal(Document* pDoc)
// make sure that the active document is set in case no GUI is up
if (pDoc) {
Base::PyGILStateLocker lock;
Py::Object active(pDoc->getPyObject(), true);
const Py::Object active(pDoc->getPyObject(), true);
Py::Module("FreeCAD").setAttr(std::string("ActiveDocument"), active);
}
else {
@@ -1093,9 +1096,9 @@ Application::TransactionSignaller::~TransactionSignaller() {
int64_t Application::applicationPid()
{
static int64_t randomNumber = []() {
auto tp = std::chrono::high_resolution_clock::now();
auto dur = tp.time_since_epoch();
auto seed = dur.count();
const auto tp = std::chrono::high_resolution_clock::now();
const auto dur = tp.time_since_epoch();
const auto seed = dur.count();
std::mt19937 generator(static_cast<unsigned>(seed));
constexpr int64_t minValue {1};
constexpr int64_t maxValue {1000000};
@@ -1118,7 +1121,7 @@ std::string Application::getExecutableName()
std::string Application::getNameWithVersion()
{
auto appname = QCoreApplication::applicationName().toStdString();
auto config = App::Application::Config();
auto config = Application::Config();
auto major = config["BuildVersionMajor"];
auto minor = config["BuildVersionMinor"];
auto point = config["BuildVersionPoint"];
@@ -1162,7 +1165,7 @@ std::string Application::getResourceDir()
// #6892: Conda may inject null characters => remove them using c_str()
std::string path = std::string(RESOURCEDIR).c_str();
path += PATHSEP;
QDir dir(QString::fromStdString(path));
const QDir dir(QString::fromStdString(path));
if (dir.isAbsolute())
return path;
return mConfig["AppHomePath"] + path;
@@ -1176,7 +1179,7 @@ std::string Application::getLibraryDir()
#ifdef LIBRARYDIR
// #6892: Conda may inject null characters => remove them using c_str()
std::string path = std::string(LIBRARYDIR).c_str();
QDir dir(QString::fromStdString(path));
const QDir dir(QString::fromStdString(path));
if (dir.isAbsolute())
return path;
return mConfig["AppHomePath"] + path;
@@ -1191,7 +1194,7 @@ std::string Application::getHelpDir()
// #6892: Conda may inject null characters => remove them using c_str()
std::string path = std::string(DOCDIR).c_str();
path += PATHSEP;
QDir dir(QString::fromStdString(path));
const QDir dir(QString::fromStdString(path));
if (dir.isAbsolute())
return path;
return mConfig["AppHomePath"] + path;
@@ -1204,13 +1207,13 @@ int Application::checkLinkDepth(int depth, MessageOption option)
{
if (_objCount < 0) {
_objCount = 0;
for (auto &v : DocMap) {
for (const auto &v : DocMap) {
_objCount += v.second->countObjects();
}
}
if (depth > _objCount + 2) {
auto msg = "Link recursion limit reached. "
const auto msg = "Link recursion limit reached. "
"Please check for cyclic reference.";
switch (option) {
case MessageOption::Quiet:
@@ -1238,7 +1241,7 @@ std::set<DocumentObject *> Application::getLinksTo(
}
} else {
std::set<Document*> docs;
for(auto o : obj->getInList()) {
for (const auto o : obj->getInList()) {
if(o && o->isAttachedToDocument() && docs.insert(o->getDocument()).second) {
o->getDocument()->getLinksTo(links,obj,options,maxCount);
if(maxCount && static_cast<int>(links.size())>=maxCount)
@@ -1265,7 +1268,7 @@ ParameterManager & Application::GetUserParameter()
ParameterManager * Application::GetParameterSet(const char* sName) const
{
auto it = mpcPramManager.find(sName);
const auto it = mpcPramManager.find(sName);
return it != mpcPramManager.end() ? it->second : nullptr;
}
@@ -1278,7 +1281,7 @@ Application::GetParameterSetList() const
void Application::AddParameterSet(const char* sName)
{
auto it = mpcPramManager.find(sName);
const auto it = mpcPramManager.find(sName);
if ( it != mpcPramManager.end() )
return;
mpcPramManager[sName] = ParameterManager::Create();
@@ -1286,7 +1289,7 @@ void Application::AddParameterSet(const char* sName)
void Application::RemoveParameterSet(const char* sName)
{
auto it = mpcPramManager.find(sName);
const auto it = mpcPramManager.find(sName);
// Must not delete user or system parameter
if ( it == mpcPramManager.end() || it->second == _pcUserParamMngr || it->second == _pcSysParamMngr )
return;
@@ -1295,9 +1298,9 @@ void Application::RemoveParameterSet(const char* sName)
Base::Reference<ParameterGrp> Application::GetParameterGroupByPath(const char* sName)
{
std::string cName = sName,cTemp;
std::string cName = sName, cTemp;
std::string::size_type pos = cName.find(':');
const std::string::size_type pos = cName.find(':');
// is there a path separator ?
if (pos == std::string::npos) {
@@ -1308,7 +1311,7 @@ Base::Reference<ParameterGrp> Application::GetParameterGroupByPath(const char*
cName.erase(0,pos+1);
// test if name is valid
auto It = mpcPramManager.find(cTemp);
const auto It = mpcPramManager.find(cTemp);
if (It == mpcPramManager.end())
throw Base::ValueError("Application::GetParameterGroupByPath() unknown parameter set name specified");
@@ -1324,8 +1327,8 @@ void Application::addImportType(const char* Type, const char* ModuleName)
// Extract each filetype from 'Type' literal
std::string::size_type pos = item.filter.find("*.");
while ( pos != std::string::npos ) {
std::string::size_type next = item.filter.find_first_of(" )", pos+1);
std::string::size_type len = next-pos-2;
const std::string::size_type next = item.filter.find_first_of(" )", pos + 1);
const std::string::size_type len = next-pos-2;
std::string type = item.filter.substr(pos+2,len);
item.types.push_back(std::move(type));
pos = item.filter.find("*.", next);
@@ -1449,8 +1452,8 @@ void Application::addExportType(const char* Type, const char* ModuleName)
// Extract each filetype from 'Type' literal
std::string::size_type pos = item.filter.find("*.");
while ( pos != std::string::npos ) {
std::string::size_type next = item.filter.find_first_of(" )", pos+1);
std::string::size_type len = next-pos-2;
const std::string::size_type next = item.filter.find_first_of(" )", pos + 1);
const std::string::size_type len = next-pos-2;
std::string type = item.filter.substr(pos+2,len);
item.types.push_back(std::move(type));
pos = item.filter.find("*.", next);
@@ -1567,54 +1570,54 @@ std::map<std::string, std::string> Application::getExportFilters() const
//**************************************************************************
// signaling
void Application::slotBeforeChangeDocument(const App::Document& doc, const Property& prop)
void Application::slotBeforeChangeDocument(const Document& doc, const Property& prop)
{
this->signalBeforeChangeDocument(doc, prop);
}
void Application::slotChangedDocument(const App::Document& doc, const Property& prop)
void Application::slotChangedDocument(const Document& doc, const Property& prop)
{
this->signalChangedDocument(doc, prop);
}
void Application::slotNewObject(const App::DocumentObject& obj)
void Application::slotNewObject(const DocumentObject& obj)
{
this->signalNewObject(obj);
_objCount = -1;
}
void Application::slotDeletedObject(const App::DocumentObject& obj)
void Application::slotDeletedObject(const DocumentObject& obj)
{
this->signalDeletedObject(obj);
_objCount = -1;
}
void Application::slotBeforeChangeObject(const App::DocumentObject& obj, const App::Property& prop)
void Application::slotBeforeChangeObject(const DocumentObject& obj, const Property& prop)
{
this->signalBeforeChangeObject(obj, prop);
}
void Application::slotChangedObject(const App::DocumentObject& obj, const App::Property& prop)
void Application::slotChangedObject(const DocumentObject& obj, const Property& prop)
{
this->signalChangedObject(obj, prop);
}
void Application::slotRelabelObject(const App::DocumentObject& obj)
void Application::slotRelabelObject(const DocumentObject& obj)
{
this->signalRelabelObject(obj);
}
void Application::slotActivatedObject(const App::DocumentObject& obj)
void Application::slotActivatedObject(const DocumentObject& obj)
{
this->signalActivatedObject(obj);
}
void Application::slotUndoDocument(const App::Document& doc)
void Application::slotUndoDocument(const Document& doc)
{
this->signalUndoDocument(doc);
}
void Application::slotRedoDocument(const App::Document& doc)
void Application::slotRedoDocument(const Document& doc)
{
this->signalRedoDocument(doc);
}
@@ -1649,18 +1652,18 @@ void Application::slotAbortTransaction(const Document& doc)
this->signalAbortTransaction(doc);
}
void Application::slotStartSaveDocument(const App::Document& doc, const std::string& filename)
void Application::slotStartSaveDocument(const Document& doc, const std::string& filename)
{
this->signalStartSaveDocument(doc, filename);
}
void Application::slotFinishSaveDocument(const App::Document& doc, const std::string& filename)
void Application::slotFinishSaveDocument(const Document& doc, const std::string& filename)
{
DocFileMap.clear();
this->signalFinishSaveDocument(doc, filename);
}
void Application::slotChangePropertyEditor(const App::Document& doc, const App::Property& prop)
void Application::slotChangePropertyEditor(const Document& doc, const Property& prop)
{
this->signalChangePropertyEditor(doc, prop);
}
@@ -2543,24 +2546,24 @@ void Application::initConfig(int argc, char ** argv)
// Version of the application extracted from SubWCRef into src/Build/Version.h
// We only set these keys if not yet defined. Therefore it suffices to search
// only for 'BuildVersionMajor'.
if (App::Application::Config().find("BuildVersionMajor") == App::Application::Config().end()) {
if (Application::Config().find("BuildVersionMajor") == Application::Config().end()) {
std::stringstream str;
str << FCVersionMajor
<< "." << FCVersionMinor
<< "." << FCVersionPoint;
App::Application::Config()["ExeVersion" ] = str.str();
App::Application::Config()["BuildVersionMajor" ] = FCVersionMajor;
App::Application::Config()["BuildVersionMinor" ] = FCVersionMinor;
App::Application::Config()["BuildVersionPoint" ] = FCVersionPoint;
App::Application::Config()["BuildVersionSuffix" ] = FCVersionSuffix;
App::Application::Config()["BuildRevision" ] = FCRevision;
App::Application::Config()["BuildRepositoryURL" ] = FCRepositoryURL;
App::Application::Config()["BuildRevisionDate" ] = FCRevisionDate;
Application::Config()["ExeVersion" ] = str.str();
Application::Config()["BuildVersionMajor" ] = FCVersionMajor;
Application::Config()["BuildVersionMinor" ] = FCVersionMinor;
Application::Config()["BuildVersionPoint" ] = FCVersionPoint;
Application::Config()["BuildVersionSuffix" ] = FCVersionSuffix;
Application::Config()["BuildRevision" ] = FCRevision;
Application::Config()["BuildRepositoryURL" ] = FCRepositoryURL;
Application::Config()["BuildRevisionDate" ] = FCRevisionDate;
#if defined(FCRepositoryHash)
App::Application::Config()["BuildRevisionHash" ] = FCRepositoryHash;
Application::Config()["BuildRevisionHash" ] = FCRepositoryHash;
#endif
#if defined(FCRepositoryBranch)
App::Application::Config()["BuildRevisionBranch"] = FCRepositoryBranch;
Application::Config()["BuildRevisionBranch"] = FCRepositoryBranch;
#endif
}
@@ -2574,7 +2577,7 @@ void Application::initConfig(int argc, char ** argv)
if (fi.exists() && brand.readFile(fi.absoluteFilePath())) {
Branding::XmlConfig cfg = brand.getUserDefines();
for (Branding::XmlConfig::iterator it = cfg.begin(); it != cfg.end(); ++it) {
App::Application::Config()[it.key()] = it.value();
Application::Config()[it.key()] = it.value();
}
}
@@ -2782,7 +2785,7 @@ void Application::initConfig(int argc, char ** argv)
void Application::SaveEnv(const char* s)
{
char *c = getenv(s);
const char *c = getenv(s);
if (c)
mConfig[s] = c;
}
@@ -2801,16 +2804,15 @@ void Application::initApplication()
Application::_pcSingleton = new Application(mConfig);
// set up Unit system default
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath
const ParameterGrp::handle hGrp = GetApplication().GetParameterGroupByPath
("User parameter:BaseApp/Preferences/Units");
Base::UnitsApi::setSchema(static_cast<Base::UnitSystem>(hGrp->GetInt("UserSchema", 0)));
Base::UnitsApi::setDecimals(static_cast<int>(hGrp->GetInt("Decimals", Base::UnitsApi::getDecimals())));
// In case we are using fractional inches, get user setting for min unit
int denom = static_cast<int>(hGrp->GetInt("FracInch", Base::QuantityFormat::getDefaultDenominator()));
const int denom = static_cast<int>(hGrp->GetInt("FracInch", Base::QuantityFormat::getDefaultDenominator()));
Base::QuantityFormat::setDefaultDenominator(denom);
#if defined (_DEBUG)
Base::Console().Log("Application is built with debug information\n");
#endif
@@ -2881,7 +2883,7 @@ std::list<std::string> Application::processFiles(const std::list<std::string>& f
}
else {
std::string ext = file.extension();
std::vector<std::string> mods = App::GetApplication().getImportModules(ext.c_str());
std::vector<std::string> mods = GetApplication().getImportModules(ext.c_str());
if (!mods.empty()) {
std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(file.filePath().c_str());
escapedstr = Base::Tools::escapeEncodeFilename(escapedstr);
@@ -2915,8 +2917,8 @@ std::list<std::string> Application::processFiles(const std::list<std::string>& f
void Application::processCmdLineFiles()
{
// process files passed to command line
std::list<std::string> files = getCmdLineFiles();
std::list<std::string> processed = processFiles(files);
const std::list<std::string> files = getCmdLineFiles();
const std::list<std::string> processed = processFiles(files);
if (files.empty()) {
if (mConfig["RunMode"] == "Exit")
@@ -2925,7 +2927,7 @@ void Application::processCmdLineFiles()
else if (processed.empty() && files.size() == 1 && mConfig["RunMode"] == "Cmd") {
// In case we are in console mode and the argument is not a file but Python code
// then execute it. This is to behave like the standard Python executable.
Base::FileInfo file(files.front());
const Base::FileInfo file(files.front());
if (!file.exists()) {
Base::Interpreter().runString(files.front().c_str());
mConfig["RunMode"] = "Exit";
@@ -2933,15 +2935,15 @@ void Application::processCmdLineFiles()
}
const std::map<std::string, std::string>& cfg = Application::Config();
auto it = cfg.find("SaveFile");
const auto it = cfg.find("SaveFile");
if (it != cfg.end()) {
std::string output = it->second;
output = Base::Tools::escapeEncodeFilename(output);
Base::FileInfo fi(output);
std::string ext = fi.extension();
const Base::FileInfo fi(output);
const std::string ext = fi.extension();
try {
std::vector<std::string> mods = App::GetApplication().getExportModules(ext.c_str());
const std::vector<std::string> mods = GetApplication().getExportModules(ext.c_str());
if (!mods.empty()) {
Base::Interpreter().loadModule(mods.front().c_str());
Base::Interpreter().runStringArg("import %s",mods.front().c_str());
@@ -2986,7 +2988,7 @@ void Application::runApplication()
void Application::logStatus()
{
std::string time_str = boost::posix_time::to_simple_string(
const std::string time_str = boost::posix_time::to_simple_string(
boost::posix_time::second_clock::local_time());
Base::Console().Log("Time = %s\n", time_str.c_str());
@@ -3034,14 +3036,14 @@ void Application::LoadParameters()
if (_pcUserParamMngr->LoadOrCreateDocument() && mConfig["Verbose"] != "Strict") {
// The user parameter file doesn't exist. When an alternative parameter file is offered
// this will be used.
auto it = mConfig.find("UserParameterTemplate");
const auto it = mConfig.find("UserParameterTemplate");
if (it != mConfig.end()) {
QString path = QString::fromUtf8(it->second.c_str());
if (QDir(path).isRelative()) {
QString home = QString::fromUtf8(mConfig["AppHomePath"].c_str());
const QString home = QString::fromUtf8(mConfig["AppHomePath"].c_str());
path = QFileInfo(QDir(home), path).absoluteFilePath();
}
QFileInfo fi(path);
const QFileInfo fi(path);
if (fi.exists()) {
_pcUserParamMngr->LoadDocument(path.toUtf8().constData());
}
@@ -3097,7 +3099,7 @@ QString getUserHome()
struct passwd *result;
const std::size_t buflen = 16384;
std::vector<char> buffer(buflen);
int error = getpwuid_r(getuid(), &pwd, buffer.data(), buffer.size(), &result);
const int error = getpwuid_r(getuid(), &pwd, buffer.data(), buffer.size(), &result);
Q_UNUSED(error)
if (!result)
throw Base::RuntimeError("Getting HOME path from system failed!");
@@ -3225,14 +3227,14 @@ std::filesystem::path findPath(const QString& stdHome, const QString& customHome
*/
std::tuple<QString, QString, QString> getCustomPaths()
{
QProcessEnvironment env(QProcessEnvironment::systemEnvironment());
const QProcessEnvironment env(QProcessEnvironment::systemEnvironment());
QString userHome = env.value(QStringLiteral("FREECAD_USER_HOME"));
QString userData = env.value(QStringLiteral("FREECAD_USER_DATA"));
QString userTemp = env.value(QStringLiteral("FREECAD_USER_TEMP"));
auto toNativePath = [](QString& path) {
if (!path.isEmpty()) {
QDir dir(path);
const QDir dir(path);
if (dir.exists())
path = QDir::toNativeSeparators(dir.canonicalPath());
else
@@ -3252,9 +3254,9 @@ std::tuple<QString, QString, QString> getCustomPaths()
// if FREECAD_USER_HOME is set but not FREECAD_USER_TEMP
if (!userHome.isEmpty() && userTemp.isEmpty()) {
QDir dir(userHome);
const QDir dir(userHome);
dir.mkdir(QStringLiteral("temp"));
QFileInfo fi(dir, QStringLiteral("temp"));
const QFileInfo fi(dir, QStringLiteral("temp"));
userTemp = fi.absoluteFilePath();
}
@@ -3484,11 +3486,12 @@ std::string Application::FindHomePath(const char* sCall)
if (const auto buf = new char[++sz]; _NSGetExecutablePath(buf, &sz) == 0) {
char resolved[PATH_MAX];
char* path = realpath(buf, resolved);
const char* path = realpath(buf, resolved);
delete [] buf;
if (path) {
std::string Call(resolved), TempHomePath;
const std::string Call(resolved);
std::string TempHomePath;
std::string::size_type pos = Call.find_last_of(PATHSEP);
TempHomePath.assign(Call,0,pos);
pos = TempHomePath.find_last_of(PATHSEP);
+13 -22
View File
@@ -39,7 +39,7 @@ PROPERTY_SOURCE(App::DatumElement, App::GeoFeature)
PROPERTY_SOURCE(App::Plane, App::DatumElement)
PROPERTY_SOURCE(App::Line, App::DatumElement)
PROPERTY_SOURCE(App::Point, App::DatumElement)
PROPERTY_SOURCE(App::LocalCoordinateSystem, App::GeoFeature)
PROPERTY_SOURCE_WITH_EXTENSIONS(App::LocalCoordinateSystem, App::GeoFeature)
DatumElement::DatumElement(bool hideRole)
: baseDir{0.0, 0.0, 1.0}
@@ -125,7 +125,6 @@ Point::Point()
// ----------------------------------------------------------------------------
LocalCoordinateSystem::LocalCoordinateSystem()
: extension(this)
{
ADD_PROPERTY_TYPE(OriginFeatures,
(nullptr),
@@ -133,8 +132,11 @@ LocalCoordinateSystem::LocalCoordinateSystem()
App::Prop_Hidden,
"Axis and baseplanes controlled by the LCS");
Group.setStatus(Property::Transient, true);
setStatus(App::NoAutoExpand, true);
extension.initExtension(this);
GroupExtension::initExtension(this);
}
@@ -201,12 +203,6 @@ App::Point* LocalCoordinateSystem::getPoint(const char* role) const
throw Base::RuntimeError(err.str().c_str());
}
bool LocalCoordinateSystem::hasObject(const DocumentObject* obj) const
{
const auto& features = OriginFeatures.getValues();
return std::ranges::find(features, obj) != features.end();
}
short LocalCoordinateSystem::mustExecute() const
{
if (OriginFeatures.isTouched()) {
@@ -340,18 +336,7 @@ void LocalCoordinateSystem::migrateOriginPoint()
// ----------------------------------------------------------------------------
LocalCoordinateSystem::LCSExtension::LCSExtension(LocalCoordinateSystem* obj)
: obj(obj)
{
Group.setStatus(Property::Transient, true);
}
void LocalCoordinateSystem::LCSExtension::initExtension(ExtensionContainer* obj)
{
App::GroupExtension::initExtension(obj);
}
bool LocalCoordinateSystem::LCSExtension::extensionGetSubObject(DocumentObject*& ret,
bool LocalCoordinateSystem::extensionGetSubObject(DocumentObject*& ret,
const char* subname,
PyObject** pyobj,
Base::Matrix4D* mat,
@@ -380,7 +365,7 @@ bool LocalCoordinateSystem::LCSExtension::extensionGetSubObject(DocumentObject*&
}
try {
ret = obj->getDatumElement(name.c_str());
ret = getDatumElement(name.c_str());
if (!ret) {
return false;
}
@@ -399,3 +384,9 @@ bool LocalCoordinateSystem::LCSExtension::extensionGetSubObject(DocumentObject*&
return false;
}
}
bool LocalCoordinateSystem::hasObject(const DocumentObject* obj, [[maybe_unused]] bool recursive) const
{
const auto& features = OriginFeatures.getValues();
return std::ranges::find(features, obj) != features.end();
}
+13 -22
View File
@@ -102,10 +102,9 @@ public:
}
};
class AppExport LocalCoordinateSystem: public App::GeoFeature
class AppExport LocalCoordinateSystem: public App::GeoFeature, public App::GeoFeatureGroupExtension
{
PROPERTY_HEADER_WITH_OVERRIDE(App::LocalCoordinateSystem);
PROPERTY_HEADER_WITH_EXTENSIONS(App::LocalCoordinateSystem);
Q_DECLARE_TR_FUNCTIONS(App::LocalCoordinateSystem)
public:
@@ -195,9 +194,6 @@ public:
App::Point* getPoint(const char* role) const;
///@}
/// Returns true if the given object is part of the origin
bool hasObject(const DocumentObject* obj) const;
/// Returns true on changing DatumElement set
short mustExecute() const override;
@@ -216,6 +212,17 @@ public:
// Axis links
PropertyLinkList OriginFeatures;
// GeoFeatureGroupExtension overrides:
bool extensionGetSubObject(DocumentObject*& ret,
const char* subname,
PyObject**,
Base::Matrix4D*,
bool,
int) const override;
// Reimplement the hasObject because LCS doesn't use Group but stores objects in OriginFeatures for whatever reason.
bool hasObject(const DocumentObject* obj, bool recursive = false) const override;
protected:
/// Checks integrity of the LCS
App::DocumentObjectExecReturn* execute() override;
@@ -229,22 +236,6 @@ private:
struct SetupData;
void setupDatumElement(App::PropertyLink& featProp, const SetupData& data);
class LCSExtension: public GeoFeatureGroupExtension
{
LocalCoordinateSystem* obj;
public:
explicit LCSExtension(LocalCoordinateSystem* obj);
void initExtension(ExtensionContainer* obj) override;
bool extensionGetSubObject(DocumentObject*& ret,
const char* subname,
PyObject**,
Base::Matrix4D*,
bool,
int) const override;
};
LCSExtension extension;
struct SetupData
{
Base::Type type;
+18 -5
View File
@@ -1630,9 +1630,9 @@ std::vector<App::DocumentObject*> Document::importObjects(Base::XMLReader& reade
o->setStatus(App::ObjImporting, true);
FC_LOG("importing " << o->getFullName());
if (auto propUUID =
Base::freecad_dynamic_cast<PropertyUUID>(o->getPropertyByName("_ObjectUUID"))) {
freecad_cast<PropertyUUID>(o->getPropertyByName("_ObjectUUID"))) {
auto propSource =
Base::freecad_dynamic_cast<PropertyUUID>(o->getPropertyByName("_SourceUUID"));
freecad_cast<PropertyUUID>(o->getPropertyByName("_SourceUUID"));
if (!propSource) {
propSource = static_cast<PropertyUUID*>(
o->addDynamicProperty("App::PropertyUUID",
@@ -2439,7 +2439,7 @@ bool Document::afterRestore(const std::vector<DocumentObject*>& objArray, bool c
// refresh properties in case the object changes its property list
obj->getPropertyList(props);
for (auto prop : props) {
auto link = Base::freecad_dynamic_cast<PropertyLinkBase>(prop);
auto link = freecad_cast<PropertyLinkBase>(prop);
int res;
std::string errMsg;
if (link && (res = link->checkRestore(&errMsg))) {
@@ -3987,7 +3987,7 @@ Document::importLinks(const std::vector<App::DocumentObject*>& objArray)
propList.clear();
obj->getPropertyList(propList);
for (auto prop : propList) {
auto linkProp = Base::freecad_dynamic_cast<PropertyLinkBase>(prop);
auto linkProp = freecad_cast<PropertyLinkBase>(prop);
if (linkProp && !prop->testStatus(Property::Immutable) && !obj->isReadOnly(prop)) {
auto copy = linkProp->CopyOnImportExternal(nameMap);
if (copy) {
@@ -4154,7 +4154,6 @@ const std::vector<DocumentObject*>& Document::getObjects() const
return d->objectArray;
}
std::vector<DocumentObject*> Document::getObjectsOfType(const Base::Type& typeId) const
{
std::vector<DocumentObject*> Objects;
@@ -4166,6 +4165,20 @@ std::vector<DocumentObject*> Document::getObjectsOfType(const Base::Type& typeId
return Objects;
}
std::vector<DocumentObject*> Document::getObjectsOfType(const std::vector<Base::Type>& types) const
{
std::vector<DocumentObject*> Objects;
for (auto it : d->objectArray) {
for (auto& typeId : types) {
if (it->isDerivedFrom(typeId)) {
Objects.push_back(it);
break; // Prevent adding several times the same object.
}
}
}
return Objects;
}
std::vector<DocumentObject*> Document::getObjectsWithExtension(const Base::Type& typeId,
bool derived) const
{
+1
View File
@@ -357,6 +357,7 @@ public:
/// Returns a list of all Objects
const std::vector<DocumentObject*>& getObjects() const;
std::vector<DocumentObject*> getObjectsOfType(const Base::Type& typeId) const;
std::vector<DocumentObject*> getObjectsOfType(const std::vector<Base::Type>& types) const;
/// Returns all object with given extensions. If derived=true also all objects with extensions
/// derived from the given one
std::vector<DocumentObject*> getObjectsWithExtension(const Base::Type& typeId,
+4 -4
View File
@@ -949,7 +949,7 @@ DocumentObject* DocumentObject::getSubObject(const char* subname,
// objects (think of the claimed children of a Fusion). But I do think we
// should change that.
if (transform && mat) {
auto pla = Base::freecad_dynamic_cast<PropertyPlacement>(getPropertyByName("Placement"));
auto pla = freecad_cast<PropertyPlacement>(getPropertyByName("Placement"));
if (pla) {
*mat *= pla->getValue().toMatrix();
}
@@ -1476,7 +1476,7 @@ bool DocumentObject::adjustRelativeLinks(const std::set<App::DocumentObject*>& i
std::vector<Property*> props;
getPropertyList(props);
for (auto prop : props) {
auto linkProp = Base::freecad_dynamic_cast<PropertyLinkBase>(prop);
auto linkProp = freecad_cast<PropertyLinkBase>(prop);
if (linkProp && linkProp->adjustLink(inList)) {
touched = true;
}
@@ -1495,7 +1495,7 @@ bool DocumentObject::adjustRelativeLinks(const std::set<App::DocumentObject*>& i
std::string DocumentObject::getElementMapVersion(const App::Property* _prop, bool restored) const
{
auto prop = Base::freecad_dynamic_cast<const PropertyComplexGeoData>(_prop);
auto prop = freecad_cast<const PropertyComplexGeoData>(_prop);
if (!prop) {
return std::string();
}
@@ -1504,7 +1504,7 @@ std::string DocumentObject::getElementMapVersion(const App::Property* _prop, boo
bool DocumentObject::checkElementMapVersion(const App::Property* _prop, const char* ver) const
{
auto prop = Base::freecad_dynamic_cast<const PropertyComplexGeoData>(_prop);
auto prop = freecad_cast<const PropertyComplexGeoData>(_prop);
if (!prop) {
return false;
}
+3 -3
View File
@@ -148,12 +148,12 @@ public:
template<typename T>
inline T* getObjectAs() const
{
return Base::freecad_dynamic_cast<T>(getObject());
return freecad_cast<T>(getObject());
}
template<typename T>
inline T* getPropertyAs() const
{
return Base::freecad_dynamic_cast<T>(getProperty());
return freecad_cast<T>(getProperty());
}
private:
@@ -409,7 +409,7 @@ public:
template<typename T>
inline T* get() const noexcept
{
return Base::freecad_dynamic_cast<T>(_get());
return freecad_cast<T>(_get());
}
private:
+21 -21
View File
@@ -1478,7 +1478,7 @@ Expression *OperatorExpression::simplify() const
Expression * v2 = right->simplify();
// Both arguments reduced to numerics? Then evaluate and return answer
if (freecad_dynamic_cast<NumberExpression>(v1) && freecad_dynamic_cast<NumberExpression>(v2)) {
if (freecad_cast<NumberExpression>(v1) && freecad_cast<NumberExpression>(v2)) {
delete v1;
delete v2;
return eval();
@@ -1499,7 +1499,7 @@ void OperatorExpression::_toString(std::ostream &s, bool persistent,int) const
Operator leftOperator(NONE), rightOperator(NONE);
needsParens = false;
if (freecad_dynamic_cast<OperatorExpression>(left))
if (freecad_cast<OperatorExpression>(left))
leftOperator = static_cast<OperatorExpression*>(left)->op;
if (left->priority() < priority()) // Check on operator priority first
needsParens = true;
@@ -1571,7 +1571,7 @@ void OperatorExpression::_toString(std::ostream &s, bool persistent,int) const
}
needsParens = false;
if (freecad_dynamic_cast<OperatorExpression>(right))
if (freecad_cast<OperatorExpression>(right))
rightOperator = static_cast<OperatorExpression*>(right)->op;
if (right->priority() < priority()) // Check on operator priority first
needsParens = true;
@@ -2012,11 +2012,11 @@ Py::Object FunctionExpression::evalAggregate(
if (!p)
continue;
if ((qp = freecad_dynamic_cast<PropertyQuantity>(p)))
if ((qp = freecad_cast<PropertyQuantity>(p)))
c->collect(qp->getQuantityValue());
else if ((fp = freecad_dynamic_cast<PropertyFloat>(p)))
else if ((fp = freecad_cast<PropertyFloat>(p)))
c->collect(Quantity(fp->getValue()));
else if ((ip = freecad_dynamic_cast<PropertyInteger>(p)))
else if ((ip = freecad_cast<PropertyInteger>(p)))
c->collect(Quantity(ip->getValue()));
else
_EXPR_THROW("Invalid property type for aggregate.", owner);
@@ -2614,7 +2614,7 @@ Expression *FunctionExpression::simplify() const
for (auto it : args) {
Expression * v = it->simplify();
if (freecad_dynamic_cast<NumberExpression>(v))
if (freecad_cast<NumberExpression>(v))
++numerics;
a.push_back(v);
}
@@ -2869,16 +2869,16 @@ void VariableExpression::addComponent(Component *c) {
}
long l1=0,l2=0,l3=1;
if(c->e3) {
auto n3 = freecad_dynamic_cast<NumberExpression>(c->e3);
auto n3 = freecad_cast<NumberExpression>(c->e3);
if(!n3 || !essentiallyEqual(n3->getValue(),(double)l3))
break;
}
if(c->e1) {
auto n1 = freecad_dynamic_cast<NumberExpression>(c->e1);
auto n1 = freecad_cast<NumberExpression>(c->e1);
if(!n1) {
if(c->e2 || c->e3)
break;
auto s = freecad_dynamic_cast<StringExpression>(c->e1);
auto s = freecad_cast<StringExpression>(c->e1);
if(!s)
break;
var << ObjectIdentifier::MapComponent(
@@ -2895,7 +2895,7 @@ void VariableExpression::addComponent(Component *c) {
return;
}
}
auto n2 = freecad_dynamic_cast<NumberExpression>(c->e2);
auto n2 = freecad_cast<NumberExpression>(c->e2);
if(n2 && essentiallyInteger(n2->getValue(),l2)) {
var << ObjectIdentifier::RangeComponent(l1,l2,l3);
return;
@@ -3195,7 +3195,7 @@ Py::Object ConditionalExpression::_getPyValue() const {
Expression *ConditionalExpression::simplify() const
{
std::unique_ptr<Expression> e(condition->simplify());
NumberExpression * v = freecad_dynamic_cast<NumberExpression>(e.get());
NumberExpression * v = freecad_cast<NumberExpression>(e.get());
if (!v)
return new ConditionalExpression(owner, condition->simplify(), trueExpr->simplify(), falseExpr->simplify());
@@ -3544,13 +3544,13 @@ int ExpressionParserlex();
# pragma GCC diagnostic ignored "-Wfree-nonheap-object"
#endif
// Parser, defined in ExpressionParser.y
// Parser, defined in Expression.y
# define YYTOKENTYPE
#include "ExpressionParser.tab.c"
#include "Expression.tab.c"
#ifndef DOXYGEN_SHOULD_SKIP_THIS
// Scanner, defined in ExpressionParser.l
#include "lex.ExpressionParser.c"
// Scanner, defined in Expression.l
#include "Expression.lex.c"
#endif // DOXYGEN_SHOULD_SKIP_THIS
class StringBufferCleaner
@@ -3752,11 +3752,11 @@ UnitExpression * ExpressionParser::parseUnit(const App::DocumentObject *owner, c
Expression * simplified = ScanResult->simplify();
if (!unitExpression) {
OperatorExpression * fraction = freecad_dynamic_cast<OperatorExpression>(ScanResult);
OperatorExpression * fraction = freecad_cast<OperatorExpression>(ScanResult);
if (fraction && fraction->getOperator() == OperatorExpression::DIV) {
NumberExpression * nom = freecad_dynamic_cast<NumberExpression>(fraction->getLeft());
UnitExpression * denom = freecad_dynamic_cast<UnitExpression>(fraction->getRight());
NumberExpression * nom = freecad_cast<NumberExpression>(fraction->getLeft());
UnitExpression * denom = freecad_cast<UnitExpression>(fraction->getRight());
// If not initially a unit expression, but value is equal to 1, it means the expression is something like 1/unit
if (denom && nom && essentiallyEqual(nom->getValue(), 1.0))
@@ -3766,13 +3766,13 @@ UnitExpression * ExpressionParser::parseUnit(const App::DocumentObject *owner, c
delete ScanResult;
if (unitExpression) {
NumberExpression * num = freecad_dynamic_cast<NumberExpression>(simplified);
NumberExpression * num = freecad_cast<NumberExpression>(simplified);
if (num) {
simplified = new UnitExpression(num->getOwner(), num->getQuantity());
delete num;
}
return freecad_dynamic_cast<UnitExpression>(simplified);
return freecad_cast<UnitExpression>(simplified);
}
else {
delete simplified;
+1 -1
View File
@@ -84,7 +84,7 @@ template<class P> class ExpressionModifier : public ExpressionVisitor {
public:
explicit ExpressionModifier(P & _prop)
: prop(_prop)
, propLink(Base::freecad_dynamic_cast<App::PropertyLinkBase>(&prop))
, propLink(freecad_cast<App::PropertyLinkBase>(&prop))
, signaller(_prop,false)
{}
@@ -22,14 +22,7 @@
* *
***************************************************************************/
/* Lexer for the FreeCAD Expression language */
/* use this file to generate the file 'lex.ExpressionParser.c' using the program flex
* the command for this operation is:
* flex --outfile=lex.ExpressionParser.c ExpressionParser.l
* (flex for Windows is available here:
* https://sourceforge.net/projects/winflexbison/
* (you must then change 'flex' to 'win_flex' in the command)) */
/* Lexer for the FreeCAD Expression language */
/* This disables inclusion of unistd.h, which is not available under Visual C++
* on Win32. The C++ scanner uses STL streams instead. */
@@ -165,7 +158,7 @@ S ({S1}|{S2})
Z (\x20|\xc2\xa0|\xe1(\x9a\x80)|\xe2(\x80[\x80-\x8a\xa8-\xa9\xaf]|\x81\x9f)|\xe3(\x80\x80))
DIGIT [0-9]
EXPO [eE][-+]?[0-9]+
EXPO [eE][-+]?[0-9]+
%% /*** Filter language Part ***/
@@ -174,7 +167,7 @@ EXPO [eE][-+]?[0-9]+
\<\<(\\(.|\n)|[^\\>\n])*\>\> COUNTCHARS; yylval.string = unquote(yytext); return STRING;
[%+()=/*^,\.\{\}\[\]:;@\?#] COUNTCHARS; return *yytext;
[%+()=/*^,\.\{\}\[\]:;@\?#] COUNTCHARS; return *yytext;
"==" COUNTCHARS; return EQ;
"!=" COUNTCHARS; return NEQ;
@@ -332,9 +325,9 @@ EXPO [eE][-+]?[0-9]+
"rad" COUNTCHARS; yylval.quantity.scaler = Quantity::Radian; yylval.quantity.unitStr = yytext; return UNIT; // radian
"gon" COUNTCHARS; yylval.quantity.scaler = Quantity::Gon; yylval.quantity.unitStr = yytext; return UNIT; // gon
"M" COUNTCHARS; yylval.quantity.scaler = Quantity::AngMinute; yylval.quantity.unitStr = yytext; return UNIT; // angminute
"\xE2\x80\xB2" COUNTCHARS; yylval.quantity.scaler = Quantity::AngMinute; yylval.quantity.unitStr = yytext; return UNIT; // angminute U+2032 &prime; &#8242;
"\xE2\x80\xB2" COUNTCHARS; yylval.quantity.scaler = Quantity::AngMinute; yylval.quantity.unitStr = yytext; return UNIT; // angminute U+2032 &prime; &#8242;
"AS" COUNTCHARS; yylval.quantity.scaler = Quantity::AngSecond; yylval.quantity.unitStr = yytext; return UNIT; // angsecond
"\xE2\x80\xB3" COUNTCHARS; yylval.quantity.scaler = Quantity::AngSecond; yylval.quantity.unitStr = yytext; return UNIT; // angsecond U+2033 &Prime; &#8243; ″
"\xE2\x80\xB3" COUNTCHARS; yylval.quantity.scaler = Quantity::AngSecond; yylval.quantity.unitStr = yytext; return UNIT; // angsecond U+2033 &Prime; &#8243; ″
{DIGIT}*"."{DIGIT}+{EXPO}? COUNTCHARS; yylval.fvalue = num_change(yytext,'.',','); return yylval.fvalue == 1 ? ONE : NUM;
{DIGIT}*","{DIGIT}+{EXPO}? COUNTCHARS; yylval.fvalue = num_change(yytext,',','.'); return yylval.fvalue == 1 ? ONE : NUM;
File diff suppressed because it is too large Load Diff
@@ -1,3 +1,4 @@
// clang-format off
/* A Bison parser, made by GNU Bison 3.8.2. */
/* Bison implementation for Yacc-like parsers in C
@@ -67,18 +68,16 @@
/* First part of user prologue. */
#line 28 "ExpressionParser.y"
#define YYSTYPE App::ExpressionParser::semantic_type
std::stack<FunctionExpression::Function> functions; /**< Function identifier */
//#define YYSTYPE yystype
#define yyparse ExpressionParser_yyparse
#define yyerror ExpressionParser_yyerror
#line 26 "Expression.y"
#line 82 "ExpressionParser.tab.c"
#define YYSTYPE App::ExpressionParser::semantic_type
std::stack<FunctionExpression::Function> functions; /**< Function identifier */
#define yyparse ExpressionParser_yyparse
#define yyerror ExpressionParser_yyerror
#line 80 "Expression.tab.c"
# ifndef YY_CAST
# ifdef __cplusplus
@@ -101,7 +100,7 @@ std::stack<FunctionExpression::Function> functions; /**< Function
# endif
# endif
#include "ExpressionParser.tab.h"
#include "Expression.tab.h"
/* Symbol kind. */
enum yysymbol_kind_t
{
@@ -555,14 +554,14 @@ static const yytype_int8 yytranslate[] =
/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
static const yytype_uint8 yyrline[] =
{
0, 86, 86, 87, 90, 91, 94, 95, 96, 97,
98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
108, 109, 110, 113, 114, 115, 116, 118, 119, 120,
121, 122, 123, 126, 129, 130, 131, 132, 133, 134,
135, 138, 139, 141, 142, 143, 144, 145, 146, 147,
150, 151, 155, 156, 160, 161, 165, 170, 175, 181,
188, 195, 202, 206, 207, 208, 209, 210, 211, 212,
213, 217, 218, 219, 223, 224, 228, 229
0, 81, 81, 82, 85, 86, 89, 90, 91, 92,
93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
103, 104, 105, 108, 109, 110, 111, 113, 114, 115,
116, 117, 118, 121, 124, 125, 126, 127, 128, 129,
130, 133, 134, 136, 137, 138, 139, 140, 141, 142,
145, 146, 150, 151, 155, 156, 160, 165, 170, 176,
183, 190, 197, 201, 202, 203, 204, 205, 206, 207,
208, 212, 213, 214, 218, 219, 223, 224
};
#endif
@@ -1008,51 +1007,51 @@ yydestruct (const char *yymsg,
switch (yykind)
{
case YYSYMBOL_exp: /* exp */
#line 78 "ExpressionParser.y"
#line 73 "Expression.y"
{ delete ((*yyvaluep).expr); }
#line 1014 "ExpressionParser.tab.c"
#line 1012 "Expression.tab.c"
break;
case YYSYMBOL_num: /* num */
#line 78 "ExpressionParser.y"
#line 73 "Expression.y"
{ delete ((*yyvaluep).expr); }
#line 1020 "ExpressionParser.tab.c"
#line 1018 "Expression.tab.c"
break;
case YYSYMBOL_args: /* args */
#line 80 "ExpressionParser.y"
#line 75 "Expression.y"
{ std::vector<Expression*>::const_iterator i = ((*yyvaluep).arguments).begin(); while (i != ((*yyvaluep).arguments).end()) { delete *i; ++i; } }
#line 1026 "ExpressionParser.tab.c"
#line 1024 "Expression.tab.c"
break;
case YYSYMBOL_range: /* range */
#line 78 "ExpressionParser.y"
#line 73 "Expression.y"
{ delete ((*yyvaluep).expr); }
#line 1032 "ExpressionParser.tab.c"
#line 1030 "Expression.tab.c"
break;
case YYSYMBOL_cond: /* cond */
#line 78 "ExpressionParser.y"
#line 73 "Expression.y"
{ delete ((*yyvaluep).expr); }
#line 1038 "ExpressionParser.tab.c"
#line 1036 "Expression.tab.c"
break;
case YYSYMBOL_unit_exp: /* unit_exp */
#line 78 "ExpressionParser.y"
#line 73 "Expression.y"
{ delete ((*yyvaluep).expr); }
#line 1044 "ExpressionParser.tab.c"
#line 1042 "Expression.tab.c"
break;
case YYSYMBOL_indexer: /* indexer */
#line 79 "ExpressionParser.y"
#line 74 "Expression.y"
{ delete ((*yyvaluep).component); }
#line 1050 "ExpressionParser.tab.c"
#line 1048 "Expression.tab.c"
break;
case YYSYMBOL_indexable: /* indexable */
#line 78 "ExpressionParser.y"
#line 73 "Expression.y"
{ delete ((*yyvaluep).expr); }
#line 1056 "ExpressionParser.tab.c"
#line 1054 "Expression.tab.c"
break;
default:
@@ -1320,362 +1319,362 @@ yyreduce:
switch (yyn)
{
case 2: /* input: exp */
#line 86 "ExpressionParser.y"
#line 81 "Expression.y"
{ ScanResult = (yyvsp[0].expr); valueExpression = true; }
#line 1326 "ExpressionParser.tab.c"
#line 1324 "Expression.tab.c"
break;
case 3: /* input: unit_exp */
#line 87 "ExpressionParser.y"
#line 82 "Expression.y"
{ ScanResult = (yyvsp[0].expr); unitExpression = true; }
#line 1332 "ExpressionParser.tab.c"
#line 1330 "Expression.tab.c"
break;
case 4: /* unit_num: num unit_exp */
#line 90 "ExpressionParser.y"
#line 85 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-1].expr), OperatorExpression::UNIT, (yyvsp[0].expr)); }
#line 1338 "ExpressionParser.tab.c"
#line 1336 "Expression.tab.c"
break;
case 5: /* unit_num: num us_building_unit num us_building_unit */
#line 91 "ExpressionParser.y"
#line 86 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, new OperatorExpression(DocumentObject, (yyvsp[-3].expr), OperatorExpression::UNIT, (yyvsp[-2].expr)), OperatorExpression::ADD, new OperatorExpression(DocumentObject, (yyvsp[-1].expr), OperatorExpression::UNIT, (yyvsp[0].expr)));}
#line 1344 "ExpressionParser.tab.c"
#line 1342 "Expression.tab.c"
break;
case 6: /* exp: num */
#line 94 "ExpressionParser.y"
#line 89 "Expression.y"
{ (yyval.expr) = (yyvsp[0].expr); }
#line 1350 "ExpressionParser.tab.c"
#line 1348 "Expression.tab.c"
break;
case 7: /* exp: unit_num */
#line 95 "ExpressionParser.y"
#line 90 "Expression.y"
{ (yyval.expr) = (yyvsp[0].expr); }
#line 1356 "ExpressionParser.tab.c"
#line 1354 "Expression.tab.c"
break;
case 8: /* exp: STRING */
#line 96 "ExpressionParser.y"
#line 91 "Expression.y"
{ (yyval.expr) = new StringExpression(DocumentObject, (yyvsp[0].string)); }
#line 1362 "ExpressionParser.tab.c"
#line 1360 "Expression.tab.c"
break;
case 9: /* exp: identifier */
#line 97 "ExpressionParser.y"
#line 92 "Expression.y"
{ (yyval.expr) = new VariableExpression(DocumentObject, (yyvsp[0].path)); }
#line 1368 "ExpressionParser.tab.c"
#line 1366 "Expression.tab.c"
break;
case 10: /* exp: MINUSSIGN exp */
#line 98 "ExpressionParser.y"
#line 93 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[0].expr), OperatorExpression::NEG, new NumberExpression(DocumentObject, Quantity(-1))); }
#line 1374 "ExpressionParser.tab.c"
#line 1372 "Expression.tab.c"
break;
case 11: /* exp: '+' exp */
#line 99 "ExpressionParser.y"
#line 94 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[0].expr), OperatorExpression::POS, new NumberExpression(DocumentObject, Quantity(1))); }
#line 1380 "ExpressionParser.tab.c"
#line 1378 "Expression.tab.c"
break;
case 12: /* exp: exp '+' exp */
#line 100 "ExpressionParser.y"
#line 95 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::ADD, (yyvsp[0].expr)); }
#line 1386 "ExpressionParser.tab.c"
#line 1384 "Expression.tab.c"
break;
case 13: /* exp: exp MINUSSIGN exp */
#line 101 "ExpressionParser.y"
#line 96 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::SUB, (yyvsp[0].expr)); }
#line 1392 "ExpressionParser.tab.c"
#line 1390 "Expression.tab.c"
break;
case 14: /* exp: exp '*' exp */
#line 102 "ExpressionParser.y"
#line 97 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::MUL, (yyvsp[0].expr)); }
#line 1398 "ExpressionParser.tab.c"
#line 1396 "Expression.tab.c"
break;
case 15: /* exp: exp '/' exp */
#line 103 "ExpressionParser.y"
#line 98 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::DIV, (yyvsp[0].expr)); }
#line 1404 "ExpressionParser.tab.c"
#line 1402 "Expression.tab.c"
break;
case 16: /* exp: exp '%' exp */
#line 104 "ExpressionParser.y"
#line 99 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::MOD, (yyvsp[0].expr)); }
#line 1410 "ExpressionParser.tab.c"
#line 1408 "Expression.tab.c"
break;
case 17: /* exp: exp '/' unit_exp */
#line 105 "ExpressionParser.y"
#line 100 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::DIV, (yyvsp[0].expr)); }
#line 1416 "ExpressionParser.tab.c"
#line 1414 "Expression.tab.c"
break;
case 18: /* exp: exp '^' exp */
#line 106 "ExpressionParser.y"
#line 101 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::POW, (yyvsp[0].expr)); }
#line 1422 "ExpressionParser.tab.c"
#line 1420 "Expression.tab.c"
break;
case 19: /* exp: indexable */
#line 107 "ExpressionParser.y"
#line 102 "Expression.y"
{ (yyval.expr) = (yyvsp[0].expr); }
#line 1428 "ExpressionParser.tab.c"
#line 1426 "Expression.tab.c"
break;
case 20: /* exp: FUNC args ')' */
#line 108 "ExpressionParser.y"
#line 103 "Expression.y"
{ (yyval.expr) = new FunctionExpression(DocumentObject, (yyvsp[-2].func).first, std::move((yyvsp[-2].func).second), (yyvsp[-1].arguments));}
#line 1434 "ExpressionParser.tab.c"
#line 1432 "Expression.tab.c"
break;
case 21: /* exp: cond '?' exp ':' exp */
#line 109 "ExpressionParser.y"
#line 104 "Expression.y"
{ (yyval.expr) = new ConditionalExpression(DocumentObject, (yyvsp[-4].expr), (yyvsp[-2].expr), (yyvsp[0].expr)); }
#line 1440 "ExpressionParser.tab.c"
#line 1438 "Expression.tab.c"
break;
case 22: /* exp: '(' exp ')' */
#line 110 "ExpressionParser.y"
#line 105 "Expression.y"
{ (yyval.expr) = (yyvsp[-1].expr); }
#line 1446 "ExpressionParser.tab.c"
#line 1444 "Expression.tab.c"
break;
case 23: /* num: ONE */
#line 113 "ExpressionParser.y"
#line 108 "Expression.y"
{ (yyval.expr) = new NumberExpression(DocumentObject, Quantity((yyvsp[0].fvalue))); }
#line 1452 "ExpressionParser.tab.c"
#line 1450 "Expression.tab.c"
break;
case 24: /* num: NUM */
#line 114 "ExpressionParser.y"
#line 109 "Expression.y"
{ (yyval.expr) = new NumberExpression(DocumentObject, Quantity((yyvsp[0].fvalue))); }
#line 1458 "ExpressionParser.tab.c"
#line 1456 "Expression.tab.c"
break;
case 25: /* num: INTEGER */
#line 115 "ExpressionParser.y"
#line 110 "Expression.y"
{ (yyval.expr) = new NumberExpression(DocumentObject, Quantity((double)(yyvsp[0].ivalue))); }
#line 1464 "ExpressionParser.tab.c"
#line 1462 "Expression.tab.c"
break;
case 26: /* num: CONSTANT */
#line 116 "ExpressionParser.y"
#line 111 "Expression.y"
{ (yyval.expr) = new ConstantExpression(DocumentObject, (yyvsp[0].constant).name, Quantity((yyvsp[0].constant).fvalue)); }
#line 1470 "ExpressionParser.tab.c"
#line 1468 "Expression.tab.c"
break;
case 27: /* args: exp */
#line 118 "ExpressionParser.y"
#line 113 "Expression.y"
{ (yyval.arguments).push_back((yyvsp[0].expr)); }
#line 1476 "ExpressionParser.tab.c"
#line 1474 "Expression.tab.c"
break;
case 28: /* args: range */
#line 119 "ExpressionParser.y"
#line 114 "Expression.y"
{ (yyval.arguments).push_back((yyvsp[0].expr)); }
#line 1482 "ExpressionParser.tab.c"
#line 1480 "Expression.tab.c"
break;
case 29: /* args: args ',' exp */
#line 120 "ExpressionParser.y"
#line 115 "Expression.y"
{ (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); }
#line 1488 "ExpressionParser.tab.c"
#line 1486 "Expression.tab.c"
break;
case 30: /* args: args ';' exp */
#line 121 "ExpressionParser.y"
#line 116 "Expression.y"
{ (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); }
#line 1494 "ExpressionParser.tab.c"
#line 1492 "Expression.tab.c"
break;
case 31: /* args: args ',' range */
#line 122 "ExpressionParser.y"
#line 117 "Expression.y"
{ (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); }
#line 1500 "ExpressionParser.tab.c"
#line 1498 "Expression.tab.c"
break;
case 32: /* args: args ';' range */
#line 123 "ExpressionParser.y"
#line 118 "Expression.y"
{ (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); }
#line 1506 "ExpressionParser.tab.c"
#line 1504 "Expression.tab.c"
break;
case 33: /* range: id_or_cell ':' id_or_cell */
#line 126 "ExpressionParser.y"
#line 121 "Expression.y"
{ (yyval.expr) = new RangeExpression(DocumentObject, (yyvsp[-2].string), (yyvsp[0].string)); }
#line 1512 "ExpressionParser.tab.c"
#line 1510 "Expression.tab.c"
break;
case 34: /* cond: exp EQ exp */
#line 129 "ExpressionParser.y"
#line 124 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::EQ, (yyvsp[0].expr)); }
#line 1518 "ExpressionParser.tab.c"
#line 1516 "Expression.tab.c"
break;
case 35: /* cond: exp NEQ exp */
#line 130 "ExpressionParser.y"
#line 125 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::NEQ, (yyvsp[0].expr)); }
#line 1524 "ExpressionParser.tab.c"
#line 1522 "Expression.tab.c"
break;
case 36: /* cond: exp LT exp */
#line 131 "ExpressionParser.y"
#line 126 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::LT, (yyvsp[0].expr)); }
#line 1530 "ExpressionParser.tab.c"
#line 1528 "Expression.tab.c"
break;
case 37: /* cond: exp GT exp */
#line 132 "ExpressionParser.y"
#line 127 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::GT, (yyvsp[0].expr)); }
#line 1536 "ExpressionParser.tab.c"
#line 1534 "Expression.tab.c"
break;
case 38: /* cond: exp GTE exp */
#line 133 "ExpressionParser.y"
#line 128 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::GTE, (yyvsp[0].expr)); }
#line 1542 "ExpressionParser.tab.c"
#line 1540 "Expression.tab.c"
break;
case 39: /* cond: exp LTE exp */
#line 134 "ExpressionParser.y"
#line 129 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::LTE, (yyvsp[0].expr)); }
#line 1548 "ExpressionParser.tab.c"
#line 1546 "Expression.tab.c"
break;
case 40: /* cond: '(' cond ')' */
#line 135 "ExpressionParser.y"
#line 130 "Expression.y"
{ (yyval.expr) = (yyvsp[-1].expr); }
#line 1554 "ExpressionParser.tab.c"
#line 1552 "Expression.tab.c"
break;
case 41: /* us_building_unit: USUNIT */
#line 138 "ExpressionParser.y"
{ (yyval.expr) = new UnitExpression(DocumentObject, (yyvsp[0].quantity).scaler, (yyvsp[0].quantity).unitStr ); }
#line 1560 "ExpressionParser.tab.c"
#line 133 "Expression.y"
{ (yyval.expr) = new UnitExpression(DocumentObject, (yyvsp[0].quantity).scaler, (yyvsp[0].quantity).unitStr ); }
#line 1558 "Expression.tab.c"
break;
case 42: /* other_unit: UNIT */
#line 139 "ExpressionParser.y"
{ (yyval.expr) = new UnitExpression(DocumentObject, (yyvsp[0].quantity).scaler, (yyvsp[0].quantity).unitStr ); }
#line 1566 "ExpressionParser.tab.c"
#line 134 "Expression.y"
{ (yyval.expr) = new UnitExpression(DocumentObject, (yyvsp[0].quantity).scaler, (yyvsp[0].quantity).unitStr ); }
#line 1564 "Expression.tab.c"
break;
case 43: /* unit_exp: other_unit */
#line 141 "ExpressionParser.y"
#line 136 "Expression.y"
{ (yyval.expr) = (yyvsp[0].expr); }
#line 1572 "ExpressionParser.tab.c"
#line 1570 "Expression.tab.c"
break;
case 44: /* unit_exp: us_building_unit */
#line 142 "ExpressionParser.y"
#line 137 "Expression.y"
{ (yyval.expr) = (yyvsp[0].expr); }
#line 1578 "ExpressionParser.tab.c"
#line 1576 "Expression.tab.c"
break;
case 45: /* unit_exp: unit_exp '/' unit_exp */
#line 143 "ExpressionParser.y"
#line 138 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::DIV, (yyvsp[0].expr)); }
#line 1584 "ExpressionParser.tab.c"
#line 1582 "Expression.tab.c"
break;
case 46: /* unit_exp: unit_exp '*' unit_exp */
#line 144 "ExpressionParser.y"
#line 139 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::MUL, (yyvsp[0].expr)); }
#line 1590 "ExpressionParser.tab.c"
#line 1588 "Expression.tab.c"
break;
case 47: /* unit_exp: unit_exp '^' integer */
#line 145 "ExpressionParser.y"
#line 140 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::POW, new NumberExpression(DocumentObject, Quantity((double)(yyvsp[0].ivalue)))); }
#line 1596 "ExpressionParser.tab.c"
#line 1594 "Expression.tab.c"
break;
case 48: /* unit_exp: unit_exp '^' MINUSSIGN integer */
#line 146 "ExpressionParser.y"
#line 141 "Expression.y"
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-3].expr), OperatorExpression::POW, new OperatorExpression(DocumentObject, new NumberExpression(DocumentObject, Quantity((double)(yyvsp[0].ivalue))), OperatorExpression::NEG, new NumberExpression(DocumentObject, Quantity(-1)))); }
#line 1602 "ExpressionParser.tab.c"
#line 1600 "Expression.tab.c"
break;
case 49: /* unit_exp: '(' unit_exp ')' */
#line 147 "ExpressionParser.y"
#line 142 "Expression.y"
{ (yyval.expr) = (yyvsp[-1].expr); }
#line 1608 "ExpressionParser.tab.c"
#line 1606 "Expression.tab.c"
break;
case 50: /* integer: INTEGER */
#line 150 "ExpressionParser.y"
#line 145 "Expression.y"
{ (yyval.ivalue) = (yyvsp[0].ivalue); }
#line 1614 "ExpressionParser.tab.c"
#line 1612 "Expression.tab.c"
break;
case 51: /* integer: ONE */
#line 151 "ExpressionParser.y"
#line 146 "Expression.y"
{ (yyval.ivalue) = (yyvsp[0].fvalue); }
#line 1620 "ExpressionParser.tab.c"
#line 1618 "Expression.tab.c"
break;
case 52: /* id_or_cell: IDENTIFIER */
#line 155 "ExpressionParser.y"
#line 150 "Expression.y"
{ (yyval.string) = std::move((yyvsp[0].string)); }
#line 1626 "ExpressionParser.tab.c"
#line 1624 "Expression.tab.c"
break;
case 53: /* id_or_cell: CELLADDRESS */
#line 156 "ExpressionParser.y"
#line 151 "Expression.y"
{ (yyval.string) = std::move((yyvsp[0].string)); }
#line 1632 "ExpressionParser.tab.c"
#line 1630 "Expression.tab.c"
break;
case 54: /* identifier: id_or_cell */
#line 160 "ExpressionParser.y"
#line 155 "Expression.y"
{ (yyval.path) = ObjectIdentifier(DocumentObject); (yyval.path) << ObjectIdentifier::SimpleComponent((yyvsp[0].string)); }
#line 1638 "ExpressionParser.tab.c"
#line 1636 "Expression.tab.c"
break;
case 55: /* identifier: iden */
#line 161 "ExpressionParser.y"
#line 156 "Expression.y"
{ (yyval.path) = std::move((yyvsp[0].path)); }
#line 1644 "ExpressionParser.tab.c"
#line 1642 "Expression.tab.c"
break;
case 56: /* iden: '.' STRING '.' id_or_cell */
#line 165 "ExpressionParser.y"
#line 160 "Expression.y"
{ /* Path to property of a sub-object of the current object*/
(yyval.path) = ObjectIdentifier(DocumentObject,true);
(yyval.path).setDocumentObjectName(DocumentObject,false,ObjectIdentifier::String(std::move((yyvsp[-2].string)),true),true);
(yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[0].string)));
}
#line 1654 "ExpressionParser.tab.c"
#line 1652 "Expression.tab.c"
break;
case 57: /* iden: '.' id_or_cell */
#line 170 "ExpressionParser.y"
#line 165 "Expression.y"
{ /* Path to property of the current document object */
(yyval.path) = ObjectIdentifier(DocumentObject,true);
(yyval.path).setDocumentObjectName(DocumentObject);
(yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[0].string)));
}
#line 1664 "ExpressionParser.tab.c"
#line 1662 "Expression.tab.c"
break;
case 58: /* iden: object '.' STRING '.' id_or_cell */
#line 175 "ExpressionParser.y"
#line 170 "Expression.y"
{ /* Path to property of a sub-object */
(yyval.path) = ObjectIdentifier(DocumentObject);
(yyval.path).setDocumentObjectName(std::move((yyvsp[-4].string_or_identifier)), true, ObjectIdentifier::String(std::move((yyvsp[-2].string)),true),true);
(yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[0].string)));
(yyval.path).resolveAmbiguity();
}
#line 1675 "ExpressionParser.tab.c"
#line 1673 "Expression.tab.c"
break;
case 59: /* iden: object '.' id_or_cell */
#line 181 "ExpressionParser.y"
#line 176 "Expression.y"
{ /* Path to property of a given document object */
(yyval.path) = ObjectIdentifier(DocumentObject);
(yyvsp[-2].string_or_identifier).checkImport(DocumentObject);
@@ -1683,11 +1682,11 @@ yyreduce:
(yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[0].string)));
(yyval.path).resolveAmbiguity();
}
#line 1687 "ExpressionParser.tab.c"
#line 1685 "Expression.tab.c"
break;
case 60: /* iden: document '#' object '.' id_or_cell */
#line 188 "ExpressionParser.y"
#line 183 "Expression.y"
{ /* Path to property from an external document, within a named document object */
(yyval.path) = ObjectIdentifier(DocumentObject);
(yyval.path).setDocumentName(std::move((yyvsp[-4].string_or_identifier)), true);
@@ -1695,118 +1694,118 @@ yyreduce:
(yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[0].string)));
(yyval.path).resolveAmbiguity();
}
#line 1699 "ExpressionParser.tab.c"
#line 1697 "Expression.tab.c"
break;
case 61: /* iden: document '#' object '.' STRING '.' id_or_cell */
#line 196 "ExpressionParser.y"
#line 191 "Expression.y"
{ (yyval.path) = ObjectIdentifier(DocumentObject);
(yyval.path).setDocumentName(std::move((yyvsp[-6].string_or_identifier)), true);
(yyval.path).setDocumentObjectName(std::move((yyvsp[-4].string_or_identifier)), true, ObjectIdentifier::String(std::move((yyvsp[-2].string)),true));
(yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[0].string)));
(yyval.path).resolveAmbiguity();
}
#line 1710 "ExpressionParser.tab.c"
#line 1708 "Expression.tab.c"
break;
case 62: /* iden: iden '.' IDENTIFIER */
#line 202 "ExpressionParser.y"
#line 197 "Expression.y"
{ (yyval.path)= std::move((yyvsp[-2].path)); (yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[0].string))); }
#line 1716 "ExpressionParser.tab.c"
#line 1714 "Expression.tab.c"
break;
case 63: /* indexer: '[' exp ']' */
#line 206 "ExpressionParser.y"
#line 201 "Expression.y"
{ (yyval.component) = Expression::createComponent((yyvsp[-1].expr)); }
#line 1722 "ExpressionParser.tab.c"
#line 1720 "Expression.tab.c"
break;
case 64: /* indexer: '[' exp ':' ']' */
#line 207 "ExpressionParser.y"
#line 202 "Expression.y"
{ (yyval.component) = Expression::createComponent((yyvsp[-2].expr),0,0,true); }
#line 1728 "ExpressionParser.tab.c"
#line 1726 "Expression.tab.c"
break;
case 65: /* indexer: '[' ':' exp ']' */
#line 208 "ExpressionParser.y"
#line 203 "Expression.y"
{ (yyval.component) = Expression::createComponent(0,(yyvsp[-1].expr)); }
#line 1734 "ExpressionParser.tab.c"
#line 1732 "Expression.tab.c"
break;
case 66: /* indexer: '[' ':' ':' exp ']' */
#line 209 "ExpressionParser.y"
#line 204 "Expression.y"
{ (yyval.component) = Expression::createComponent(0,0,(yyvsp[-1].expr)); }
#line 1740 "ExpressionParser.tab.c"
#line 1738 "Expression.tab.c"
break;
case 67: /* indexer: '[' exp ':' exp ']' */
#line 210 "ExpressionParser.y"
#line 205 "Expression.y"
{ (yyval.component) = Expression::createComponent((yyvsp[-3].expr),(yyvsp[-1].expr));}
#line 1746 "ExpressionParser.tab.c"
#line 1744 "Expression.tab.c"
break;
case 68: /* indexer: '[' exp ':' ':' exp ']' */
#line 211 "ExpressionParser.y"
#line 206 "Expression.y"
{ (yyval.component) = Expression::createComponent((yyvsp[-4].expr),0,(yyvsp[-1].expr)); }
#line 1752 "ExpressionParser.tab.c"
#line 1750 "Expression.tab.c"
break;
case 69: /* indexer: '[' ':' exp ':' exp ']' */
#line 212 "ExpressionParser.y"
#line 207 "Expression.y"
{ (yyval.component) = Expression::createComponent(0,(yyvsp[-3].expr),(yyvsp[-1].expr)); }
#line 1758 "ExpressionParser.tab.c"
#line 1756 "Expression.tab.c"
break;
case 70: /* indexer: '[' exp ':' exp ':' exp ']' */
#line 213 "ExpressionParser.y"
#line 208 "Expression.y"
{ (yyval.component) = Expression::createComponent((yyvsp[-5].expr),(yyvsp[-3].expr),(yyvsp[-1].expr));}
#line 1764 "ExpressionParser.tab.c"
#line 1762 "Expression.tab.c"
break;
case 71: /* indexable: identifier indexer */
#line 217 "ExpressionParser.y"
#line 212 "Expression.y"
{ (yyval.expr) = new VariableExpression(DocumentObject,(yyvsp[-1].path)); (yyval.expr)->addComponent((yyvsp[0].component)); }
#line 1770 "ExpressionParser.tab.c"
#line 1768 "Expression.tab.c"
break;
case 72: /* indexable: indexable indexer */
#line 218 "ExpressionParser.y"
#line 213 "Expression.y"
{ (yyvsp[-1].expr)->addComponent(std::move((yyvsp[0].component))); (yyval.expr) = (yyvsp[-1].expr); }
#line 1776 "ExpressionParser.tab.c"
#line 1774 "Expression.tab.c"
break;
case 73: /* indexable: indexable '.' IDENTIFIER */
#line 219 "ExpressionParser.y"
#line 214 "Expression.y"
{ (yyvsp[-2].expr)->addComponent(Expression::createComponent((yyvsp[0].string))); (yyval.expr) = (yyvsp[-2].expr); }
#line 1782 "ExpressionParser.tab.c"
#line 1780 "Expression.tab.c"
break;
case 74: /* document: STRING */
#line 223 "ExpressionParser.y"
#line 218 "Expression.y"
{ (yyval.string_or_identifier) = ObjectIdentifier::String(std::move((yyvsp[0].string)), true); }
#line 1788 "ExpressionParser.tab.c"
#line 1786 "Expression.tab.c"
break;
case 75: /* document: IDENTIFIER */
#line 224 "ExpressionParser.y"
#line 219 "Expression.y"
{ (yyval.string_or_identifier) = ObjectIdentifier::String(std::move((yyvsp[0].string)), false, true);}
#line 1794 "ExpressionParser.tab.c"
#line 1792 "Expression.tab.c"
break;
case 76: /* object: STRING */
#line 228 "ExpressionParser.y"
#line 223 "Expression.y"
{ (yyval.string_or_identifier) = ObjectIdentifier::String(std::move((yyvsp[0].string)), true); }
#line 1800 "ExpressionParser.tab.c"
#line 1798 "Expression.tab.c"
break;
case 77: /* object: id_or_cell */
#line 229 "ExpressionParser.y"
#line 224 "Expression.y"
{ (yyval.string_or_identifier) = ObjectIdentifier::String(std::move((yyvsp[0].string)), false);}
#line 1806 "ExpressionParser.tab.c"
#line 1804 "Expression.tab.c"
break;
#line 1810 "ExpressionParser.tab.c"
#line 1808 "Expression.tab.c"
default: break;
}
@@ -1999,5 +1998,4 @@ yyreturnlab:
return yyresult;
}
#line 232 "ExpressionParser.y"
#line 227 "Expression.y"
@@ -35,8 +35,8 @@
especially those whose name start with YY_ or yy_. They are
private implementation details that can be changed or removed. */
#ifndef YY_YY_EXPRESSIONPARSER_TAB_H_INCLUDED
# define YY_YY_EXPRESSIONPARSER_TAB_H_INCLUDED
#ifndef YY_YY_EXPRESSION_TAB_H_INCLUDED
# define YY_YY_EXPRESSION_TAB_H_INCLUDED
/* Debug traces. */
#ifndef YYDEBUG
# define YYDEBUG 0
@@ -91,4 +91,4 @@ extern YYSTYPE yylval;
int yyparse (void);
#endif /* !YY_YY_EXPRESSIONPARSER_TAB_H_INCLUDED */
#endif /* !YY_YY_EXPRESSION_TAB_H_INCLUDED */
@@ -1,231 +1,227 @@
/***************************************************************************
* Copyright (c) 2010 Jürgen Riegel <[email protected]> *
* Copyright (c) 2015 Eivind Kvedalen <[email protected]> *
* *
* 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 *
* *
***************************************************************************/
/* Parser for the FreeCAD Units language */
/* Represents the many different ways we can access our data */
%{
#define YYSTYPE App::ExpressionParser::semantic_type
std::stack<FunctionExpression::Function> functions; /**< Function identifier */
//#define YYSTYPE yystype
#define yyparse ExpressionParser_yyparse
#define yyerror ExpressionParser_yyerror
%}
%token FUNC
%token ONE
%token NUM
%token IDENTIFIER
%token UNIT USUNIT
%token INTEGER
%token CONSTANT
%token CELLADDRESS
%token EQ NEQ LT GT GTE LTE
%token STRING MINUSSIGN PROPERTY_REF
%token DOCUMENT OBJECT
%token EXPONENT
%type <arguments> args
%type <expr> input unit_num us_building_unit other_unit exp unit_exp cond indexable
%type <quantity> UNIT USUNIT
%type <string> id_or_cell STRING IDENTIFIER CELLADDRESS
%type <ivalue> INTEGER
%type <string> PROPERTY_REF
%type <fvalue> ONE
%type <fvalue> NUM
%type <constant> CONSTANT
%type <expr> num
%type <expr> range
%type <path> identifier iden
%type <component> indexer
%type <func> FUNC
%type <string_or_identifier> document
%type <string_or_identifier> object
%type <ivalue> integer
%precedence EQ NEQ LT GT GTE LTE
%precedence ':'
%left MINUSSIGN '+'
%left '*' '/' '%'
%precedence NUM_AND_UNIT
%left '^'
%precedence NEG
%precedence POS
%destructor { delete $$; } num range exp cond unit_exp indexable
%destructor { delete $$; } <component>
%destructor { std::vector<Expression*>::const_iterator i = $$.begin(); while (i != $$.end()) { delete *i; ++i; } } args
%start input
%%
input: exp { ScanResult = $1; valueExpression = true; }
| unit_exp { ScanResult = $1; unitExpression = true; }
;
unit_num: num unit_exp %prec NUM_AND_UNIT { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::UNIT, $2); }
| num us_building_unit num us_building_unit %prec NUM_AND_UNIT { $$ = new OperatorExpression(DocumentObject, new OperatorExpression(DocumentObject, $1, OperatorExpression::UNIT, $2), OperatorExpression::ADD, new OperatorExpression(DocumentObject, $3, OperatorExpression::UNIT, $4));}
;
exp: num { $$ = $1; }
| unit_num { $$ = $1; }
| STRING { $$ = new StringExpression(DocumentObject, $1); }
| identifier { $$ = new VariableExpression(DocumentObject, $1); }
| MINUSSIGN exp %prec NEG { $$ = new OperatorExpression(DocumentObject, $2, OperatorExpression::NEG, new NumberExpression(DocumentObject, Quantity(-1))); }
| '+' exp %prec POS { $$ = new OperatorExpression(DocumentObject, $2, OperatorExpression::POS, new NumberExpression(DocumentObject, Quantity(1))); }
| exp '+' exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::ADD, $3); }
| exp MINUSSIGN exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::SUB, $3); }
| exp '*' exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::MUL, $3); }
| exp '/' exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::DIV, $3); }
| exp '%' exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::MOD, $3); }
| exp '/' unit_exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::DIV, $3); }
| exp '^' exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::POW, $3); }
| indexable { $$ = $1; }
| FUNC args ')' { $$ = new FunctionExpression(DocumentObject, $1.first, std::move($1.second), $2);}
| cond '?' exp ':' exp { $$ = new ConditionalExpression(DocumentObject, $1, $3, $5); }
| '(' exp ')' { $$ = $2; }
;
num: ONE { $$ = new NumberExpression(DocumentObject, Quantity($1)); }
| NUM { $$ = new NumberExpression(DocumentObject, Quantity($1)); }
| INTEGER { $$ = new NumberExpression(DocumentObject, Quantity((double)$1)); }
| CONSTANT { $$ = new ConstantExpression(DocumentObject, $1.name, Quantity($1.fvalue)); }
args: exp { $$.push_back($1); }
| range { $$.push_back($1); }
| args ',' exp { $1.push_back($3); $$ = $1; }
| args ';' exp { $1.push_back($3); $$ = $1; }
| args ',' range { $1.push_back($3); $$ = $1; }
| args ';' range { $1.push_back($3); $$ = $1; }
;
range: id_or_cell ':' id_or_cell { $$ = new RangeExpression(DocumentObject, $1, $3); }
;
cond: exp EQ exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::EQ, $3); }
| exp NEQ exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::NEQ, $3); }
| exp LT exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::LT, $3); }
| exp GT exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::GT, $3); }
| exp GTE exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::GTE, $3); }
| exp LTE exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::LTE, $3); }
| '(' cond ')' { $$ = $2; }
;
us_building_unit: USUNIT { $$ = new UnitExpression(DocumentObject, $1.scaler, $1.unitStr ); }
other_unit: UNIT { $$ = new UnitExpression(DocumentObject, $1.scaler, $1.unitStr ); }
unit_exp: other_unit { $$ = $1; }
| us_building_unit { $$ = $1; }
| unit_exp '/' unit_exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::DIV, $3); }
| unit_exp '*' unit_exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::MUL, $3); }
| unit_exp '^' integer { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::POW, new NumberExpression(DocumentObject, Quantity((double)$3))); }
| unit_exp '^' MINUSSIGN integer { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::POW, new OperatorExpression(DocumentObject, new NumberExpression(DocumentObject, Quantity((double)$4)), OperatorExpression::NEG, new NumberExpression(DocumentObject, Quantity(-1)))); }
| '(' unit_exp ')' { $$ = $2; }
;
integer: INTEGER { $$ = $1; }
| ONE { $$ = $1; }
;
id_or_cell
: IDENTIFIER { $$ = std::move($1); }
| CELLADDRESS { $$ = std::move($1); }
;
identifier
: id_or_cell { $$ = ObjectIdentifier(DocumentObject); $$ << ObjectIdentifier::SimpleComponent($1); }
| iden { $$ = std::move($1); }
;
iden
: '.' STRING '.' id_or_cell { /* Path to property of a sub-object of the current object*/
$$ = ObjectIdentifier(DocumentObject,true);
$$.setDocumentObjectName(DocumentObject,false,ObjectIdentifier::String(std::move($2),true),true);
$$.addComponent(ObjectIdentifier::SimpleComponent($4));
}
| '.' id_or_cell { /* Path to property of the current document object */
$$ = ObjectIdentifier(DocumentObject,true);
$$.setDocumentObjectName(DocumentObject);
$$.addComponent(ObjectIdentifier::SimpleComponent($2));
}
| object '.' STRING '.' id_or_cell { /* Path to property of a sub-object */
$$ = ObjectIdentifier(DocumentObject);
$$.setDocumentObjectName(std::move($1), true, ObjectIdentifier::String(std::move($3),true),true);
$$.addComponent(ObjectIdentifier::SimpleComponent($5));
$$.resolveAmbiguity();
}
| object '.' id_or_cell { /* Path to property of a given document object */
$$ = ObjectIdentifier(DocumentObject);
$1.checkImport(DocumentObject);
$$.addComponent(ObjectIdentifier::SimpleComponent($1));
$$.addComponent(ObjectIdentifier::SimpleComponent($3));
$$.resolveAmbiguity();
}
| document '#' object '.' id_or_cell { /* Path to property from an external document, within a named document object */
$$ = ObjectIdentifier(DocumentObject);
$$.setDocumentName(std::move($1), true);
$$.setDocumentObjectName(std::move($3), true);
$$.addComponent(ObjectIdentifier::SimpleComponent($5));
$$.resolveAmbiguity();
}
| document '#' object '.' STRING '.' id_or_cell
{ $$ = ObjectIdentifier(DocumentObject);
$$.setDocumentName(std::move($1), true);
$$.setDocumentObjectName(std::move($3), true, ObjectIdentifier::String(std::move($5),true));
$$.addComponent(ObjectIdentifier::SimpleComponent($7));
$$.resolveAmbiguity();
}
| iden '.' IDENTIFIER { $$= std::move($1); $$.addComponent(ObjectIdentifier::SimpleComponent($3)); }
;
indexer
: '[' exp ']' { $$ = Expression::createComponent($2); }
| '[' exp ':' ']' { $$ = Expression::createComponent($2,0,0,true); }
| '[' ':' exp ']' { $$ = Expression::createComponent(0,$3); }
| '[' ':' ':' exp ']' { $$ = Expression::createComponent(0,0,$4); }
| '[' exp ':' exp ']' { $$ = Expression::createComponent($2,$4);}
| '[' exp ':' ':' exp ']' { $$ = Expression::createComponent($2,0,$5); }
| '[' ':' exp ':' exp ']' { $$ = Expression::createComponent(0,$3,$5); }
| '[' exp ':' exp ':' exp ']' { $$ = Expression::createComponent($2,$4,$6);}
;
indexable
: identifier indexer { $$ = new VariableExpression(DocumentObject,$1); $$->addComponent($2); }
| indexable indexer { $1->addComponent(std::move($2)); $$ = $1; }
| indexable '.' IDENTIFIER { $1->addComponent(Expression::createComponent($3)); $$ = $1; }
;
document
: STRING { $$ = ObjectIdentifier::String(std::move($1), true); }
| IDENTIFIER { $$ = ObjectIdentifier::String(std::move($1), false, true);}
;
object
: STRING { $$ = ObjectIdentifier::String(std::move($1), true); }
| id_or_cell { $$ = ObjectIdentifier::String(std::move($1), false);}
;
%%
/***************************************************************************
* Copyright (c) 2010 Jürgen Riegel <[email protected]> *
* Copyright (c) 2015 Eivind Kvedalen <[email protected]> *
* *
* 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 *
* *
***************************************************************************/
/* Parser for the FreeCAD Expression language */
%{
#define YYSTYPE App::ExpressionParser::semantic_type
std::stack<FunctionExpression::Function> functions; /**< Function identifier */
#define yyparse ExpressionParser_yyparse
#define yyerror ExpressionParser_yyerror
%}
%token FUNC
%token ONE
%token NUM
%token IDENTIFIER
%token UNIT USUNIT
%token INTEGER
%token CONSTANT
%token CELLADDRESS
%token EQ NEQ LT GT GTE LTE
%token STRING MINUSSIGN PROPERTY_REF
%token DOCUMENT OBJECT
%token EXPONENT
%type <arguments> args
%type <expr> input unit_num us_building_unit other_unit exp unit_exp cond indexable
%type <quantity> UNIT USUNIT
%type <string> id_or_cell STRING IDENTIFIER CELLADDRESS
%type <ivalue> INTEGER
%type <string> PROPERTY_REF
%type <fvalue> ONE
%type <fvalue> NUM
%type <constant> CONSTANT
%type <expr> num
%type <expr> range
%type <path> identifier iden
%type <component> indexer
%type <func> FUNC
%type <string_or_identifier> document
%type <string_or_identifier> object
%type <ivalue> integer
%precedence EQ NEQ LT GT GTE LTE
%precedence ':'
%left MINUSSIGN '+'
%left '*' '/' '%'
%precedence NUM_AND_UNIT
%left '^'
%precedence NEG
%precedence POS
%destructor { delete $$; } num range exp cond unit_exp indexable
%destructor { delete $$; } <component>
%destructor { std::vector<Expression*>::const_iterator i = $$.begin(); while (i != $$.end()) { delete *i; ++i; } } args
%start input
%%
input: exp { ScanResult = $1; valueExpression = true; }
| unit_exp { ScanResult = $1; unitExpression = true; }
;
unit_num: num unit_exp %prec NUM_AND_UNIT { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::UNIT, $2); }
| num us_building_unit num us_building_unit %prec NUM_AND_UNIT { $$ = new OperatorExpression(DocumentObject, new OperatorExpression(DocumentObject, $1, OperatorExpression::UNIT, $2), OperatorExpression::ADD, new OperatorExpression(DocumentObject, $3, OperatorExpression::UNIT, $4));}
;
exp: num { $$ = $1; }
| unit_num { $$ = $1; }
| STRING { $$ = new StringExpression(DocumentObject, $1); }
| identifier { $$ = new VariableExpression(DocumentObject, $1); }
| MINUSSIGN exp %prec NEG { $$ = new OperatorExpression(DocumentObject, $2, OperatorExpression::NEG, new NumberExpression(DocumentObject, Quantity(-1))); }
| '+' exp %prec POS { $$ = new OperatorExpression(DocumentObject, $2, OperatorExpression::POS, new NumberExpression(DocumentObject, Quantity(1))); }
| exp '+' exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::ADD, $3); }
| exp MINUSSIGN exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::SUB, $3); }
| exp '*' exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::MUL, $3); }
| exp '/' exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::DIV, $3); }
| exp '%' exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::MOD, $3); }
| exp '/' unit_exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::DIV, $3); }
| exp '^' exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::POW, $3); }
| indexable { $$ = $1; }
| FUNC args ')' { $$ = new FunctionExpression(DocumentObject, $1.first, std::move($1.second), $2);}
| cond '?' exp ':' exp { $$ = new ConditionalExpression(DocumentObject, $1, $3, $5); }
| '(' exp ')' { $$ = $2; }
;
num: ONE { $$ = new NumberExpression(DocumentObject, Quantity($1)); }
| NUM { $$ = new NumberExpression(DocumentObject, Quantity($1)); }
| INTEGER { $$ = new NumberExpression(DocumentObject, Quantity((double)$1)); }
| CONSTANT { $$ = new ConstantExpression(DocumentObject, $1.name, Quantity($1.fvalue)); }
args: exp { $$.push_back($1); }
| range { $$.push_back($1); }
| args ',' exp { $1.push_back($3); $$ = $1; }
| args ';' exp { $1.push_back($3); $$ = $1; }
| args ',' range { $1.push_back($3); $$ = $1; }
| args ';' range { $1.push_back($3); $$ = $1; }
;
range: id_or_cell ':' id_or_cell { $$ = new RangeExpression(DocumentObject, $1, $3); }
;
cond: exp EQ exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::EQ, $3); }
| exp NEQ exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::NEQ, $3); }
| exp LT exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::LT, $3); }
| exp GT exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::GT, $3); }
| exp GTE exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::GTE, $3); }
| exp LTE exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::LTE, $3); }
| '(' cond ')' { $$ = $2; }
;
us_building_unit: USUNIT { $$ = new UnitExpression(DocumentObject, $1.scaler, $1.unitStr ); }
other_unit: UNIT { $$ = new UnitExpression(DocumentObject, $1.scaler, $1.unitStr ); }
unit_exp: other_unit { $$ = $1; }
| us_building_unit { $$ = $1; }
| unit_exp '/' unit_exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::DIV, $3); }
| unit_exp '*' unit_exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::MUL, $3); }
| unit_exp '^' integer { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::POW, new NumberExpression(DocumentObject, Quantity((double)$3))); }
| unit_exp '^' MINUSSIGN integer { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::POW, new OperatorExpression(DocumentObject, new NumberExpression(DocumentObject, Quantity((double)$4)), OperatorExpression::NEG, new NumberExpression(DocumentObject, Quantity(-1)))); }
| '(' unit_exp ')' { $$ = $2; }
;
integer: INTEGER { $$ = $1; }
| ONE { $$ = $1; }
;
id_or_cell
: IDENTIFIER { $$ = std::move($1); }
| CELLADDRESS { $$ = std::move($1); }
;
identifier
: id_or_cell { $$ = ObjectIdentifier(DocumentObject); $$ << ObjectIdentifier::SimpleComponent($1); }
| iden { $$ = std::move($1); }
;
iden
: '.' STRING '.' id_or_cell { /* Path to property of a sub-object of the current object*/
$$ = ObjectIdentifier(DocumentObject,true);
$$.setDocumentObjectName(DocumentObject,false,ObjectIdentifier::String(std::move($2),true),true);
$$.addComponent(ObjectIdentifier::SimpleComponent($4));
}
| '.' id_or_cell { /* Path to property of the current document object */
$$ = ObjectIdentifier(DocumentObject,true);
$$.setDocumentObjectName(DocumentObject);
$$.addComponent(ObjectIdentifier::SimpleComponent($2));
}
| object '.' STRING '.' id_or_cell { /* Path to property of a sub-object */
$$ = ObjectIdentifier(DocumentObject);
$$.setDocumentObjectName(std::move($1), true, ObjectIdentifier::String(std::move($3),true),true);
$$.addComponent(ObjectIdentifier::SimpleComponent($5));
$$.resolveAmbiguity();
}
| object '.' id_or_cell { /* Path to property of a given document object */
$$ = ObjectIdentifier(DocumentObject);
$1.checkImport(DocumentObject);
$$.addComponent(ObjectIdentifier::SimpleComponent($1));
$$.addComponent(ObjectIdentifier::SimpleComponent($3));
$$.resolveAmbiguity();
}
| document '#' object '.' id_or_cell { /* Path to property from an external document, within a named document object */
$$ = ObjectIdentifier(DocumentObject);
$$.setDocumentName(std::move($1), true);
$$.setDocumentObjectName(std::move($3), true);
$$.addComponent(ObjectIdentifier::SimpleComponent($5));
$$.resolveAmbiguity();
}
| document '#' object '.' STRING '.' id_or_cell
{ $$ = ObjectIdentifier(DocumentObject);
$$.setDocumentName(std::move($1), true);
$$.setDocumentObjectName(std::move($3), true, ObjectIdentifier::String(std::move($5),true));
$$.addComponent(ObjectIdentifier::SimpleComponent($7));
$$.resolveAmbiguity();
}
| iden '.' IDENTIFIER { $$= std::move($1); $$.addComponent(ObjectIdentifier::SimpleComponent($3)); }
;
indexer
: '[' exp ']' { $$ = Expression::createComponent($2); }
| '[' exp ':' ']' { $$ = Expression::createComponent($2,0,0,true); }
| '[' ':' exp ']' { $$ = Expression::createComponent(0,$3); }
| '[' ':' ':' exp ']' { $$ = Expression::createComponent(0,0,$4); }
| '[' exp ':' exp ']' { $$ = Expression::createComponent($2,$4);}
| '[' exp ':' ':' exp ']' { $$ = Expression::createComponent($2,0,$5); }
| '[' ':' exp ':' exp ']' { $$ = Expression::createComponent(0,$3,$5); }
| '[' exp ':' exp ':' exp ']' { $$ = Expression::createComponent($2,$4,$6);}
;
indexable
: identifier indexer { $$ = new VariableExpression(DocumentObject,$1); $$->addComponent($2); }
| indexable indexer { $1->addComponent(std::move($2)); $$ = $1; }
| indexable '.' IDENTIFIER { $1->addComponent(Expression::createComponent($3)); $$ = $1; }
;
document
: STRING { $$ = ObjectIdentifier::String(std::move($1), true); }
| IDENTIFIER { $$ = ObjectIdentifier::String(std::move($1), false, true);}
;
object
: STRING { $$ = ObjectIdentifier::String(std::move($1), true); }
| id_or_cell { $$ = ObjectIdentifier::String(std::move($1), false);}
;
%%
+1 -1
View File
@@ -649,7 +649,7 @@ public:
};
#define YYSTYPE semantic_type
#include "ExpressionParser.tab.h"
#include "Expression.tab.h"
#undef YYTOKENTYPE
#undef YYSTYPE
#undef YYSTYPE_ISDECLARED
+6 -4
View File
@@ -1,6 +1,8 @@
#!/usr/bin/env sh
cd "$(dirname "$0")"
flex -v -olex.ExpressionParser.c ExpressionParser.l
bison -d -v -Wall -oExpressionParser.tab.c ExpressionParser.y
(cd "$(dirname "$0")" && \
flex -v -oExpression.lex.c Expression.l && \
bison -d -v -Wall -oExpression.tab.c Expression.y && \
sed -i '1s|^|// clang-format off\n|' Expression.tab.c && \
sed -i '1s|^|// clang-format off\n|' Expression.lex.c \
)
+2 -2
View File
@@ -160,11 +160,11 @@ DocumentObject* GeoFeature::resolveElement(const DocumentObject* obj,
return nullptr;
}
auto linked = sobj->getLinkedObject(true);
auto geo = Base::freecad_dynamic_cast<GeoFeature>(linked);
auto geo = freecad_cast<GeoFeature>(linked);
if (!geo && linked) {
auto ext = linked->getExtensionByType<LinkBaseExtension>(true);
if (ext) {
geo = Base::freecad_dynamic_cast<GeoFeature>(ext->getTrueLinkedObject(true));
geo = freecad_cast<GeoFeature>(ext->getTrueLinkedObject(true));
}
}
if (geoFeature) {
+1 -1
View File
@@ -285,7 +285,7 @@ std::vector<DocumentObject*> GeoFeatureGroupExtension::getScopedObjectsFromLink(
}
std::vector<App::DocumentObject*> result;
auto link = Base::freecad_dynamic_cast<PropertyLinkBase>(prop);
auto link = freecad_cast<PropertyLinkBase>(prop);
if (link && link->getScope() == scope) {
link->getLinks(result);
}
+1 -1
View File
@@ -414,7 +414,7 @@ bool GroupExtension::extensionGetSubObject(DocumentObject*& ret,
{
const char* dot;
if (!subname || *subname == 0) {
auto obj = Base::freecad_dynamic_cast<const DocumentObject>(getExtendedContainer());
auto obj = freecad_cast<const DocumentObject>(getExtendedContainer());
ret = const_cast<DocumentObject*>(obj);
return true;
}
+19 -19
View File
@@ -286,7 +286,7 @@ void LinkBaseExtension::setProperty(int idx, Property* prop)
}
case PropLinkCopyOnChangeSource:
case PropLinkCopyOnChangeGroup:
if (auto linkProp = Base::freecad_dynamic_cast<PropertyLinkBase>(prop)) {
if (auto linkProp = freecad_cast<PropertyLinkBase>(prop)) {
linkProp->setScope(LinkScope::Global);
}
// fall through
@@ -356,7 +356,7 @@ App::DocumentObjectExecReturn* LinkBaseExtension::extensionExecute()
if (!linked) {
std::ostringstream ss;
ss << "Link broken!";
auto xlink = Base::freecad_dynamic_cast<PropertyXLink>(getLinkedObjectProperty());
auto xlink = freecad_cast<PropertyXLink>(getLinkedObjectProperty());
if (xlink) {
const char* objname = xlink->getObjectName();
if (!Base::Tools::isNullOrEmpty(objname)) {
@@ -389,7 +389,7 @@ App::DocumentObjectExecReturn* LinkBaseExtension::extensionExecute()
|| !container->getDocument()->getObjectByID(_LinkOwner.getValue()))) {
// Check if this is an element link. Do not invoke appLinkExecute()
// if so, because it will be called from the link array.
proxy = Base::freecad_dynamic_cast<PropertyPythonObject>(
proxy = freecad_cast<PropertyPythonObject>(
linked->getPropertyByName("Proxy"));
}
if (proxy) {
@@ -491,7 +491,7 @@ LinkBaseExtension::getOnChangeCopyObjects(std::vector<App::DocumentObject*>* exc
continue;
}
auto prop =
Base::freecad_dynamic_cast<PropertyMap>(obj->getPropertyByName("_CopyOnChangeControl"));
freecad_cast<PropertyMap>(obj->getPropertyByName("_CopyOnChangeControl"));
static std::map<std::string, std::string> dummy;
const auto& map = prop && prop->getContainer() == obj ? prop->getValues() : dummy;
const char* v = "";
@@ -526,7 +526,7 @@ void LinkBaseExtension::setOnChangeCopyObject(App::DocumentObject* obj, OnChange
bool exclude = flags.testFlag(OnChangeCopyOptions::Exclude);
bool external = parent->getDocument() != obj->getDocument();
auto prop =
Base::freecad_dynamic_cast<PropertyMap>(obj->getPropertyByName("_CopyOnChangeControl"));
freecad_cast<PropertyMap>(obj->getPropertyByName("_CopyOnChangeControl"));
if (external == exclude && !prop) {
return;
@@ -583,7 +583,7 @@ void LinkBaseExtension::syncCopyOnChange()
// dependencies.
LinkGroup* copyOnChangeGroup = nullptr;
if (auto prop = getLinkCopyOnChangeGroupProperty()) {
copyOnChangeGroup = Base::freecad_dynamic_cast<LinkGroup>(prop->getValue());
copyOnChangeGroup = freecad_cast<LinkGroup>(prop->getValue());
if (!copyOnChangeGroup) {
// Create the LinkGroup if not exist
auto group = new LinkGroup;
@@ -601,7 +601,7 @@ void LinkBaseExtension::syncCopyOnChange()
continue;
}
auto prop =
Base::freecad_dynamic_cast<PropertyUUID>(obj->getPropertyByName("_SourceUUID"));
freecad_cast<PropertyUUID>(obj->getPropertyByName("_SourceUUID"));
if (prop && prop->getContainer() == obj) {
oldObjs.emplace_back(prop);
}
@@ -665,7 +665,7 @@ void LinkBaseExtension::syncCopyOnChange()
std::map<Base::Uuid, App::DocumentObjectT> newObjs;
for (auto obj : copiedObjs) {
auto prop = Base::freecad_dynamic_cast<PropertyUUID>(obj->getPropertyByName("_SourceUUID"));
auto prop = freecad_cast<PropertyUUID>(obj->getPropertyByName("_SourceUUID"));
if (prop) {
newObjs.insert(std::make_pair(prop->getValue(), obj));
}
@@ -673,7 +673,7 @@ void LinkBaseExtension::syncCopyOnChange()
std::vector<std::pair<App::DocumentObject*, App::DocumentObject*>> replacements;
for (const auto& objT : oldObjs) {
auto prop = Base::freecad_dynamic_cast<PropertyUUID>(objT.getProperty());
auto prop = freecad_cast<PropertyUUID>(objT.getProperty());
if (!prop) {
continue;
}
@@ -707,7 +707,7 @@ void LinkBaseExtension::syncCopyOnChange()
if (prop->getContainer() != o) {
continue;
}
auto linkProp = Base::freecad_dynamic_cast<App::PropertyLinkBase>(prop);
auto linkProp = freecad_cast<App::PropertyLinkBase>(prop);
if (!linkProp) {
continue;
}
@@ -1739,7 +1739,7 @@ void LinkBaseExtension::parseSubName() const
bool hasSubElement = !mySubElements.empty();
mySubElements.clear();
mySubName.clear();
auto xlink = freecad_dynamic_cast<const PropertyXLink>(getLinkedObjectProperty());
auto xlink = freecad_cast<const PropertyXLink>(getLinkedObjectProperty());
if (!xlink || xlink->getSubValues().empty()) {
if (hasSubElement) {
mySubElements.emplace_back("");
@@ -1896,7 +1896,7 @@ void LinkBaseExtension::update(App::DocumentObject* parent, const Property* prop
std::vector<Base::Vector3d> scales;
scales.reserve(objs.size());
for (auto obj : objs) {
auto element = freecad_dynamic_cast<LinkElement>(obj);
auto element = freecad_cast<LinkElement>(obj);
if (element) {
placements.push_back(element->Placement.getValue());
scales.push_back(element->getScaleVector());
@@ -1990,7 +1990,7 @@ void LinkBaseExtension::update(App::DocumentObject* parent, const Property* prop
// It is possible to have orphan LinkElement here due to,
// for example, undo and redo. So we try to re-claim the
// children element first.
auto obj = freecad_dynamic_cast<LinkElement>(doc->getObject(name.c_str()));
auto obj = freecad_cast<LinkElement>(doc->getObject(name.c_str()));
if (obj
&& (!obj->_LinkOwner.getValue() || obj->_LinkOwner.getValue() == ownerID)) {
obj->Visibility.setValue(false);
@@ -2034,7 +2034,7 @@ void LinkBaseExtension::update(App::DocumentObject* parent, const Property* prop
auto owner = getContainer();
long ownerID = owner ? owner->getID() : 0;
while (objs.size() > elementCount) {
auto element = freecad_dynamic_cast<LinkElement>(objs.back());
auto element = freecad_cast<LinkElement>(objs.back());
if (element && element->_LinkOwner.getValue() == ownerID) {
tmpObjs.push_back(objs.back());
}
@@ -2201,13 +2201,13 @@ void LinkBaseExtension::syncElementList()
{
auto transform = getLinkTransformProperty();
auto link = getLinkedObjectProperty();
auto xlink = freecad_dynamic_cast<const PropertyXLink>(link);
auto xlink = freecad_cast<const PropertyXLink>(link);
auto owner = getContainer();
auto ownerID = owner ? owner->getID() : 0;
auto elements = getElementListValue();
for (auto i : elements) {
auto element = freecad_dynamic_cast<LinkElement>(i);
auto element = freecad_cast<LinkElement>(i);
if (!element
|| (element->_LinkOwner.getValue() && element->_LinkOwner.getValue() != ownerID)) {
continue;
@@ -2251,7 +2251,7 @@ void LinkBaseExtension::onExtendedDocumentRestored()
hasOldSubElement = false;
// SubElements was stored as a PropertyStringList. It is now migrated to be
// stored inside PropertyXLink.
auto xlink = freecad_dynamic_cast<PropertyXLink>(getLinkedObjectProperty());
auto xlink = freecad_cast<PropertyXLink>(getLinkedObjectProperty());
if (!xlink) {
FC_ERR("Failed to restore SubElements for " << parent->getFullName());
}
@@ -2393,7 +2393,7 @@ void LinkBaseExtension::setLink(int index,
link->Label.setValue(linked->Label.getValue());
}
auto pla =
freecad_dynamic_cast<PropertyPlacement>(obj->getPropertyByName("Placement"));
freecad_cast<PropertyPlacement>(obj->getPropertyByName("Placement"));
if (pla) {
link->Placement.setValue(pla->getValue());
}
@@ -2424,7 +2424,7 @@ void LinkBaseExtension::setLink(int index,
// Here means we are assigning a Link
auto xlink = freecad_dynamic_cast<PropertyXLink>(linkProp);
auto xlink = freecad_cast<PropertyXLink>(linkProp);
if (obj) {
if (!obj->isAttachedToDocument()) {
LINK_THROW(Base::ValueError, "Invalid document object");
+6 -6
View File
@@ -119,7 +119,7 @@ ObjectIdentifier::ObjectIdentifier(const App::PropertyContainer* _owner,
, _hash(0)
{
if (_owner) {
const DocumentObject* docObj = freecad_dynamic_cast<const DocumentObject>(_owner);
const DocumentObject* docObj = freecad_cast<const DocumentObject>(_owner);
if (!docObj) {
FC_THROWM(Base::RuntimeError, "Property must be owned by a document object.");
}
@@ -145,7 +145,7 @@ ObjectIdentifier::ObjectIdentifier(const App::PropertyContainer* _owner, bool lo
, _hash(0)
{
if (_owner) {
const DocumentObject* docObj = freecad_dynamic_cast<const DocumentObject>(_owner);
const DocumentObject* docObj = freecad_cast<const DocumentObject>(_owner);
if (!docObj) {
FC_THROWM(Base::RuntimeError, "Property must be owned by a document object.");
}
@@ -166,7 +166,7 @@ ObjectIdentifier::ObjectIdentifier(const Property& prop, int index)
, localProperty(false)
, _hash(0)
{
DocumentObject* docObj = freecad_dynamic_cast<DocumentObject>(prop.getContainer());
DocumentObject* docObj = freecad_cast<DocumentObject>(prop.getContainer());
if (!docObj) {
FC_THROWM(Base::TypeError, "Property must be owned by a document object.");
@@ -1364,7 +1364,7 @@ ObjectIdentifier ObjectIdentifier::relativeTo(const ObjectIdentifier& other) con
ObjectIdentifier ObjectIdentifier::parse(const DocumentObject* docObj, const std::string& str)
{
std::unique_ptr<Expression> expr(ExpressionParser::parse(docObj, str.c_str()));
VariableExpression* v = freecad_dynamic_cast<VariableExpression>(expr.get());
VariableExpression* v = freecad_cast<VariableExpression>(expr.get());
if (v) {
return v->getPath();
@@ -1853,12 +1853,12 @@ ObjectIdentifier::access(const ResolveResults& result, Py::Object* value, Depend
}
if (prop && prop->getContainer() != obj) {
auto linkTouched =
Base::freecad_dynamic_cast<PropertyBool>(obj->getPropertyByName("_LinkTouched"));
freecad_cast<PropertyBool>(obj->getPropertyByName("_LinkTouched"));
if (linkTouched) {
propName = linkTouched->getName();
}
else {
auto propOwner = Base::freecad_dynamic_cast<DocumentObject>(prop->getContainer());
auto propOwner = freecad_cast<DocumentObject>(prop->getContainer());
if (propOwner) {
obj = propOwner;
}
+2 -2
View File
@@ -247,7 +247,7 @@ PyObject* PropertyContainerPy::setPropertyStatus(PyObject* args)
return nullptr;
}
auto linkProp = Base::freecad_dynamic_cast<App::PropertyLinkBase>(prop);
auto linkProp = freecad_cast<App::PropertyLinkBase>(prop);
std::bitset<32> status(prop->getStatus());
std::vector<Py::Object> items;
@@ -326,7 +326,7 @@ PyObject* PropertyContainerPy::getPropertyStatus(PyObject* args)
return nullptr;
}
auto linkProp = Base::freecad_dynamic_cast<App::PropertyLinkBase>(prop);
auto linkProp = freecad_cast<App::PropertyLinkBase>(prop);
if (linkProp && linkProp->testFlag(App::PropertyLinkBase::LinkAllowPartial)) {
ret.append(Py::String("AllowPartial"));
}
+5 -5
View File
@@ -408,7 +408,7 @@ void PropertyExpressionEngine::buildGraphStructures(
ObjectIdentifier PropertyExpressionEngine::canonicalPath(const ObjectIdentifier& p) const
{
DocumentObject* docObj = freecad_dynamic_cast<DocumentObject>(getContainer());
DocumentObject* docObj = freecad_cast<DocumentObject>(getContainer());
// Am I owned by a DocumentObject?
if (!docObj) {
@@ -448,7 +448,7 @@ size_t PropertyExpressionEngine::numExpressions() const
void PropertyExpressionEngine::afterRestore()
{
DocumentObject* docObj = freecad_dynamic_cast<DocumentObject>(getContainer());
DocumentObject* docObj = freecad_cast<DocumentObject>(getContainer());
if (restoredExpressions && docObj) {
Base::FlagToggler<bool> flag(restoring);
AtomicPropertyChange signaller(*this);
@@ -669,7 +669,7 @@ PropertyExpressionEngine::computeEvaluationOrder(ExecuteOption option)
DocumentObjectExecReturn* App::PropertyExpressionEngine::execute(ExecuteOption option,
bool* touched)
{
DocumentObject* docObj = freecad_dynamic_cast<DocumentObject>(getContainer());
DocumentObject* docObj = freecad_cast<DocumentObject>(getContainer());
if (!docObj) {
throw Base::RuntimeError("PropertyExpressionEngine must be owned by a DocumentObject.");
@@ -739,7 +739,7 @@ DocumentObjectExecReturn* App::PropertyExpressionEngine::execute(ExecuteOption o
throw Base::RuntimeError("Path does not resolve to a property.");
}
DocumentObject* parent = freecad_dynamic_cast<DocumentObject>(prop->getContainer());
DocumentObject* parent = freecad_cast<DocumentObject>(prop->getContainer());
/* Make sure property belongs to the same container as this PropertyExpressionEngine */
if (parent != docObj) {
@@ -808,7 +808,7 @@ void PropertyExpressionEngine::getPathsToDocumentObject(
DocumentObject* obj,
std::vector<App::ObjectIdentifier>& paths) const
{
DocumentObject* owner = freecad_dynamic_cast<DocumentObject>(getContainer());
DocumentObject* owner = freecad_cast<DocumentObject>(getContainer());
if (!owner || owner == obj) {
return;
+3 -3
View File
@@ -1301,7 +1301,7 @@ std::string PropertyComplexGeoData::getElementMapVersion(bool) const
if (!data) {
return std::string();
}
auto owner = Base::freecad_dynamic_cast<DocumentObject>(getContainer());
auto owner = freecad_cast<DocumentObject>(getContainer());
std::ostringstream ss;
if (owner && owner->getDocument() && owner->getDocument()->getStringHasher() == data->Hasher) {
ss << "1.";
@@ -1319,7 +1319,7 @@ bool PropertyComplexGeoData::checkElementMapVersion(const char* ver) const
if (!data) {
return false;
}
auto owner = Base::freecad_dynamic_cast<DocumentObject>(getContainer());
auto owner = freecad_cast<DocumentObject>(getContainer());
std::ostringstream ss;
const char* prefix;
if (owner && owner->getDocument() && owner->getDocument()->getStringHasher() == data->Hasher) {
@@ -1340,7 +1340,7 @@ void PropertyComplexGeoData::afterRestore()
auto data = getComplexData();
if (data && data->isRestoreFailed()) {
data->resetRestoreFailure();
auto owner = Base::freecad_dynamic_cast<DocumentObject>(getContainer());
auto owner = freecad_cast<DocumentObject>(getContainer());
if (owner && owner->getDocument()
&& !owner->getDocument()->testStatus(App::Document::PartialDoc)) {
owner->getDocument()->addRecomputeObject(owner);
+12 -12
View File
@@ -249,7 +249,7 @@ static std::string propertyName(const Property* prop)
return {};
}
if (!prop->getContainer() || !prop->hasName()) {
auto xlink = Base::freecad_dynamic_cast<const PropertyXLink>(prop);
auto xlink = freecad_cast<const PropertyXLink>(prop);
if (xlink) {
return propertyName(xlink->parent());
}
@@ -976,7 +976,7 @@ void PropertyLinkList::setValues(const std::vector<DocumentObject*>& value)
return;
}
auto parent = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
auto parent = freecad_cast<App::DocumentObject>(getContainer());
for (auto obj : value) {
if (!obj || !obj->isAttachedToDocument()) {
throw Base::ValueError("PropertyLinkList: invalid document object");
@@ -1321,7 +1321,7 @@ void PropertyLinkSub::setValue(App::DocumentObject* lValue,
std::vector<std::string>&& subs,
std::vector<ShadowSub>&& shadows)
{
auto parent = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
auto parent = freecad_cast<App::DocumentObject>(getContainer());
if (lValue) {
if (!lValue->isAttachedToDocument()) {
throw Base::ValueError("PropertyLinkSub: invalid document object");
@@ -2216,7 +2216,7 @@ int PropertyLinkSubList::getSize() const
void PropertyLinkSubList::setValue(DocumentObject* lValue, const char* SubName)
{
auto parent = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
auto parent = freecad_cast<App::DocumentObject>(getContainer());
verifyObject(lValue, parent);
// maintain backlinks
@@ -2255,7 +2255,7 @@ void PropertyLinkSubList::setValue(DocumentObject* lValue, const char* SubName)
void PropertyLinkSubList::setValues(const std::vector<DocumentObject*>& lValue,
const std::vector<const char*>& lSubNames)
{
auto parent = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
auto parent = freecad_cast<App::DocumentObject>(getContainer());
for (auto obj : lValue) {
verifyObject(obj, parent);
}
@@ -2316,7 +2316,7 @@ void PropertyLinkSubList::setValues(std::vector<DocumentObject*>&& lValue,
std::vector<std::string>&& lSubNames,
std::vector<ShadowSub>&& ShadowSubList)
{
auto parent = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
auto parent = freecad_cast<App::DocumentObject>(getContainer());
for (auto obj : lValue) {
verifyObject(obj, parent);
}
@@ -2411,7 +2411,7 @@ void PropertyLinkSubList::addValue(App::DocumentObject* obj,
const std::vector<std::string>& subs,
bool reset)
{
auto parent = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
auto parent = freecad_cast<App::DocumentObject>(getContainer());
verifyObject(obj, parent);
// maintain backlinks.
@@ -2729,7 +2729,7 @@ void PropertyLinkSubList::updateElementReference(DocumentObject* feature, bool r
unregisterElementReference();
}
_ShadowSubList.resize(_lSubList.size());
auto owner = freecad_dynamic_cast<DocumentObject>(getContainer());
auto owner = freecad_cast<DocumentObject>(getContainer());
if (owner && owner->isRestoring()) {
return;
}
@@ -3370,7 +3370,7 @@ public:
if (info->pcDoc) {
// make sure to attach only external object
auto owner = Base::freecad_dynamic_cast<DocumentObject>(l->getContainer());
auto owner = freecad_cast<DocumentObject>(l->getContainer());
if (owner && owner->getDocument() == info->pcDoc) {
return info;
}
@@ -4332,7 +4332,7 @@ void PropertyXLink::Restore(Base::XMLReader& reader)
Property*
PropertyXLink::CopyOnImportExternal(const std::map<std::string, std::string>& nameMap) const
{
auto owner = Base::freecad_dynamic_cast<const DocumentObject>(getContainer());
auto owner = freecad_cast<const DocumentObject>(getContainer());
if (!owner || !owner->getDocument() || !_pcLink || !_pcLink->isAttachedToDocument()) {
return nullptr;
}
@@ -5735,7 +5735,7 @@ void PropertyXLinkContainer::Save(Base::Writer& writer) const
writer.Stream() << writer.ind() << "<XLinks count=\"" << _XLinks.size();
std::map<App::Document*, int> docSet;
auto owner = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
auto owner = freecad_cast<App::DocumentObject>(getContainer());
if (owner && !owner->isExporting()) {
// Document name and label can change on restore, we shall record the
// current document name and label and pair it with the associated
@@ -5878,7 +5878,7 @@ bool PropertyXLinkContainer::isLinkedToDocument(const App::Document& doc) const
void PropertyXLinkContainer::updateDeps(std::map<DocumentObject*, bool>&& newDeps)
{
auto owner = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
auto owner = freecad_cast<App::DocumentObject>(getContainer());
if (!owner || !owner->isAttachedToDocument()) {
return;
}
+12 -5
View File
@@ -201,9 +201,10 @@ bool BaseClass::isDerivedFrom() const
*
*/
template<typename T>
T* freecad_dynamic_cast(Base::BaseClass* type)
T* freecad_cast(Base::BaseClass* type)
{
static_assert(std::is_base_of<BaseClass, T>::value, "T must be derived from Base::BaseClass");
static_assert(std::is_base_of<Base::BaseClass, T>::value,
"T must be derived from Base::BaseClass");
if (type && type->isDerivedFrom(T::getClassTypeId())) {
return static_cast<T*>(type);
@@ -218,9 +219,10 @@ T* freecad_dynamic_cast(Base::BaseClass* type)
*
*/
template<typename T>
const T* freecad_dynamic_cast(const Base::BaseClass* type)
const T* freecad_cast(const Base::BaseClass* type)
{
static_assert(std::is_base_of<BaseClass, T>::value, "T must be derived from Base::BaseClass");
static_assert(std::is_base_of<Base::BaseClass, T>::value,
"T must be derived from Base::BaseClass");
if (type && type->isDerivedFrom(T::getClassTypeId())) {
return static_cast<const T*>(type);
@@ -229,7 +231,12 @@ const T* freecad_dynamic_cast(const Base::BaseClass* type)
return nullptr;
}
} // namespace Base
// We define global alias for freecad_cast to be used by all FreeCAD files that include
// BaseClass.h. While doing using on header level is non-ideal it allows for much easier use
// of the important freecad_cast. In that case the name is prefixed with freecad so there is no
// chance of symbols collision.
using Base::freecad_cast;
#endif // BASE_BASECLASS_H
+2 -4
View File
@@ -167,8 +167,8 @@ SET(FreeCADBase_UNITAPI_SRCS
Quantity.h
Quantity.cpp
QuantityPyImp.cpp
QuantityParser.l
QuantityParser.y
Quantity.l
Quantity.y
Unit.h
Unit.cpp
UnitPyImp.cpp
@@ -199,7 +199,6 @@ SET(FreeCADBase_CPP_SRCS
ExceptionFactory.cpp
Factory.cpp
FileInfo.cpp
FileTemplate.cpp
FutureWatcherProgress.cpp
GeometryPyCXX.cpp
Handle.cpp
@@ -270,7 +269,6 @@ SET(FreeCADBase_HPP_SRCS
ExceptionFactory.h
Factory.h
FileInfo.h
FileTemplate.h
FutureWatcherProgress.h
GeometryPyCXX.h
Handle.h
-82
View File
@@ -1,82 +0,0 @@
/***************************************************************************
* Copyright (c) 2011 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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"
#include "FileTemplate.h"
using namespace Base;
//**************************************************************************
// Construction/Destruction
/**
* A constructor.
* A more elaborate description of the constructor.
*/
ClassTemplate::ClassTemplate() = default;
ClassTemplate::ClassTemplate(const ClassTemplate&) = default;
ClassTemplate::ClassTemplate(ClassTemplate&&) = default;
/**
* A destructor.
* A more elaborate description of the destructor.
*/
ClassTemplate::~ClassTemplate() = default;
ClassTemplate& ClassTemplate::operator=(const ClassTemplate&) = default;
ClassTemplate& ClassTemplate::operator=(ClassTemplate&&) = default;
//**************************************************************************
// separator for other implementation aspects
/**
* a normal member taking two arguments and returning an integer value.
* \par
* You can use a printf like interface like:
* \code
* GetConsole().Warning("Some defects in %s, loading anyway\n",str);
* \endcode
* @param a an integer argument.
* @param s a constant character pointer.
* @see ClassTemplate()
* @see ~ClassTemplate()
* @see testMeToo()
* @see publicVar()
* @return The test results
*/
int ClassTemplate::testMe(int /*a*/, const char* /*s*/)
{
return 0;
}
//**************************************************************************
//**************************************************************************
// Separator for additional classes
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-148
View File
@@ -1,148 +0,0 @@
/***************************************************************************
* Copyright (c) 2011 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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_FILETEMPLATE_H
#define BASE_FILETEMPLATE_H
#include <string>
#ifndef FC_GLOBAL_H
#include <FCGlobal.h>
#endif
namespace Base
{
/** A test class. A more elaborate class description.
* Detailed description with some formatting:
* \par
* bla blablablablablablabl:
* \code
* #include <Base/Console.h>
* Base::Console().Log("Stage: %d",i);
* \endcode
* \par
* another blablablablablablablablablabl:
* Text before the list
* - list item 1
* - sub item 1
* - sub sub item 1
* - sub sub item 2
* .
* The dot above ends the sub sub item list.
* More text for the first sub item
* .
* The dot above ends the first sub item.
* More text for the first list item
* - sub item 2
* - sub item 3
* - list item 2
* .
* More text in the same paragraph.
*
* More text in a new paragraph.
* Also with HTML tags:
* <ul>
* <li> mouse events
* <ol>
* <li>mouse move event
* <li>mouse click event
* More info about the click event.
* <li>mouse double click event
* </ol>
* <li> keyboard events
* <ol>
* <li>key down event
* <li>key up event
* </ol>
* </ul>
* More text here.
*
* \author YOUR NAME
*/
class BaseExport ClassTemplate
{
public:
/// Construction
ClassTemplate();
ClassTemplate(const ClassTemplate&);
ClassTemplate(ClassTemplate&&);
/// Destruction
virtual ~ClassTemplate();
ClassTemplate& operator=(const ClassTemplate&);
ClassTemplate& operator=(ClassTemplate&&);
int testMe(int a, const char* s);
/**
* An enum.
* More detailed enum description.
*/
enum TEnum
{
TVal1, /**< enum value TVal1. */
TVal2, /**< enum value TVal2. */
TVal3 /**< enum value TVal3. */
}
* enumPtr {nullptr}, /**< enum pointer. Details. */
enumVar {TVal1}; /**< enum variable. Details. */
/**
* A pure virtual member.
* @see testMe()
* @param c1 the first argument.
* @param c2 the second argument.
*/
virtual void testMeToo(char c1, char c2) = 0;
/** @name a group of methods */
//@{
/// I am method one
virtual void one() = 0;
/// I am method two
virtual void two() = 0;
/// I am method three
virtual void three() = 0;
//@}
/**
* a public variable.
* Details.
*/
int publicVar {0};
/**
* a function variable.
* Details.
*/
int (*handler)(int a, int b) {nullptr};
std::string something;
};
} // namespace Base
#endif // BASE_FILETEMPLATE_H
+2 -2
View File
@@ -1,2 +1,2 @@
C:\cygwin\bin\flex.exe -oQuantityLexer.c QuantityParser.l
C:\cygwin\bin\bison -oQuantityParser.c QuantityParser.y
C:\cygwin\bin\flex.exe -oQuantity.lex.c Quantity.l
C:\cygwin\bin\bison -oQuantity.tab.c Quantity.y
+6 -2
View File
@@ -1,2 +1,6 @@
flex -oQuantityLexer.c < QuantityParser.l
bison -oQuantityParser.c QuantityParser.y
(cd "$(dirname "$0")" && \
flex -oQuantity.lex.c Quantity.l && \
bison -oQuantity.tab.c Quantity.y && \
sed -i '1s|^|// clang-format off\n|' Quantity.tab.c && \
sed -i '1s|^|// clang-format off\n|' Quantity.lex.c \
)
+1 -26
View File
@@ -25,36 +25,11 @@
// clang-format off
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
// Std. configurations
// (re-)defined in pyconfig.h
#if defined (_POSIX_C_SOURCE)
# undef _POSIX_C_SOURCE
#endif
#if defined (_XOPEN_SOURCE)
# undef _XOPEN_SOURCE
#endif
// needed header
#undef slots
#include <Python.h>
#ifdef FC_OS_MACOSX
#undef toupper
#undef tolower
#undef isupper
#undef islower
#undef isspace
#undef isalpha
#undef isalnum
#endif
#define slots
#include <bitset>
#include <cstring>
#include "Exception.h"
#include <CXX/Objects.hxx>
#include "Exception.h"
/** Python static class macro for definition
* sets up a static function entry in a class inheriting
+4 -4
View File
@@ -523,14 +523,14 @@ namespace QuantityParser
#define yylex QuantityLexer
int QuantityLexer();
// Parser, defined in QuantityParser.y
// Parser, defined in Quantity.y
// NOLINTNEXTLINE
#include "QuantityParser.c"
#include "Quantity.tab.c"
#ifndef DOXYGEN_SHOULD_SKIP_THIS
// Scanner, defined in QuantityParser.l
// Scanner, defined in Quantity.l
// NOLINTNEXTLINE
#include "QuantityLexer.c"
#include "Quantity.lex.c"
#endif // DOXYGEN_SHOULD_SKIP_THIS
class StringBufferCleaner
+240
View File
@@ -0,0 +1,240 @@
%{
/***************************************************************************
* Copyright (c) 2013 Jürgen Riegel <[email protected]> *
* *
* 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 *
* *
***************************************************************************/
/* Lexer for the FreeCAD Units language */
/* This disables inclusion of unistd.h, which is not available under Visual C++
* on Win32. The C++ scanner uses STL streams instead. */
#define YY_NO_UNISTD_H
%}
/*** Flex Declarations and Options ***/
/* the manual says "somewhat more optimized" */
%option batch
%option never-interactive
/* no support for include files is planned */
%option noyywrap nounput
DIGIT [0-9]
ID [a-z][a-z0-9]*
EXPO [eE][-+]?[0-9]+
DGRP '\.'[0-9][0-9][0-9]
CGRP '\,'[0-9][0-9][0-9]
%x C_COMMENT
%% /*** Filter language Part ***/
"\[" { BEGIN(C_COMMENT); }
<C_COMMENT>"\]" { BEGIN(INITIAL); }
<C_COMMENT>. { ;}
[ \t] ;
[\n]+ ;
[+()=/*^] return *yytext;
"-" return MINUSSIGN;
"\xe2\x88\x92" return MINUSSIGN;
"nm" yylval = Quantity::NanoMetre; return UNIT; // nano meter
"um" yylval = Quantity::MicroMetre; return UNIT; // micro meter
"\xC2\xB5m" yylval = Quantity::MicroMetre; return UNIT; // micro meter (greek micro in UTF8)
"mm" yylval = Quantity::MilliMetre; return UNIT; // milli meter (internal standard length)
"cm" yylval = Quantity::CentiMetre; return UNIT; // centi meter
"dm" yylval = Quantity::DeciMetre; return UNIT; // deci meter
"m" yylval = Quantity::Metre; return UNIT; // Metre
"km" yylval = Quantity::KiloMetre; return UNIT; // kilo meter
"l" yylval = Quantity::Liter; return UNIT; // Liter (dm^3)
"ml" yylval = Quantity::MilliLiter; return UNIT; // milli Liter
"Hz" yylval = Quantity::Hertz; return UNIT; // Hertz
"kHz" yylval = Quantity::KiloHertz; return UNIT; // kilo Hertz
"MHz" yylval = Quantity::MegaHertz; return UNIT; // mega Hertz
"GHz" yylval = Quantity::GigaHertz; return UNIT; // giga Hertz
"THz" yylval = Quantity::TeraHertz; return UNIT; // tera Hertz
"ug" yylval = Quantity::MicroGram; return UNIT; // micro gram
"\xC2\xB5g" yylval = Quantity::MicroGram; return UNIT; // micro gram
"mg" yylval = Quantity::MilliGram; return UNIT; // milli gram
"g" yylval = Quantity::Gram; return UNIT; // gram
"kg" yylval = Quantity::KiloGram; return UNIT; // kilo gram (internal standard for mass)
"t" yylval = Quantity::Ton; return UNIT; // Metric Tonne
"s" yylval = Quantity::Second; return UNIT; // second (internal standard time)
"min" yylval = Quantity::Minute; return UNIT; // minute
"h" yylval = Quantity::Hour; return UNIT; // hour
"A" yylval = Quantity::Ampere; return UNIT; // Ampere (internal standard electric current)
"mA" yylval = Quantity::MilliAmpere; return UNIT; // milli Ampere
"kA" yylval = Quantity::KiloAmpere; return UNIT; // kilo Ampere
"MA" yylval = Quantity::MegaAmpere; return UNIT; // mega Ampere
"K" yylval = Quantity::Kelvin; return UNIT; // Kelvin (internal standard thermodynamic temperature)
"mK" yylval = Quantity::MilliKelvin; return UNIT; // milli Kelvin
"\xC2\xB5K" yylval = Quantity::MicroKelvin; return UNIT; // micro Kelvin
"uK" yylval = Quantity::MicroKelvin; return UNIT; // micro Kelvin
"mol" yylval = Quantity::Mole; return UNIT; // Mole (internal standard amount of substance)
"mmol" yylval = Quantity::MilliMole; return UNIT; // Milli Mole
"cd" yylval = Quantity::Candela; return UNIT; // Candela (internal standard luminous intensity)
"in" yylval = Quantity::Inch; return UNIT; // inch
"\"" yylval = Quantity::Inch; return UNIT; // inch
"ft" yylval = Quantity::Foot; return UNIT; // foot
"'" yylval = Quantity::Foot; return UNIT; // foot
"thou" yylval = Quantity::Thou; return UNIT; // thou (in/1000)
"mil" yylval = Quantity::Thou; return UNIT; // mil (the thou in US)
"yd" yylval = Quantity::Yard; return UNIT; // yard
"mi" yylval = Quantity::Mile; return UNIT; // mile
"mph" yylval = Quantity::MilePerHour; return UNIT; // mile per hour
"sqft" yylval = Quantity::SquareFoot; return UNIT; // square foot
"cft" yylval = Quantity::CubicFoot; return UNIT; // cubic foot
"lb" yylval = Quantity::Pound; return UNIT; // pound
"lbm" yylval = Quantity::Pound; return UNIT; // pound
"oz" yylval = Quantity::Ounce; return UNIT; // ounce
"st" yylval = Quantity::Stone; return UNIT; // Stone
"cwt" yylval = Quantity::Hundredweights; return UNIT; // hundredweights
"lbf" yylval = Quantity::PoundForce; return UNIT; // pound
"N" yylval = Quantity::Newton; return UNIT; // Newton (kg*m/s^2)
"mN" yylval = Quantity::MilliNewton; return UNIT; // milli Newton
"kN" yylval = Quantity::KiloNewton; return UNIT; // kilo Newton
"MN" yylval = Quantity::MegaNewton; return UNIT; // mega Newton
"Pa" yylval = Quantity::Pascal; return UNIT; // Pascal (kg/m/s^2 or N/m^2)
"kPa" yylval = Quantity::KiloPascal; return UNIT; // kilo Pascal
"MPa" yylval = Quantity::MegaPascal; return UNIT; // mega Pascal
"GPa" yylval = Quantity::GigaPascal; return UNIT; // giga Pascal
"bar" yylval = Quantity::Bar; return UNIT; // 1 bar = 100 kPa
"mbar" yylval = Quantity::MilliBar; return UNIT; // milli Bar
"Torr" yylval = Quantity::Torr; return UNIT; // portion of Pascal ( 101325/760 )
"mTorr" yylval = Quantity::mTorr; return UNIT; //
"uTorr" yylval = Quantity::yTorr; return UNIT; //
"\xC2\xB5Torr" yylval = Quantity::yTorr; return UNIT; //
"psi" yylval = Quantity::PSI; return UNIT; // pounds/in^2
"ksi" yylval = Quantity::KSI; return UNIT; // 1000 x pounds/in^2
"Mpsi" yylval = Quantity::MPSI; return UNIT; // 1000 ksi
"W" yylval = Quantity::Watt; return UNIT; // Watt (kg*m^2/s^3)
"mW" yylval = Quantity::MilliWatt; return UNIT; // milli Watt
"kW" yylval = Quantity::KiloWatt; return UNIT; // kilo Watt
"VA" yylval = Quantity::VoltAmpere; return UNIT; // VoltAmpere (kg*m^2/s^3)
"V" yylval = Quantity::Volt; return UNIT; // Volt (kg*m^2/A/s^3)
"kV" yylval = Quantity::KiloVolt; return UNIT; // kilo Volt
"mV" yylval = Quantity::MilliVolt; return UNIT; // milli Volt
"MS" yylval = Quantity::MegaSiemens; return UNIT; // mega Siemens
"kS" yylval = Quantity::KiloSiemens; return UNIT; // kilo Siemens
"S" yylval = Quantity::Siemens; return UNIT; // Siemens (A^2*s^3/kg/m^2)
"mS" yylval = Quantity::MilliSiemens; return UNIT; // milli Siemens
"\xC2\xB5S" yylval = Quantity::MicroSiemens; return UNIT; // micro Siemens
"uS" yylval = Quantity::MicroSiemens; return UNIT; // micro Siemens
"Ohm" yylval = Quantity::Ohm; return UNIT; // Ohm (kg*m^2/A^2/s^3)
"kOhm" yylval = Quantity::KiloOhm; return UNIT; // kilo Ohm
"MOhm" yylval = Quantity::MegaOhm; return UNIT; // mega Ohm
"C" yylval = Quantity::Coulomb; return UNIT; // Coulomb (A*s)
"T" yylval = Quantity::Tesla; return UNIT; // Tesla (kg/s^2/A)
"G" yylval = Quantity::Gauss; return UNIT; // Gauss (1 G = 1e-4 T)
"Wb" yylval = Quantity::Weber; return UNIT; // Weber (kg*m^2/s^2/A)
"F" yylval = Quantity::Farad; return UNIT; // Farad (s^4*A^2/m^2/kg)
"mF" yylval = Quantity::MilliFarad; return UNIT; // milli Farad
"\xC2\xB5F" yylval = Quantity::MicroFarad; return UNIT; // micro Farad
"uF" yylval = Quantity::MicroFarad; return UNIT; // micro Farad
"nF" yylval = Quantity::NanoFarad; return UNIT; // nano Farad
"pF" yylval = Quantity::PicoFarad; return UNIT; // pico Farad
"H" yylval = Quantity::Henry; return UNIT; // Henry (kg*m^2/s^2/A^2)
"mH" yylval = Quantity::MilliHenry; return UNIT; // milli Henry
"\xC2\xB5H" yylval = Quantity::MicroHenry; return UNIT; // micro Henry
"uH" yylval = Quantity::MicroHenry; return UNIT; // micro Henry
"nH" yylval = Quantity::NanoHenry; return UNIT; // nano Henry
"J" yylval = Quantity::Joule; return UNIT; // Joule (kg*m^2/s^2)
"mJ" yylval = Quantity::MilliJoule; return UNIT; // milli Joule
"kJ" yylval = Quantity::KiloJoule; return UNIT; // kilo Joule
"Nm" yylval = Quantity::NewtonMeter; return UNIT; // N*m = Joule
"VAs" yylval = Quantity::VoltAmpereSecond; return UNIT; // V*A*s = Joule
"CV" yylval = Quantity::WattSecond; return UNIT; //
"Ws" yylval = Quantity::WattSecond; return UNIT; // W*s = Joule
"kWh" yylval = Quantity::KiloWattHour; return UNIT; // 1 kWh = 3.6e6 J
"eV" yylval = Quantity::ElectronVolt; return UNIT; // 1 eV = 1.602176634e-19 J
"keV" yylval = Quantity::KiloElectronVolt; return UNIT;
"MeV" yylval = Quantity::MegaElectronVolt; return UNIT;
"cal" yylval = Quantity::Calorie; return UNIT; // 1 cal = 4.1868 J
"kcal" yylval = Quantity::KiloCalorie; return UNIT;
"\xC2\xB0" yylval = Quantity::Degree; return UNIT; // degree (internal standard angle)
"deg" yylval = Quantity::Degree; return UNIT; // degree (internal standard angle)
"rad" yylval = Quantity::Radian; return UNIT; // radian
"gon" yylval = Quantity::Gon; return UNIT; // gon
"M" yylval = Quantity::AngMinute; return UNIT; // minute(Angular)
"\xE2\x80\xB2" yylval = Quantity::AngMinute; return UNIT; // minute(Angular)
"AS" yylval = Quantity::AngSecond; return UNIT; // second(Angular)
"\xE2\x80\xB3" yylval = Quantity::AngSecond; return UNIT; // second(Angular)
"1" yylval = Quantity(1.0); return ONE;
{DIGIT}+"."?{DIGIT}*{EXPO}? yylval = Quantity(num_change(yytext,'.',',')); return NUM;
"."?{DIGIT}+{EXPO}? yylval = Quantity(num_change(yytext,'.',',')); return NUM;
{DIGIT}+","?{DIGIT}*{EXPO}? yylval = Quantity(num_change(yytext,',','.')); return NUM;
","?{DIGIT}+{EXPO}? yylval = Quantity(num_change(yytext,',','.')); return NUM;
"pi" yylval = Quantity(std::numbers::pi); return NUM; // constant pi
"e" yylval = Quantity(std::numbers::e); return NUM; // constant e
"acos" return ACOS;
"asin" return ASIN;
"atan" return ATAN;
"atan2" return ATAN2;
"cos" return COS;
"exp" return EXP;
"abs" return ABS;
"mod" return MOD;
"log" return LOG;
"log10" return LOG10;
"pow" return POW;
"sin" return SIN;
"sinh" return SINH;
"tan" return TAN;
"tanh" return TANH;
"sqrt" return SQRT;
. return *yytext;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+85
View File
@@ -0,0 +1,85 @@
/***************************************************************************
* Copyright (c) 2013 Jürgen Riegel <[email protected]> *
* *
* 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 *
* *
***************************************************************************/
/* Parser for the FreeCAD Units language */
/* Represents the many different ways we can access our data */
%{
#define YYSTYPE Quantity
#define yyparse Quantity_yyparse
#define yyerror Quantity_yyerror
%}
/* Bison declarations. */
%token UNIT ONE NUM MINUSSIGN
%token ACOS ASIN ATAN ATAN2 COS EXP ABS MOD LOG LOG10 POW SIN SINH TAN TANH SQRT;
%left MINUSSIGN '+'
%left '*' '/'
%left NEG /* negation--unary minus */
%right '^' /* exponentiation */
%left ONE NUM
%start input
%%
input: { QuantResult = Quantity(std::numeric_limits<double>::min()); /* empty input */ }
| num { QuantResult = $1; }
| unit { QuantResult = $1; }
| quantity { QuantResult = $1; }
| quantity quantity { QuantResult = $1 + $2; }
| quantity quantity quantity { QuantResult = $1 + $2 + $3; }
;
num: NUM { $$ = $1; }
| ONE { $$ = $1; }
| num '+' num { $$ = Quantity($1.getValue() + $3.getValue()); }
| num MINUSSIGN num { $$ = Quantity($1.getValue() - $3.getValue()); }
| num '*' num { $$ = Quantity($1.getValue() * $3.getValue()); }
| num '/' num { $$ = Quantity($1.getValue() / $3.getValue()); }
| MINUSSIGN num %prec NEG { $$ = Quantity(-$2.getValue()); }
| num '^' num { $$ = Quantity(pow($1.getValue(), $3.getValue())); }
| '(' num ')' { $$ = $2; }
| ACOS '(' num ')' { $$ = Quantity(acos($3.getValue())); }
| ASIN '(' num ')' { $$ = Quantity(asin($3.getValue())); }
| ATAN '(' num ')' { $$ = Quantity(atan($3.getValue())); }
| ABS '(' num ')' { $$ = Quantity(fabs($3.getValue())); }
| EXP '(' num ')' { $$ = Quantity(exp($3.getValue())); }
| LOG '(' num ')' { $$ = Quantity(log($3.getValue())); }
| LOG10 '(' num ')' { $$ = Quantity(log10($3.getValue())); }
| SIN '(' num ')' { $$ = Quantity(sin($3.getValue())); }
| SINH '(' num ')' { $$ = Quantity(sinh($3.getValue())); }
| TAN '(' num ')' { $$ = Quantity(tan($3.getValue())); }
| TANH '(' num ')' { $$ = Quantity(tanh($3.getValue())); }
| SQRT '(' num ')' { $$ = Quantity(sqrt($3.getValue())); }
| COS '(' num ')' { $$ = Quantity(cos($3.getValue())); }
;
unit: UNIT { $$ = $1; }
| ONE '/' unit { $$ = Quantity(1.0)/$3; }
| unit '*' unit { $$ = $1 * $3; }
| unit '/' unit { $$ = $1 / $3; }
| unit '^' num { $$ = $1.pow ($3); }
| '(' unit ')' { $$ = $2; }
;
quantity: num unit { $$ = $1*$2; }
| num '/' unit { $$ = Quantity($1)/$3; }
;
%%
-85
View File
@@ -1,85 +0,0 @@
// clang-format off
/* A Bison parser, made by GNU Bison 3.0.4. */
/* Bison interface for Yacc-like parsers in C
Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
#ifndef YY_YY_QUANTITYPARSER_H_INCLUDED
# define YY_YY_QUANTITYPARSER_H_INCLUDED
/* Debug traces. */
#ifndef YYDEBUG
# define YYDEBUG 0
#endif
#if YYDEBUG
extern int yydebug;
#endif
/* Token type. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
enum yytokentype
{
UNIT = 258,
NUM = 259,
MINUSSIGN = 260,
ACOS = 261,
ASIN = 262,
ATAN = 263,
ATAN2 = 264,
COS = 265,
EXP = 266,
ABS = 267,
MOD = 268,
LOG = 269,
LOG10 = 270,
POW = 271,
SIN = 272,
SINH = 273,
TAN = 274,
TANH = 275,
SQRT = 276,
NEG = 277
};
#endif
/* Value type. */
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef int YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define YYSTYPE_IS_DECLARED 1
#endif
extern YYSTYPE yylval;
int yyparse (void);
#endif /* !YY_YY_QUANTITYPARSER_H_INCLUDED */
// clang-format on
-248
View File
@@ -1,248 +0,0 @@
%{
/***************************************************************************
* Copyright (c) 2013 Jürgen Riegel <[email protected]> *
* *
* 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 *
* *
***************************************************************************/
/* Lexer for the FreeCAD Units language */
/* use this file to generate the file 'QuantityLexer.c' using the program flex
* the command for this operation is:
* flex --outfile=QuantityLexer.c QuantityParser.l
* (flex for Windows is available here:
* https://sourceforge.net/projects/winflexbison/
* (you must then change 'flex' to 'win_flex' in the command)) */
/* This disables inclusion of unistd.h, which is not available under Visual C++
* on Win32. The C++ scanner uses STL streams instead. */
#define YY_NO_UNISTD_H
%}
/*** Flex Declarations and Options ***/
/* the manual says "somewhat more optimized" */
%option batch
%option never-interactive
/* no support for include files is planned */
%option noyywrap nounput
DIGIT [0-9]
ID [a-z][a-z0-9]*
EXPO [eE][-+]?[0-9]+
DGRP '\.'[0-9][0-9][0-9]
CGRP '\,'[0-9][0-9][0-9]
%x C_COMMENT
%% /*** Filter language Part ***/
"\[" { BEGIN(C_COMMENT); }
<C_COMMENT>"\]" { BEGIN(INITIAL); }
<C_COMMENT>. { ;}
[ \t] ;
[\n]+ ;
[+()=/*^] { return *yytext; }
"-" return MINUSSIGN;
"\xe2\x88\x92" return MINUSSIGN;
"nm" yylval = Quantity::NanoMetre; return UNIT; // nano meter
"um" yylval = Quantity::MicroMetre; return UNIT; // micro meter
"\xC2\xB5m" yylval = Quantity::MicroMetre; return UNIT; // micro meter (greek micro in UTF8)
"mm" yylval = Quantity::MilliMetre; return UNIT; // milli meter (internal standard length)
"cm" yylval = Quantity::CentiMetre; return UNIT; // centi meter
"dm" yylval = Quantity::DeciMetre; return UNIT; // deci meter
"m" yylval = Quantity::Metre; return UNIT; // Metre
"km" yylval = Quantity::KiloMetre; return UNIT; // kilo meter
"l" yylval = Quantity::Liter; return UNIT; // Liter (dm^3)
"ml" yylval = Quantity::MilliLiter; return UNIT; // milli Liter
"Hz" yylval = Quantity::Hertz; return UNIT; // Hertz
"kHz" yylval = Quantity::KiloHertz; return UNIT; // kilo Hertz
"MHz" yylval = Quantity::MegaHertz; return UNIT; // mega Hertz
"GHz" yylval = Quantity::GigaHertz; return UNIT; // giga Hertz
"THz" yylval = Quantity::TeraHertz; return UNIT; // tera Hertz
"ug" yylval = Quantity::MicroGram; return UNIT; // micro gram
"\xC2\xB5g" yylval = Quantity::MicroGram; return UNIT; // micro gram
"mg" yylval = Quantity::MilliGram; return UNIT; // milli gram
"g" yylval = Quantity::Gram; return UNIT; // gram
"kg" yylval = Quantity::KiloGram; return UNIT; // kilo gram (internal standard for mass)
"t" yylval = Quantity::Ton; return UNIT; // Metric Tonne
"s" yylval = Quantity::Second; return UNIT; // second (internal standard time)
"min" yylval = Quantity::Minute; return UNIT; // minute
"h" yylval = Quantity::Hour; return UNIT; // hour
"A" yylval = Quantity::Ampere; return UNIT; // Ampere (internal standard electric current)
"mA" yylval = Quantity::MilliAmpere; return UNIT; // milli Ampere
"kA" yylval = Quantity::KiloAmpere; return UNIT; // kilo Ampere
"MA" yylval = Quantity::MegaAmpere; return UNIT; // mega Ampere
"K" yylval = Quantity::Kelvin; return UNIT; // Kelvin (internal standard thermodynamic temperature)
"mK" yylval = Quantity::MilliKelvin; return UNIT; // milli Kelvin
"\xC2\xB5K" yylval = Quantity::MicroKelvin; return UNIT; // micro Kelvin
"uK" yylval = Quantity::MicroKelvin; return UNIT; // micro Kelvin
"mol" yylval = Quantity::Mole; return UNIT; // Mole (internal standard amount of substance)
"mmol" yylval = Quantity::MilliMole; return UNIT; // Milli Mole
"cd" yylval = Quantity::Candela; return UNIT; // Candela (internal standard luminous intensity)
"in" yylval = Quantity::Inch; return UNIT; // inch
"\"" yylval = Quantity::Inch; return UNIT; // inch
"ft" yylval = Quantity::Foot; return UNIT; // foot
"'" yylval = Quantity::Foot; return UNIT; // foot
"thou" yylval = Quantity::Thou; return UNIT; // thou (in/1000)
"mil" yylval = Quantity::Thou; return UNIT; // mil (the thou in US)
"yd" yylval = Quantity::Yard; return UNIT; // yard
"mi" yylval = Quantity::Mile; return UNIT; // mile
"mph" yylval = Quantity::MilePerHour; return UNIT; // mile per hour
"sqft" yylval = Quantity::SquareFoot; return UNIT; // square foot
"cft" yylval = Quantity::CubicFoot; return UNIT; // cubic foot
"lb" yylval = Quantity::Pound; return UNIT; // pound
"lbm" yylval = Quantity::Pound; return UNIT; // pound
"oz" yylval = Quantity::Ounce; return UNIT; // ounce
"st" yylval = Quantity::Stone; return UNIT; // Stone
"cwt" yylval = Quantity::Hundredweights; return UNIT; // hundredweights
"lbf" yylval = Quantity::PoundForce; return UNIT; // pound
"N" yylval = Quantity::Newton; return UNIT; // Newton (kg*m/s^2)
"mN" yylval = Quantity::MilliNewton; return UNIT; // milli Newton
"kN" yylval = Quantity::KiloNewton; return UNIT; // kilo Newton
"MN" yylval = Quantity::MegaNewton; return UNIT; // mega Newton
"Pa" yylval = Quantity::Pascal; return UNIT; // Pascal (kg/m/s^2 or N/m^2)
"kPa" yylval = Quantity::KiloPascal; return UNIT; // kilo Pascal
"MPa" yylval = Quantity::MegaPascal; return UNIT; // mega Pascal
"GPa" yylval = Quantity::GigaPascal; return UNIT; // giga Pascal
"bar" yylval = Quantity::Bar; return UNIT; // 1 bar = 100 kPa
"mbar" yylval = Quantity::MilliBar; return UNIT; // milli Bar
"Torr" yylval = Quantity::Torr; return UNIT; // portion of Pascal ( 101325/760 )
"mTorr" yylval = Quantity::mTorr; return UNIT; //
"uTorr" yylval = Quantity::yTorr; return UNIT; //
"\xC2\xB5Torr" yylval = Quantity::yTorr; return UNIT; //
"psi" yylval = Quantity::PSI; return UNIT; // pounds/in^2
"ksi" yylval = Quantity::KSI; return UNIT; // 1000 x pounds/in^2
"Mpsi" yylval = Quantity::MPSI; return UNIT; // 1000 ksi
"W" yylval = Quantity::Watt; return UNIT; // Watt (kg*m^2/s^3)
"mW" yylval = Quantity::MilliWatt; return UNIT; // milli Watt
"kW" yylval = Quantity::KiloWatt; return UNIT; // kilo Watt
"VA" yylval = Quantity::VoltAmpere; return UNIT; // VoltAmpere (kg*m^2/s^3)
"V" yylval = Quantity::Volt; return UNIT; // Volt (kg*m^2/A/s^3)
"kV" yylval = Quantity::KiloVolt; return UNIT; // kilo Volt
"mV" yylval = Quantity::MilliVolt; return UNIT; // milli Volt
"MS" yylval = Quantity::MegaSiemens; return UNIT; // mega Siemens
"kS" yylval = Quantity::KiloSiemens; return UNIT; // kilo Siemens
"S" yylval = Quantity::Siemens; return UNIT; // Siemens (A^2*s^3/kg/m^2)
"mS" yylval = Quantity::MilliSiemens; return UNIT; // milli Siemens
"\xC2\xB5S" yylval = Quantity::MicroSiemens; return UNIT; // micro Siemens
"uS" yylval = Quantity::MicroSiemens; return UNIT; // micro Siemens
"Ohm" yylval = Quantity::Ohm; return UNIT; // Ohm (kg*m^2/A^2/s^3)
"kOhm" yylval = Quantity::KiloOhm; return UNIT; // kilo Ohm
"MOhm" yylval = Quantity::MegaOhm; return UNIT; // mega Ohm
"C" yylval = Quantity::Coulomb; return UNIT; // Coulomb (A*s)
"T" yylval = Quantity::Tesla; return UNIT; // Tesla (kg/s^2/A)
"G" yylval = Quantity::Gauss; return UNIT; // Gauss (1 G = 1e-4 T)
"Wb" yylval = Quantity::Weber; return UNIT; // Weber (kg*m^2/s^2/A)
"F" yylval = Quantity::Farad; return UNIT; // Farad (s^4*A^2/m^2/kg)
"mF" yylval = Quantity::MilliFarad; return UNIT; // milli Farad
"\xC2\xB5F" yylval = Quantity::MicroFarad; return UNIT; // micro Farad
"uF" yylval = Quantity::MicroFarad; return UNIT; // micro Farad
"nF" yylval = Quantity::NanoFarad; return UNIT; // nano Farad
"pF" yylval = Quantity::PicoFarad; return UNIT; // pico Farad
"H" yylval = Quantity::Henry; return UNIT; // Henry (kg*m^2/s^2/A^2)
"mH" yylval = Quantity::MilliHenry; return UNIT; // milli Henry
"\xC2\xB5H" yylval = Quantity::MicroHenry; return UNIT; // micro Henry
"uH" yylval = Quantity::MicroHenry; return UNIT; // micro Henry
"nH" yylval = Quantity::NanoHenry; return UNIT; // nano Henry
"J" yylval = Quantity::Joule; return UNIT; // Joule (kg*m^2/s^2)
"mJ" yylval = Quantity::MilliJoule; return UNIT; // milli Joule
"kJ" yylval = Quantity::KiloJoule; return UNIT; // kilo Joule
"Nm" yylval = Quantity::NewtonMeter; return UNIT; // N*m = Joule
"VAs" yylval = Quantity::VoltAmpereSecond; return UNIT; // V*A*s = Joule
"CV" yylval = Quantity::WattSecond; return UNIT; //
"Ws" yylval = Quantity::WattSecond; return UNIT; // W*s = Joule
"kWh" yylval = Quantity::KiloWattHour; return UNIT; // 1 kWh = 3.6e6 J
"eV" yylval = Quantity::ElectronVolt; return UNIT; // 1 eV = 1.602176634e-19 J
"keV" yylval = Quantity::KiloElectronVolt; return UNIT;
"MeV" yylval = Quantity::MegaElectronVolt; return UNIT;
"cal" yylval = Quantity::Calorie; return UNIT; // 1 cal = 4.1868 J
"kcal" yylval = Quantity::KiloCalorie; return UNIT;
"\xC2\xB0" yylval = Quantity::Degree; return UNIT; // degree (internal standard angle)
"deg" yylval = Quantity::Degree; return UNIT; // degree (internal standard angle)
"rad" yylval = Quantity::Radian; return UNIT; // radian
"gon" yylval = Quantity::Gon; return UNIT; // gon
"M" yylval = Quantity::AngMinute; return UNIT; // minute(Angular)
"\xE2\x80\xB2" yylval = Quantity::AngMinute; return UNIT; // minute(Angular)
"AS" yylval = Quantity::AngSecond; return UNIT; // second(Angular)
"\xE2\x80\xB3" yylval = Quantity::AngSecond; return UNIT; // second(Angular)
"1" yylval = Quantity(1.0); return ONE;
{DIGIT}+"."?{DIGIT}*{EXPO}? { yylval = Quantity(num_change(yytext,'.',','));return NUM; }
"."?{DIGIT}+{EXPO}? { yylval = Quantity(num_change(yytext,'.',','));return NUM; }
{DIGIT}+","?{DIGIT}*{EXPO}? { yylval = Quantity(num_change(yytext,',','.'));return NUM; }
","?{DIGIT}+{EXPO}? { yylval = Quantity(num_change(yytext,',','.'));return NUM; }
"pi" {yylval = Quantity(std::numbers::pi) ; return NUM;} // constant pi
"e" {yylval = Quantity(std::numbers::e) ; return NUM;} // constant e
"acos" return ACOS;
"asin" return ASIN;
"atan" return ATAN;
"atan2" return ATAN2;
"cos" return COS;
"exp" return EXP;
"abs" return ABS;
"mod" return MOD;
"log" return LOG;
"log10" return LOG10;
"pow" return POW;
"sin" return SIN;
"sinh" return SINH;
"tan" return TAN;
"tanh" return TANH;
"sqrt" return SQRT;
. return *yytext;
-95
View File
@@ -1,95 +0,0 @@
/***************************************************************************
* Copyright (c) 2013 Jürgen Riegel <[email protected]> *
* *
* 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 *
* *
***************************************************************************/
//* Parser for the FreeCAD Units language */
/* Represents the many different ways we can access our data */
%{
#define YYSTYPE Quantity
#define yyparse Quantity_yyparse
#define yyerror Quantity_yyerror
%}
/* Bison declarations. */
%token UNIT ONE NUM MINUSSIGN
%token ACOS ASIN ATAN ATAN2 COS EXP ABS MOD LOG LOG10 POW SIN SINH TAN TANH SQRT;
%left MINUSSIGN '+'
%left '*' '/'
%left NEG /* negation--unary minus */
%right '^' /* exponentiation */
%left ONE NUM
%start input
%%
input: { QuantResult = Quantity(std::numeric_limits<double>::min()); /* empty input */ }
| num { QuantResult = $1 ; }
| unit { QuantResult = $1 ; }
| quantity { QuantResult = $1 ; }
| quantity quantity { QuantResult = $1 + $2; }
| quantity quantity quantity { QuantResult = $1 + $2 + $3; }
;
num: NUM { $$ = $1; }
| ONE { $$ = $1; }
| num '+' num { $$ = Quantity($1.getValue() + $3.getValue()); }
| num MINUSSIGN num { $$ = Quantity($1.getValue() - $3.getValue()); }
| num '*' num { $$ = Quantity($1.getValue() * $3.getValue()); }
| num '/' num { $$ = Quantity($1.getValue() / $3.getValue()); }
| MINUSSIGN num %prec NEG { $$ = Quantity(-$2.getValue()); }
| num '^' num { $$ = Quantity(pow ($1.getValue(), $3.getValue()));}
| '(' num ')' { $$ = $2; }
| ACOS '(' num ')' { $$ = Quantity(acos($3.getValue())); }
| ASIN '(' num ')' { $$ = Quantity(asin($3.getValue())); }
| ATAN '(' num ')' { $$ = Quantity(atan($3.getValue())); }
| ABS '(' num ')' { $$ = Quantity(fabs($3.getValue())); }
| EXP '(' num ')' { $$ = Quantity(exp($3.getValue())); }
| LOG '(' num ')' { $$ = Quantity(log($3.getValue())); }
| LOG10 '(' num ')' { $$ = Quantity(log10($3.getValue())); }
| SIN '(' num ')' { $$ = Quantity(sin($3.getValue())); }
| SINH '(' num ')' { $$ = Quantity(sinh($3.getValue())); }
| TAN '(' num ')' { $$ = Quantity(tan($3.getValue())); }
| TANH '(' num ')' { $$ = Quantity(tanh($3.getValue())); }
| SQRT '(' num ')' { $$ = Quantity(sqrt($3.getValue())); }
| COS '(' num ')' { $$ = Quantity(cos($3.getValue())); }
;
unit: UNIT { $$ = $1; }
| ONE '/' unit { $$ = Quantity(1.0)/$3; }
| unit '*' unit { $$ = $1 * $3; }
| unit '/' unit { $$ = $1 / $3; }
| unit '^' num { $$ = $1.pow ($3); }
| '(' unit ')' { $$ = $2; }
;
quantity: num unit { $$ = $1*$2; }
| num '/' unit { $$ = Quantity($1)/$3; }
;
%%
+1 -1
View File
@@ -2794,7 +2794,7 @@ TEMPLATE_RELATIONS = NO
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
# YES then Doxygen will generate a graph for each documented file showing the
# direct and indirect include dependencies of the file with other documented
# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
# files. Explicit enabling an include graph, when INCLUDE_GRAPH is set to NO,
# can be accomplished by means of the command \includegraph. Disabling an
# include graph can be accomplished by means of the command \hideincludegraph.
# The default value is: YES.
+11 -12
View File
@@ -24,7 +24,7 @@ if(DOXYGEN_FOUND)
SET(DOXYGEN_LANGUAGE "English" CACHE STRING "Language used by doxygen")
MARK_AS_ADVANCED(DOXYGEN_LANGUAGE)
# note: this test is obsolete if DevDoc target is used
# note: this test is obsolete if DevDoc target is used
if (NOT BUILD_GUI)
message("Note: Gui is not built. Documentation may lack some parts.")
endif (NOT BUILD_GUI)
@@ -65,7 +65,7 @@ if(DOXYGEN_FOUND)
find_package(Coin3DDoc)
if( COIN3D_DOC_FOUND )
SET(DOXYGEN_TAGFILES
SET(DOXYGEN_TAGFILES
${COIN3D_DOC_TAGFILE}=${COIN3D_DOC_PATH}
)
endif( COIN3D_DOC_FOUND )
@@ -88,26 +88,25 @@ if(DOXYGEN_FOUND)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/mainpage.dox.in
${CMAKE_CURRENT_BINARY_DIR}/mainpage.dox @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/BuildDevDoc.cfg.in
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/BuildDevDoc.cfg.in
${CMAKE_CURRENT_BINARY_DIR}/BuildDevDoc.cfg @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/BuildWebDoc.cfg.in
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/BuildWebDoc.cfg.in
${CMAKE_CURRENT_BINARY_DIR}/BuildWebDoc.cfg @ONLY)
# reconfigure to have all sources files generated from xml, whatever build options were
# generate source documentation
# delete CMakeCache to reset to default build options next time
file(MAKE_DIRECTORY "${DOXYGEN_OUTPUT_DIR}")
# Note that these two targets do not pick up changes in *.pyi and *Py.xml
# files. To ensure these changes are propagated, execute a build before
# executing these targets.
ADD_CUSTOM_TARGET(DevDoc
${CMAKE_COMMAND} -D BUILD_GUI:BOOL=ON -D BUILD_FEM:BOOL=ON ${CMAKE_SOURCE_DIR}
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/src/Doc/templates ${CMAKE_CURRENT_BINARY_DIR}/templates
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/BuildDevDoc.cfg
COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_BINARY_DIR}/CMakeCache.txt
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
# lightweight version for the web
ADD_CUSTOM_TARGET(WebDoc
${CMAKE_COMMAND} -D BUILD_GUI:BOOL=ON -D BUILD_FEM:BOOL=ON ${CMAKE_SOURCE_DIR}
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/src/Doc/templates ${CMAKE_CURRENT_BINARY_DIR}/templates
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/BuildWebDoc.cfg
COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_BINARY_DIR}/CMakeCache.txt
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
+3 -1
View File
@@ -153,7 +153,9 @@ long NavlibInterface::SetActiveCommand(std::string commandId)
if (!std::string(command->getName()).compare(parsedData.commandName)) {
if (parsedData.actionIndex == -1) {
Gui::Action* pAction = command->getAction();
pAction->action()->trigger();
if (pAction != nullptr) {
pAction->action()->trigger();
}
}
else
command->invoke(parsedData.actionIndex);
+2 -2
View File
@@ -159,7 +159,7 @@ public:
void newObject(const ViewProvider& vp)
{
auto vpd =
Base::freecad_dynamic_cast<ViewProviderDocumentObject>(const_cast<ViewProvider*>(&vp));
freecad_cast<ViewProviderDocumentObject>(const_cast<ViewProvider*>(&vp));
if (vpd && vpd->getObject()) {
map[vpd->getObject()] = vpd;
}
@@ -167,7 +167,7 @@ public:
void deleteObject(const ViewProvider& vp)
{
auto vpd =
Base::freecad_dynamic_cast<ViewProviderDocumentObject>(const_cast<ViewProvider*>(&vp));
freecad_cast<ViewProviderDocumentObject>(const_cast<ViewProvider*>(&vp));
if (vpd && vpd->getObject()) {
map.erase(vpd->getObject());
}
+19 -11
View File
@@ -32,6 +32,7 @@
# include <QImageReader>
# include <QPainter>
# include <QPalette>
# include <QScreen>
# include <QString>
# include <QSvgRenderer>
# include <QStyleOption>
@@ -277,6 +278,8 @@ QPixmap BitmapFactoryInst::pixmap(const char* name) const
QPixmap BitmapFactoryInst::pixmapFromSvg(const char* name, const QSizeF& size,
const ColorMap& colorMapping) const
{
static qreal dpr = getMaximumDPR();
// If an absolute path is given
QPixmap icon;
QString iconPath;
@@ -304,21 +307,15 @@ QPixmap BitmapFactoryInst::pixmapFromSvg(const char* name, const QSizeF& size,
QFile file(iconPath);
if (file.open(QFile::ReadOnly | QFile::Text)) {
QByteArray content = file.readAll();
icon = pixmapFromSvg(content, size, colorMapping);
icon = pixmapFromSvg(content, size * dpr, colorMapping);
}
}
return icon;
}
if (!icon.isNull()) {
icon.setDevicePixelRatio(dpr);
}
QPixmap BitmapFactoryInst::pixmapFromSvg(const char* name, const QSizeF& size, qreal dpr,
const ColorMap& colorMapping) const
{
qreal width = size.width() * dpr;
qreal height = size.height() * dpr;
QPixmap px(pixmapFromSvg(name, QSizeF(width, height), colorMapping));
px.setDevicePixelRatio(dpr);
return px;
return icon;
}
QPixmap BitmapFactoryInst::pixmapFromSvg(const QByteArray& originalContents, const QSizeF& size,
@@ -677,3 +674,14 @@ QIcon BitmapFactoryInst::mergePixmap (const QIcon &base, const QPixmap &px, Gui:
return overlayedIcon;
}
qreal BitmapFactoryInst::getMaximumDPR()
{
qreal dpr = 1.0F;
for (QScreen* screen: QGuiApplication::screens()) {
dpr = std::max(screen->devicePixelRatio(), dpr);
}
return dpr;
}
+2 -8
View File
@@ -87,14 +87,6 @@ public:
*/
QPixmap pixmapFromSvg(const char* name, const QSizeF& size,
const ColorMap& colorMapping = ColorMap()) const;
/** Retrieves a pixmap by name and size created by an
* scalable vector graphics (SVG) and a device pixel ratio
*
* @param colorMapping - a dictionary of substitute colors.
* Can be used to customize icon color scheme, e.g. crosshair color
*/
QPixmap pixmapFromSvg(const char* name, const QSizeF& size, qreal dpr,
const ColorMap& colorMapping = ColorMap()) const;
/** This method is provided for convenience and does the same
* as the method above except that it creates the pixmap from
* a byte array.
@@ -149,6 +141,8 @@ public:
/// Helper method to merge a pixmap into one corner of a QIcon
static QIcon mergePixmap (const QIcon &base, const QPixmap &px, Gui::BitmapFactoryInst::Position position);
static qreal getMaximumDPR();
private:
bool loadPixmap(const QString& path, QPixmap&) const;
void restoreCustomPaths();
+3 -3
View File
@@ -398,7 +398,7 @@ static void linkConvert(bool unlink) {
for(auto &v : infos) {
auto &info = v.second;
auto parent = info.parent.getObject();
auto parentVp = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
auto parentVp = freecad_cast<ViewProviderDocumentObject>(
Application::Instance->getViewProvider(parent));
auto obj = info.obj.getObject();
if(!parent || !obj || !parentVp)
@@ -418,7 +418,7 @@ static void linkConvert(bool unlink) {
FC_THROWM(Base::RuntimeError,"Failed to create link");
link->setLink(-1,obj);
link->Label.setValue(obj->Label.getValue());
auto pla = Base::freecad_dynamic_cast<App::PropertyPlacement>(
auto pla = freecad_cast<App::PropertyPlacement>(
obj->getPropertyByName("Placement"));
if(pla)
link->Placement.setValue(pla->getValue());
@@ -662,7 +662,7 @@ static App::DocumentObject *getSelectedLink(bool finalLink, std::string *subname
auto sobj = sels[0].pObject->getSubObject(sels[0].SubName);
if(!sobj)
return nullptr;
auto vp = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
auto vp = freecad_cast<ViewProviderDocumentObject>(
Application::Instance->getViewProvider(sobj));
if(!vp)
return nullptr;
+1 -4
View File
@@ -2697,10 +2697,7 @@ public:
hotYF *= pRatio;
}
#endif
qreal cursorWidth = size.width() * pRatio;
qreal cursorHeight = size.height() * pRatio;
QPixmap px(Gui::BitmapFactory().pixmapFromSvg(svgFile, QSizeF(cursorWidth, cursorHeight)));
px.setDevicePixelRatio(pRatio);
QPixmap px(Gui::BitmapFactory().pixmapFromSvg(svgFile, size));
return QCursor(px, hotXF, hotYF);
}
};
+1 -1
View File
@@ -146,7 +146,7 @@ Model::Model(QObject *parentIn, const Gui::Document &documentIn) : QGraphicsScen
//NOLINTEND
for (auto obj : documentIn.getDocument()->getObjects()) {
auto vpd = Base::freecad_dynamic_cast<Gui::ViewProviderDocumentObject>(documentIn.getViewProvider(obj));
auto vpd = freecad_cast<Gui::ViewProviderDocumentObject>(documentIn.getViewProvider(obj));
if (vpd)
slotNewObject(*vpd);
}
+1 -1
View File
@@ -145,7 +145,7 @@ void DemoMode::hideEvent(QHideEvent*)
Gui::View3DInventor* DemoMode::activeView() const
{
if (Document* doc = Application::Instance->activeDocument()) {
return Base::freecad_dynamic_cast<Gui::View3DInventor>(doc->getActiveView());
return freecad_cast<Gui::View3DInventor>(doc->getActiveView());
}
return nullptr;
+3 -3
View File
@@ -82,13 +82,13 @@ DlgAddProperty::DlgAddProperty(QWidget* parent,
DlgAddProperty::~DlgAddProperty() = default;
static std::string containerName(const App::PropertyContainer *c) {
auto doc = Base::freecad_dynamic_cast<App::Document>(c);
auto doc = freecad_cast<App::Document>(c);
if(doc)
return doc->getName();
auto obj = Base::freecad_dynamic_cast<App::DocumentObject>(c);
auto obj = freecad_cast<App::DocumentObject>(c);
if(obj)
return obj->getFullName();
auto vpd = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(c);
auto vpd = freecad_cast<ViewProviderDocumentObject>(c);
if(vpd)
return vpd->getObject()->getFullName();
return "?";
+19 -2
View File
@@ -153,7 +153,7 @@ static void printFocusChain(QWidget *widget) {
QWidget* start = widget;
int i = 0;
do {
FC_ERR(" " << widget->objectName().toStdString();
FC_ERR(" " << widget->objectName().toStdString());
widget = widget->nextInFocusChain();
i++;
} while (widget != nullptr && i < 30 && start != widget);
@@ -245,13 +245,27 @@ static PropertyEditor::PropertyItem *createPropertyItem(App::Property *prop)
return item;
}
void DlgAddPropertyVarSet::removeSelectionEditor()
{
// If the editor has a lineedit, then Qt selects the string inside it when
// the editor is created. This interferes with the editor getting focus.
// For example, units will then be selected as well, whereas this is not
// the behavior we want. We therefore deselect the text in the lineedit.
if (auto lineEdit = editor->findChild<QLineEdit*>()) {
lineEdit->deselect();
}
}
void DlgAddPropertyVarSet::addEditor(PropertyEditor::PropertyItem* propertyItem,
[[maybe_unused]]std::string& type)
{
editor.reset(propertyItem->createEditor(this, [this]() {
this->valueChanged();
}));
propertyItem->setEditorData(editor.get(), QVariant());
editor->blockSignals(true);
propertyItem->setEditorData(
editor.get(),
propertyItem->data(PropertyEditor::PropertyItem::ValueColumn, Qt::EditRole));
editor->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
editor->setObjectName(QStringLiteral("editor"));
auto formLayout = qobject_cast<QFormLayout*>(layout());
@@ -260,6 +274,9 @@ void DlgAddPropertyVarSet::addEditor(PropertyEditor::PropertyItem* propertyItem,
QWidget::setTabOrder(ui->comboBoxType, editor.get());
QWidget::setTabOrder(editor.get(), ui->checkBoxAdd);
removeSelectionEditor();
editor->blockSignals(false);
// FC_ERR("add editor");
// printFocusChain(editor.get());
}
+1
View File
@@ -92,6 +92,7 @@ private:
void clearCurrentProperty();
void removeEditor();
void removeSelectionEditor();
void addEditor(PropertyEditor::PropertyItem* propertyItem, std::string& type);
bool isTypeWithEditor(const std::string& type);
+2 -2
View File
@@ -286,7 +286,7 @@ void DlgExpressionInput::checkExpression(const QString& text)
//set default palette as we may have read text right now
ui->msg->setPalette(ui->okBtn->palette());
auto * n = Base::freecad_dynamic_cast<NumberExpression>(result.get());
auto * n = freecad_cast<NumberExpression>(result.get());
if (n) {
Base::Quantity value = n->getQuantity();
if (!value.isValid()) {
@@ -554,7 +554,7 @@ static void addGroupsVarSetComboBox(App::VarSet* varSet, QTreeWidgetItem* varSet
static void addVarSetsVarSetComboBox(std::vector<App::VarSet*>& varSets, QTreeWidgetItem* docItem)
{
for (auto varSet : varSets) {
auto vp = Base::freecad_dynamic_cast<Gui::ViewProviderDocumentObject>(
auto vp = freecad_cast<Gui::ViewProviderDocumentObject>(
Gui::Application::Instance->getViewProvider(varSet));
// the item will be automatically destroyed when the docItem will be destroyed
auto item = new QTreeWidgetItem(docItem);
+1 -1
View File
@@ -194,7 +194,7 @@ QTreeWidgetItem *DlgObjectSelection::getItem(App::DocumentObject *obj,
if (!items.empty())
return items[0];
item = new QTreeWidgetItem(ui->treeWidget);
auto vp = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
auto vp = freecad_cast<ViewProviderDocumentObject>(
Gui::Application::Instance->getViewProvider(obj));
if (vp) item->setIcon(0, vp->getIcon());
App::SubObjectT objT(obj, "");
+4 -4
View File
@@ -272,7 +272,7 @@ void DlgPropertyLink::init(const App::DocumentObjectT& prop, bool tryFilter)
ui->searchBox->setDocumentObject(owner);
auto propLink = Base::freecad_dynamic_cast<App::PropertyLinkBase>(objProp.getProperty());
auto propLink = freecad_cast<App::PropertyLinkBase>(objProp.getProperty());
if (!propLink) {
return;
}
@@ -580,7 +580,7 @@ void DlgPropertyLink::onItemSelectionChanged()
focus = ui->treeWidget->hasFocus();
auto doc = Gui::Application::Instance->getDocument(sobjs.front().getDocumentName().c_str());
if (doc) {
auto vp = Base::freecad_dynamic_cast<Gui::ViewProviderDocumentObject>(
auto vp = freecad_cast<Gui::ViewProviderDocumentObject>(
doc->getViewProvider(obj));
if (vp) {
// If the view provider uses a special window for rendering, switch to it
@@ -1016,7 +1016,7 @@ QTreeWidgetItem* DlgPropertyLink::createItem(App::DocumentObject* obj, QTreeWidg
return nullptr;
}
auto vp = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
auto vp = freecad_cast<ViewProviderDocumentObject>(
Application::Instance->getViewProvider(obj));
if (!vp) {
return nullptr;
@@ -1047,7 +1047,7 @@ QTreeWidgetItem* DlgPropertyLink::createItem(App::DocumentObject* obj, QTreeWidg
QByteArray proxyType;
auto prop =
Base::freecad_dynamic_cast<App::PropertyPythonObject>(obj->getPropertyByName("Proxy"));
freecad_cast<App::PropertyPythonObject>(obj->getPropertyByName("Proxy"));
if (prop) {
Base::PyGILStateLocker lock;
Py::Object proxy = prop->getValue();
+1 -1
View File
@@ -1932,7 +1932,7 @@ void Document::importObjects(const std::vector<App::DocumentObject*>& obj, Base:
Gui::ViewProvider* pObj = this->getViewProviderByName(name.c_str());
if (pObj) {
pObj->setStatus(Gui::isRestoring,true);
auto vpd = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(pObj);
auto vpd = freecad_cast<ViewProviderDocumentObject>(pObj);
if(vpd) vpd->startRestoring();
pObj->Restore(*localreader);
if (expanded && vpd)
+2 -2
View File
@@ -120,7 +120,7 @@ public:
template<typename T>
inline T* getObjectAs() const
{
return Base::freecad_dynamic_cast<T>(getViewProvider());
return freecad_cast<T>(getViewProvider());
}
private:
@@ -215,7 +215,7 @@ public:
template<typename T>
inline T* get() const noexcept
{
return Base::freecad_dynamic_cast<T>(_get());
return freecad_cast<T>(_get());
}
private:
+2 -2
View File
@@ -120,7 +120,7 @@ void InputField::bind(const App::ObjectIdentifier &_path)
{
ExpressionBinding::bind(_path);
auto * prop = freecad_dynamic_cast<PropertyQuantity>(getPath().getProperty());
auto * prop = freecad_cast<PropertyQuantity>(getPath().getProperty());
if (prop)
actQuantity = Base::Quantity(prop->getValue());
@@ -259,7 +259,7 @@ void InputField::newInput(const QString & text)
std::unique_ptr<Expression> evalRes(getExpression()->eval());
auto * value = freecad_dynamic_cast<NumberExpression>(evalRes.get());
auto * value = freecad_cast<NumberExpression>(evalRes.get());
if (value) {
res.setValue(value->getValue());
res.setUnit(value->getUnit());
+46 -16
View File
@@ -36,6 +36,7 @@
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoRotation.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoSphere.h>
#include <Inventor/nodes/SoTransform.h>
#include <Inventor/nodes/SoTranslation.h>
#include <Inventor/nodes/SoFontStyle.h>
@@ -68,19 +69,20 @@ SoFCPlacementIndicatorKit::SoFCPlacementIndicatorKit()
SO_KIT_INIT_INSTANCE();
SO_NODE_ADD_FIELD(coloredAxis, (true));
SO_NODE_ADD_FIELD(scaleFactor, (scaleFactorDefault));
SO_NODE_ADD_FIELD(scaleFactor, (ViewParams::instance()->getPlacementIndicatorScale()));
SO_NODE_ADD_FIELD(axisLength, (axisLengthDefault));
SO_NODE_ADD_FIELD(parts, (AxisCross));
SO_NODE_ADD_FIELD(axes, (AllAxes));
SO_NODE_DEFINE_ENUM_VALUE(Part, Axes);
SO_NODE_DEFINE_ENUM_VALUE(Part, PlaneIndicator);
SO_NODE_DEFINE_ENUM_VALUE(Part, Labels);
SO_NODE_DEFINE_ENUM_VALUE(Part, ArrowHeads);
SO_NODE_DEFINE_ENUM_VALUE(Part, Labels);
SO_NODE_DEFINE_ENUM_VALUE(Part, AxisCross);
SO_NODE_DEFINE_ENUM_VALUE(Part, PlaneIndicator);
SO_NODE_DEFINE_ENUM_VALUE(Part, OriginIndicator);
SO_NODE_DEFINE_ENUM_VALUE(Part, AllParts);
SO_NODE_SET_SF_ENUM_TYPE(parts, Part);
SO_NODE_ADD_FIELD(axes, (AllAxes));
SO_NODE_DEFINE_ENUM_VALUE(Axes, X);
SO_NODE_DEFINE_ENUM_VALUE(Axes, Y);
SO_NODE_DEFINE_ENUM_VALUE(Axes, Z);
@@ -120,12 +122,34 @@ void SoFCPlacementIndicatorKit::recomputeGeometry()
root->setPart("shape", createGeometry());
}
SoSeparator* SoFCPlacementIndicatorKit::createGeometry()
SoSeparator* SoFCPlacementIndicatorKit::createOriginIndicator()
{
const uint32_t originColor = ViewParams::instance()->getOriginColor();
auto sep = new SoSeparator();
auto pcBaseColor = new SoBaseColor();
pcBaseColor->rgb.setValue(0.7f, 0.7f, 0.5f);
pcBaseColor->rgb.setValue(Base::Color::fromPackedRGBA<SbColor>(originColor));
auto pcSphere = new SoSphere();
// the factor aligns radius of sphere and arrow head visually, without it, it looks too small
constexpr float visualAdjustmentFactor = 1.2F;
pcSphere->radius = arrowHeadRadius * visualAdjustmentFactor;
sep->addChild(pcBaseColor);
sep->addChild(pcSphere);
return sep;
}
SoSeparator* SoFCPlacementIndicatorKit::createGeometry()
{
const uint32_t neutralColor = ViewParams::instance()->getNeutralColor();
auto sep = new SoSeparator();
auto pcBaseColor = new SoBaseColor();
pcBaseColor->rgb.setValue(Base::Color::fromPackedRGBA<SbColor>(neutralColor));
auto pcLightModel = new SoLightModel();
pcLightModel->model = SoLightModel::BASE_COLOR;
@@ -137,6 +161,10 @@ SoSeparator* SoFCPlacementIndicatorKit::createGeometry()
sep->addChild(createPlaneIndicator());
}
if (parts.getValue() & OriginIndicator) {
sep->addChild(createOriginIndicator());
}
if (parts.getValue() & Axes) {
sep->addChild(createAxes());
}
@@ -146,24 +174,24 @@ SoSeparator* SoFCPlacementIndicatorKit::createGeometry()
SoSeparator* SoFCPlacementIndicatorKit::createAxes()
{
const auto cylinderOffset = axisLength.getValue() / 2.f;
const auto cylinderOffset = axisLength.getValue() / 2.F;
const auto createAxis = [&](const char* label,
Base::Vector3d axis,
uint32_t packedColor,
const double offset) {
Base::Color axisColor(packedColor);
Base::Rotation rotation(Base::Vector3d::UnitY, axis);
auto sep = new SoSeparator;
auto rotation = Base::Rotation(Base::Vector3d::UnitY, axis);
auto pcTranslate = new SoTransform();
pcTranslate->translation.setValue(
Base::convertTo<SbVec3f>((cylinderOffset + offset) * axis));
pcTranslate->rotation.setValue(Base::convertTo<SbRotation>(rotation));
auto pcArrowShaft = new SoCylinder();
pcArrowShaft->radius = axisThickness / 2.0f;
pcArrowShaft->radius = axisThickness / 2.F;
pcArrowShaft->height = axisLength;
if (coloredAxis.getValue()) {
@@ -216,11 +244,12 @@ SoSeparator* SoFCPlacementIndicatorKit::createAxes()
return sep;
};
auto sep = new SoSeparator;
auto xyOffset = (parts.getValue() & PlaneIndicator)
double additionalAxisMargin = (parts.getValue() & OriginIndicator) ? axisThickness * 4 : 0;
double xyOffset = (parts.getValue() & PlaneIndicator)
? planeIndicatorRadius + planeIndicatorMargin
: axisMargin;
: axisMargin + additionalAxisMargin;
auto sep = new SoSeparator;
if (axes.getValue() & X) {
sep->addChild(createAxis("X",
@@ -237,9 +266,10 @@ SoSeparator* SoFCPlacementIndicatorKit::createAxes()
}
if (axes.getValue() & Z) {
auto zOffset = (parts.getValue() & PlaneIndicator)
double zOffset = (parts.getValue() & PlaneIndicator)
? planeIndicatorMargin
: axisMargin;
: axisMargin + additionalAxisMargin;
sep->addChild(createAxis("Z",
Base::Vector3d::UnitZ,
ViewParams::instance()->getAxisZColor(),
+6 -5
View File
@@ -62,13 +62,14 @@ public:
// clang-format off
enum Part
{
Axes = 1 << 0,
PlaneIndicator = 1 << 1,
Labels = 1 << 2,
ArrowHeads = 1 << 3,
Axes = 1 << 0,
ArrowHeads = 1 << 1,
Labels = 1 << 2,
PlaneIndicator = 1 << 3,
OriginIndicator = 1 << 4,
// common configurations
AllParts = Axes | PlaneIndicator | Labels | ArrowHeads,
AllParts = Axes | OriginIndicator | Labels | ArrowHeads,
AxisCross = Axes | Labels | ArrowHeads
};
+2
View File
@@ -403,6 +403,8 @@ MainWindow::MainWindow(QWidget * parent, Qt::WindowFlags f)
if(notificationAreaEnabled) {
NotificationArea* notificationArea = new NotificationArea(statusBar());
notificationArea->setObjectName(QStringLiteral("notificationArea"));
//: A context menu action used to show or hide the 'notificationArea' toolbar widget
notificationArea->setWindowTitle(tr("Notification area"));
notificationArea->setStyleSheet(QStringLiteral("text-align:left;"));
statusBar()->addPermanentWidget(notificationArea);
}
@@ -554,7 +554,6 @@ void DlgSettingsAdvanced::setOffset1(qreal t)
QPoint pos(width(), 0);
this->b1 = width() - label->fontMetrics().boundingRect(label->text()).width() - 5;
}
label->move(this->a1 * (1-t) + this->b1 * t, label->y());
}
void DlgSettingsAdvanced::onCurveChange(int index)
+1 -1
View File
@@ -139,7 +139,7 @@ public:
if (expr) {
std::unique_ptr<Expression> res(expr->eval());
NumberExpression * n = Base::freecad_dynamic_cast<NumberExpression>(res.get());
NumberExpression * n = freecad_cast<NumberExpression>(res.get());
if (n){
result = n->getQuantity();
value = result.getValue();
+11
View File
@@ -25,6 +25,7 @@
#include <QLabel>
#include <QMouseEvent>
#include <QLineEdit>
class ExpressionLabel : public QLabel
{
@@ -39,6 +40,16 @@ public:
this->setToolTip(expressionEditorTooltipPrefix + text);
}
void show() {
if (auto parentLineEdit = qobject_cast<QLineEdit*>(parent())) {
// horizontal margin, so text will not be behind the icon
QMargins margins = parentLineEdit->contentsMargins();
margins.setRight(2 * margins.right() + sizeHint().width());
parentLineEdit->setContentsMargins(margins);
}
QLabel::show();
}
protected:
void mouseReleaseEvent(QMouseEvent * event) override {
if (rect().contains(event->pos()))
+95
View File
@@ -311,6 +311,101 @@ std::vector<SelectionSingleton::SelObj> SelectionSingleton::getPickedList(const
return temp;
}
std::vector<Gui::SelectionObject> SelectionSingleton::getSelectionIn(App::DocumentObject* container,
Base::Type typeId, bool single) const
{
if (!container) {
return getSelectionEx(nullptr, typeId, ResolveMode::NoResolve, single);
}
std::vector<SelectionObject> sels = getSelectionEx(nullptr, App::DocumentObject::getClassTypeId(), ResolveMode::NoResolve, single);
std::vector<SelectionObject> ret;
std::map<App::DocumentObject*, size_t> SortMap;
for (auto& sel : sels) {
auto* rootObj = sel.getObject();
App::Document* doc = rootObj->getDocument();
std::vector<std::string> subs = sel.getSubNames();
bool objPassed = false;
for (size_t i = 0; i < subs.size(); ++i) {
auto& sub = subs[i];
App::DocumentObject* newRootObj = nullptr;
std::string newSub = "";
std::vector<std::string> names = Base::Tools::splitSubName(sub);
if (container == rootObj) {
objPassed = true;
}
if (rootObj->isLink()) {
// Update doc in case its an external link.
doc = rootObj->getLinkedObject()->getDocument();
}
for (auto& name : names) {
App::DocumentObject* obj = doc->getObject(name.c_str());
if (!obj) { // We reached the element name (for example 'edge1')
newSub += name;
break;
}
if (objPassed) {
if (!newRootObj) {
// We are the first object after the container is passed.
newRootObj = obj;
}
else {
newSub += name + ".";
}
}
if (obj == container) {
objPassed = true;
}
if (obj->isLink()) {
// Update doc in case its an external link.
doc = obj->getLinkedObject()->getDocument();
}
}
if (newRootObj) {
// Make sure selected object is of correct type
auto* lastObj = newRootObj->resolve(newSub.c_str());
if (!lastObj || !lastObj->isDerivedFrom(typeId)) {
continue;
}
auto it = SortMap.find(newRootObj);
if (it != SortMap.end()) {
// only add sub-element
if (newSub != "") {
ret[it->second].SubNames.emplace_back(newSub);
ret[it->second].SelPoses.emplace_back(sel.SelPoses[i]);
}
}
else {
if (single && !ret.empty()) {
ret.clear();
break;
}
// create a new entry
ret.emplace_back(newRootObj);
if (newSub != "") {
ret.back().SubNames.emplace_back(newSub);
ret.back().SelPoses.emplace_back(sel.SelPoses[i]);
}
SortMap.insert(std::make_pair(newRootObj, ret.size() - 1));
}
}
}
}
return ret;
}
std::vector<SelectionObject> SelectionSingleton::getSelectionEx(const char* pDocName, Base::Type typeId,
ResolveMode resolve, bool single) const
{
+20
View File
@@ -445,6 +445,26 @@ public:
*/
std::vector<Gui::SelectionObject> getSelectionEx(const char* pDocName=nullptr,
Base::Type typeId=App::DocumentObject::getClassTypeId(), ResolveMode resolve = ResolveMode::OldStyleElement, bool single=false) const;
/** Returns a vector of selection objects children of an object
*
* @param obj: Object within which you want to find a selection.
* The selection objects returned will be direct children of Obj. The rest of the subname will be unresolved.
* So this is equivalent to ResolveMode::NoResolve, but starting from obj.
* For instance if you have : Assembly.Part.Body.LCS.PlaneXY
* - If obj = Assembly : SelectionObject = Part, subname = "Body.LCS.PlaneXY."
* - If obj = Part : SelectionObject = Body, subname = "LCS.PlaneXY."
* - If obj = Body : SelectionObject = LCS, subname = "PlaneXY."
* The docname used is the document of obj.
* if obj is nullptr, this acts as getSelectionEx with empty docName
*
* @param typeId: specify the type of object to be returned.
* @param single: if set to true, then it will return an empty vector if
* there is more than one selection.
*
* @return The returned vector reflects the sequence of selection.
*/
std::vector<Gui::SelectionObject> getSelectionIn(App::DocumentObject* obj=nullptr,
Base::Type typeId=App::DocumentObject::getClassTypeId(), bool single=false) const;
/**
* @brief getAsPropertyLinkSubList fills PropertyLinkSubList with current selection.
+8 -7
View File
@@ -131,14 +131,16 @@ bool SelectionFilterGatePython::allow(App::Document*, App::DocumentObject* obj,
// ----------------------------------------------------------------------------
SelectionFilter::SelectionFilter(const char* filter)
: Ast(nullptr)
SelectionFilter::SelectionFilter(const char* filter, App::DocumentObject* container)
: Ast(nullptr)
, container(container)
{
setFilter(filter);
}
SelectionFilter::SelectionFilter(const std::string& filter)
: Ast(nullptr)
SelectionFilter::SelectionFilter(const std::string& filter, App::DocumentObject* container)
: Ast(nullptr)
, container(container)
{
setFilter(filter.c_str());
}
@@ -172,8 +174,7 @@ bool SelectionFilter::match()
min = it->Slice->Min;
max = it->Slice->Max;
}
std::vector<Gui::SelectionObject> temp = Gui::Selection().getSelectionEx(nullptr, it->ObjectType);
std::vector<Gui::SelectionObject> temp = container ? Gui::Selection().getSelectionIn(container, it->ObjectType) : Gui::Selection().getSelectionEx(nullptr, it->ObjectType);
// test if subnames present
if (it->SubName.empty()) {
@@ -290,7 +291,7 @@ int SelectionFilterlex();
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wsign-compare"
#endif
#include "lex.SelectionFilter.c"
#include "SelectionFilter.lex.cpp"
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined (__GNUC__)
+3 -2
View File
@@ -53,8 +53,8 @@ class GuiExport SelectionFilter
public:
/** Constructs a SelectionFilter object. */
explicit SelectionFilter(const char* filter);
explicit SelectionFilter(const std::string& filter);
explicit SelectionFilter(const char* filter, App::DocumentObject* container = nullptr);
explicit SelectionFilter(const std::string& filter, App::DocumentObject* container = nullptr);
virtual ~SelectionFilter();
/// Set a new filter string
@@ -88,6 +88,7 @@ protected:
std::string Filter;
std::string Errors;
bool parse();
App::DocumentObject* container;
std::shared_ptr<Node_Block> Ast;
};
+1 -1
View File
@@ -21,7 +21,7 @@
/* Lexer for the FreeCAD selection filter language */
/* flex -o lex.SelectionFilter.c SelectionFilter.l */
/* flex -o SelectionFilter.lex.cpp SelectionFilter.l */
#include <string>
@@ -18,9 +18,9 @@
* *
***************************************************************************/
#line 2 "lex.SelectionFilter.c"
#line 2 "SelectionFilter.lex.cpp"
#line 4 "lex.SelectionFilter.c"
#line 4 "SelectionFilter.lex.cpp"
#define YY_INT_ALIGNED short int
@@ -528,7 +528,7 @@ char *SelectionFiltertext;
/* Lexer for the FreeCAD selection filter language */
/* Copyright (c) 2010 Jürgen Riegel LGPL */
/* flex -o lex.SelectionFilter.c SelectionFilter.l */
/* flex -o SelectionFilter.lex.cpp SelectionFilter.l */
#include <string>
@@ -543,7 +543,7 @@ char *SelectionFiltertext;
/* change the name of the scanner class. results in "SelectionFilterFlexLexer" */
/* the manual says "somewhat more optimized" */
/* no support for include files is planned */
#line 527 "lex.SelectionFilter.c"
#line 527 "SelectionFilter.lex.cpp"
#define INITIAL 0
@@ -763,7 +763,7 @@ YY_DECL
#line 747 "lex.SelectionFilter.c"
#line 747 "SelectionFilter.lex.cpp"
while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */
{
@@ -874,7 +874,7 @@ YY_RULE_SETUP
#line 54 "SelectionFilter.l"
ECHO;
YY_BREAK
#line 858 "lex.SelectionFilter.c"
#line 858 "SelectionFilter.lex.cpp"
case YY_STATE_EOF(INITIAL):
yyterminate();
+1 -1
View File
@@ -965,7 +965,7 @@ void SoDatumLabel::generatePrimitives(SoAction * action)
{
// Initialisation check (needs something more sensible) prevents an infinite loop bug
constexpr float floatEpsilon = std::numeric_limits<float>::epsilon();
if (this->imgHeight <= floatEpsilon | this->imgWidth <= floatEpsilon) {
if (this->imgHeight <= floatEpsilon || this->imgWidth <= floatEpsilon) {
return;
}
+9 -10
View File
@@ -50,18 +50,17 @@ ExpressionSpinBox::ExpressionSpinBox(QAbstractSpinBox* sb)
: spinbox(sb)
{
lineedit = spinbox->findChild<QLineEdit*>();
// Set Margins
// https://forum.freecad.org/viewtopic.php?f=8&t=50615
// vertical margin, otherwise `,` is clipped to a `.` on some OSX versions
int margin = getMargin();
lineedit->setTextMargins(margin, margin, margin, margin);
lineedit->setAlignment(Qt::AlignVCenter);
makeLabel(lineedit);
QObject::connect(iconLabel, &ExpressionLabel::clicked, [this]() {
this->openFormulaDialog();
});
// Set Margins
// vertical to avoid this: https://forum.freecad.org/viewtopic.php?f=8&t=50615
// horizontal to avoid going under the icon
lineedit->setAlignment(Qt::AlignVCenter);
int iconWidth = iconLabel->sizeHint().width();
int margin = getMargin();
lineedit->setTextMargins(margin, margin, margin + iconWidth, margin);
}
ExpressionSpinBox::~ExpressionSpinBox() = default;
@@ -103,7 +102,7 @@ void ExpressionSpinBox::showInvalidExpression(const QString& tip)
void ExpressionSpinBox::showValidExpression(ExpressionSpinBox::Number number)
{
std::unique_ptr<Expression> result(getExpression()->eval());
auto * value = freecad_dynamic_cast<NumberExpression>(result.get());
auto * value = freecad_cast<NumberExpression>(result.get());
if (value) {
switch (number) {
@@ -187,7 +186,7 @@ void ExpressionSpinBox::openFormulaDialog()
{
Q_ASSERT(isBound());
auto * qprop = freecad_dynamic_cast<PropertyQuantity>(getPath().getProperty());
auto * qprop = freecad_cast<PropertyQuantity>(getPath().getProperty());
Unit unit;
if (qprop)
+2 -4
View File
@@ -120,7 +120,7 @@ void ToolHandler::setSvgCursor(const QString& cursorName,
//
qreal pRatio = devicePixelRatio();
bool isRatioOne = (pRatio == 1.0);
qreal defaultCursorSize = isRatioOne ? 64 : 32;
qreal cursorSize = isRatioOne ? 64 : 32;
qreal hotX = x;
qreal hotY = y;
#if !defined(Q_OS_WIN32) && !defined(Q_OS_MACOS)
@@ -129,15 +129,13 @@ void ToolHandler::setSvgCursor(const QString& cursorName,
hotY *= pRatio;
}
#endif
qreal cursorSize = defaultCursorSize * pRatio;
QPixmap pointer = Gui::BitmapFactory().pixmapFromSvg(cursorName.toStdString().c_str(),
QSizeF(cursorSize, cursorSize),
QSizeF{cursorSize, cursorSize},
colorMapping);
if (isRatioOne) {
pointer = pointer.scaled(32, 32);
}
pointer.setDevicePixelRatio(pRatio);
setCursor(pointer, hotX, hotY, false);
}
-288
View File
@@ -1,288 +0,0 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2024 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef GUI_COLORTRAITS_H
#define GUI_COLORTRAITS_H
#include <Base/Color.h>
#include <Inventor/SbColor.h>
#include <Inventor/SbColor4f.h>
#include <QColor>
namespace Base
{
// Specialization for SbColor
template<>
struct color_traits<SbColor>
{
using color_type = SbColor;
color_traits() = default;
explicit color_traits(const color_type& ct)
: ct(ct)
{}
float redF() const
{
return ct[0];
}
float greenF() const
{
return ct[1];
}
float blueF() const
{
return ct[2];
}
float alphaF() const
{
return 1.0F;
}
void setRedF(float red)
{
ct[0] = red;
}
void setGreenF(float green)
{
ct[1] = green;
}
void setBlueF(float blue)
{
ct[2] = blue;
}
void setAlphaF(float alpha)
{
(void)alpha;
}
int red() const
{
return int(std::lround(ct[0] * 255.0F));
}
int green() const
{
return int(std::lround(ct[1] * 255.0F));
}
int blue() const
{
return int(std::lround(ct[2] * 255.0F));
}
int alpha() const
{
return 255;
}
void setRed(int red)
{
ct[0] = static_cast<float>(red) / 255.0F;
}
void setGreen(int green)
{
ct[1] = static_cast<float>(green) / 255.0F;
}
void setBlue(int blue)
{
ct[2] = static_cast<float>(blue) / 255.0F;
}
void setAlpha(int alpha)
{
(void)alpha;
}
static color_type makeColor(int red, int green, int blue, int alpha = 255)
{
(void)alpha;
return color_type{static_cast<float>(red) / 255.0F,
static_cast<float>(green) / 255.0F,
static_cast<float>(blue) / 255.0F};
}
private:
color_type ct;
};
// Specialization for SbColor4f
template<>
struct color_traits<SbColor4f>
{
using color_type = SbColor4f;
color_traits() = default;
explicit color_traits(const color_type& ct)
: ct(ct)
{}
float redF() const
{
return ct[0];
}
float greenF() const
{
return ct[1];
}
float blueF() const
{
return ct[2];
}
float alphaF() const
{
return ct[3];
}
void setRedF(float red)
{
ct[0] = red;
}
void setGreenF(float green)
{
ct[1] = green;
}
void setBlueF(float blue)
{
ct[2] = blue;
}
void setAlphaF(float alpha)
{
ct[3] = alpha;
}
int red() const
{
return int(std::lround(ct[0] * 255.0F));
}
int green() const
{
return int(std::lround(ct[1] * 255.0F));
}
int blue() const
{
return int(std::lround(ct[2] * 255.0F));
}
int alpha() const
{
return int(std::lround(ct[3] * 255.0F));
}
void setRed(int red)
{
ct[0] = static_cast<float>(red) / 255.0F;
}
void setGreen(int green)
{
ct[1] = static_cast<float>(green) / 255.0F;
}
void setBlue(int blue)
{
ct[2] = static_cast<float>(blue) / 255.0F;
}
void setAlpha(int alpha)
{
ct[3] = static_cast<float>(alpha) / 255.0F;
}
static color_type makeColor(int red, int green, int blue, int alpha = 255)
{
return color_type{static_cast<float>(red) / 255.0F,
static_cast<float>(green) / 255.0F,
static_cast<float>(blue) / 255.0F,
static_cast<float>(alpha) / 255.0F};
}
private:
color_type ct;
};
// Specialization for QColor
template<>
struct color_traits<QColor>
{
using color_type = QColor;
color_traits() = default;
explicit color_traits(const color_type& ct)
: ct(ct)
{}
float redF() const
{
return static_cast<float>(ct.redF());
}
float greenF() const
{
return static_cast<float>(ct.greenF());
}
float blueF() const
{
return static_cast<float>(ct.blueF());
}
float alphaF() const
{
return static_cast<float>(ct.alphaF());
}
void setRedF(float red)
{
ct.setRedF(red);
}
void setGreenF(float green)
{
ct.setGreenF(green);
}
void setBlueF(float blue)
{
ct.setBlueF(blue);
}
void setAlphaF(float alpha)
{
ct.setAlphaF(alpha);
}
int red() const
{
return ct.red();
}
int green() const
{
return ct.green();
}
int blue() const
{
return ct.blue();
}
int alpha() const
{
return ct.alpha();
}
void setRed(int red)
{
ct.setRed(red);
}
void setGreen(int green)
{
ct.setGreen(green);
}
void setBlue(int blue)
{
ct.setBlue(blue);
}
void setAlpha(int alpha)
{
ct.setAlpha(alpha);
}
static color_type makeColor(int red, int green, int blue, int alpha = 255)
{
return color_type{red, green, blue, alpha};
}
private:
color_type ct;
};
} // namespace App
#endif // GUI_COLORTRAITS_H
+9 -9
View File
@@ -1918,14 +1918,14 @@ namespace {
if (topObj) {
auto sobj = topObj->getSubObject(info.topSubname.c_str(), nullptr, &mat);
if (sobj == obj) {
propPlacement = Base::freecad_dynamic_cast<App::PropertyPlacement>(
propPlacement = freecad_cast<App::PropertyPlacement>(
obj->getPropertyByName("Placement"));
}
}
}
}
else {
propPlacement = Base::freecad_dynamic_cast<App::PropertyPlacement>(
propPlacement = freecad_cast<App::PropertyPlacement>(
obj->getPropertyByName("Placement"));
if (propPlacement)
mat = propPlacement->getValue().toMatrix();
@@ -2484,7 +2484,7 @@ bool TreeWidget::dropInObject(QDropEvent* event, TargetItemInfo& targetInfo,
for (auto& info : infos) {
auto& subname = info.subname;
targetObj = targetDoc->getObject(target.c_str());
vp = Base::freecad_dynamic_cast<ViewProviderDocumentObject>( Application::Instance->getViewProvider(targetObj));
vp = freecad_cast<ViewProviderDocumentObject>( Application::Instance->getViewProvider(targetObj));
if (!vp) {
FC_ERR("Cannot find drop target object " << target);
break;
@@ -2652,7 +2652,7 @@ bool TreeWidget::dropInObject(QDropEvent* event, TargetItemInfo& targetInfo,
(!info.dragging && sobj->getLinkedObject(false) == obj))
{
if (!info.dragging)
propPlacement = Base::freecad_dynamic_cast<App::PropertyPlacement>(
propPlacement = freecad_cast<App::PropertyPlacement>(
sobj->getPropertyByName("Placement"));
if (propPlacement) {
newMat *= propPlacement->getValue().inverse().toMatrix();
@@ -2791,7 +2791,7 @@ void TreeWidget::sortDroppedObjects(TargetItemInfo& targetInfo, std::vector<App:
auto targetItemObj = static_cast<DocumentObjectItem*>(targetInfo.targetItem);
App::DocumentObject* targetObj = targetItemObj->object()->getObject();
auto propGroup = Base::freecad_dynamic_cast<App::PropertyLinkList>(targetObj->getPropertyByName("Group"));
auto propGroup = freecad_cast<App::PropertyLinkList>(targetObj->getPropertyByName("Group"));
if (!propGroup) {
return;
}
@@ -3061,7 +3061,7 @@ void TreeWidget::onUpdateStatus()
errors.push_back(obj);
if (docItem->ObjectMap.count(obj))
continue;
auto vpd = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(gdoc->getViewProvider(obj));
auto vpd = freecad_cast<ViewProviderDocumentObject>(gdoc->getViewProvider(obj));
if (vpd)
docItem->createNewItem(*vpd);
}
@@ -3746,7 +3746,7 @@ void TreeWidget::selectLinkedObject(App::DocumentObject* linked) {
if (!isSelectionAttached() || isSelectionBlocked())
return;
auto linkedVp = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
auto linkedVp = freecad_cast<ViewProviderDocumentObject>(
Application::Instance->getViewProvider(linked));
if (!linkedVp) {
TREE_ERR("invalid linked view provider");
@@ -3962,7 +3962,7 @@ bool DocumentItem::createNewItem(const Gui::ViewProviderDocumentObject& obj,
}
ViewProviderDocumentObject* DocumentItem::getViewProvider(App::DocumentObject* obj) {
return Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
return freecad_cast<ViewProviderDocumentObject>(
Application::Instance->getViewProvider(obj));
}
@@ -5488,7 +5488,7 @@ void DocumentObjectItem::testStatus(bool resetStatus, QIcon& icon1, QIcon& icon2
if (currentStatus & Status::External) {
static QPixmap pxExternal;
int px = 12 * getMainWindow()->devicePixelRatioF();
constexpr int px = 12;
if (pxExternal.isNull()) {
pxExternal = Gui::BitmapFactory().pixmapFromSvg("LinkOverlay",
QSize(px, px));
+253
View File
@@ -29,6 +29,7 @@
#include <Base/Converter.h>
#include <Base/ViewProj.h>
#include <Inventor/SbColor.h>
#include <Inventor/SbColor4f.h>
#include <Inventor/SbMatrix.h>
#include <Inventor/SbRotation.h>
#include <Inventor/SbVec2f.h>
@@ -117,6 +118,258 @@ private:
const vec_type& v;
};
// Specialization for SbColor
template<>
struct color_traits<SbColor>
{
using color_type = SbColor;
color_traits() = default;
explicit color_traits(const color_type& ct)
: ct(ct)
{}
float redF() const
{
return ct[0];
}
float greenF() const
{
return ct[1];
}
float blueF() const
{
return ct[2];
}
float alphaF() const
{
return 1.0F;
}
void setRedF(float red)
{
ct[0] = red;
}
void setGreenF(float green)
{
ct[1] = green;
}
void setBlueF(float blue)
{
ct[2] = blue;
}
void setAlphaF(float alpha)
{
(void)alpha;
}
int red() const
{
return int(std::lround(ct[0] * 255.0F));
}
int green() const
{
return int(std::lround(ct[1] * 255.0F));
}
int blue() const
{
return int(std::lround(ct[2] * 255.0F));
}
int alpha() const
{
return 255;
}
void setRed(int red)
{
ct[0] = static_cast<float>(red) / 255.0F;
}
void setGreen(int green)
{
ct[1] = static_cast<float>(green) / 255.0F;
}
void setBlue(int blue)
{
ct[2] = static_cast<float>(blue) / 255.0F;
}
void setAlpha(int alpha)
{
(void)alpha;
}
static color_type makeColor(int red, int green, int blue, int alpha = 255)
{
(void)alpha;
return color_type{static_cast<float>(red) / 255.0F,
static_cast<float>(green) / 255.0F,
static_cast<float>(blue) / 255.0F};
}
private:
color_type ct;
};
// Specialization for SbColor4f
template<>
struct color_traits<SbColor4f>
{
using color_type = SbColor4f;
color_traits() = default;
explicit color_traits(const color_type& ct)
: ct(ct)
{}
float redF() const
{
return ct[0];
}
float greenF() const
{
return ct[1];
}
float blueF() const
{
return ct[2];
}
float alphaF() const
{
return ct[3];
}
void setRedF(float red)
{
ct[0] = red;
}
void setGreenF(float green)
{
ct[1] = green;
}
void setBlueF(float blue)
{
ct[2] = blue;
}
void setAlphaF(float alpha)
{
ct[3] = alpha;
}
int red() const
{
return int(std::lround(ct[0] * 255.0F));
}
int green() const
{
return int(std::lround(ct[1] * 255.0F));
}
int blue() const
{
return int(std::lround(ct[2] * 255.0F));
}
int alpha() const
{
return int(std::lround(ct[3] * 255.0F));
}
void setRed(int red)
{
ct[0] = static_cast<float>(red) / 255.0F;
}
void setGreen(int green)
{
ct[1] = static_cast<float>(green) / 255.0F;
}
void setBlue(int blue)
{
ct[2] = static_cast<float>(blue) / 255.0F;
}
void setAlpha(int alpha)
{
ct[3] = static_cast<float>(alpha) / 255.0F;
}
static color_type makeColor(int red, int green, int blue, int alpha = 255)
{
return color_type{static_cast<float>(red) / 255.0F,
static_cast<float>(green) / 255.0F,
static_cast<float>(blue) / 255.0F,
static_cast<float>(alpha) / 255.0F};
}
private:
color_type ct;
};
// Specialization for QColor
template<>
struct color_traits<QColor>
{
using color_type = QColor;
color_traits() = default;
explicit color_traits(const color_type& ct)
: ct(ct)
{}
float redF() const
{
return static_cast<float>(ct.redF());
}
float greenF() const
{
return static_cast<float>(ct.greenF());
}
float blueF() const
{
return static_cast<float>(ct.blueF());
}
float alphaF() const
{
return static_cast<float>(ct.alphaF());
}
void setRedF(float red)
{
ct.setRedF(red);
}
void setGreenF(float green)
{
ct.setGreenF(green);
}
void setBlueF(float blue)
{
ct.setBlueF(blue);
}
void setAlphaF(float alpha)
{
ct.setAlphaF(alpha);
}
int red() const
{
return ct.red();
}
int green() const
{
return ct.green();
}
int blue() const
{
return ct.blue();
}
int alpha() const
{
return ct.alpha();
}
void setRed(int red)
{
ct.setRed(red);
}
void setGreen(int green)
{
ct.setGreen(green);
}
void setBlue(int blue)
{
ct.setBlue(blue);
}
void setAlpha(int alpha)
{
ct.setAlpha(alpha);
}
static color_type makeColor(int red, int green, int blue, int alpha = 255)
{
return color_type{red, green, blue, alpha};
}
private:
color_type ct;
};
template <>
inline SbMatrix convertTo<SbMatrix, Base::Matrix4D>(const Base::Matrix4D& vec2)
{
+3
View File
@@ -2135,6 +2135,9 @@ Py::Object View3DInventorPy::getSceneGraph()
{
try {
SoNode* scene = getView3DInventorPtr()->getViewer()->getSceneGraph();
if (scene == nullptr) {
return Py::None();
}
PyObject* proxy = nullptr;
proxy = Base::Interpreter().createSWIGPointerObj("pivy.coin", "SoSeparator *", static_cast<void*>(scene), 1);
scene->ref();
+3
View File
@@ -66,6 +66,9 @@ public:
FC_VIEW_PARAM(AxisXColor,unsigned long,Unsigned,0xCC333300) \
FC_VIEW_PARAM(AxisYColor,unsigned long,Unsigned,0x33CC3300) \
FC_VIEW_PARAM(AxisZColor,unsigned long,Unsigned,0x3333CC00) \
FC_VIEW_PARAM(OriginColor,unsigned long,Unsigned,0xFBD62900) \
FC_VIEW_PARAM(NeutralColor,unsigned long,Unsigned,0xB3B38000) \
FC_VIEW_PARAM(PlacementIndicatorScale,double,Float,40.0) \
FC_VIEW_PARAM(DraggerScale,double,Float,0.03) \
+3 -3
View File
@@ -547,7 +547,7 @@ int ViewProviderDocumentObject::replaceObject(
std::vector<App::Property*> props;
obj->getPropertyList(props);
for(auto prop : props) {
auto linkProp = Base::freecad_dynamic_cast<App::PropertyLinkBase>(prop);
auto linkProp = freecad_cast<App::PropertyLinkBase>(prop);
if(!linkProp)
continue;
std::unique_ptr<App::Property> copy(linkProp->CopyOnLinkReplace(obj, oldObj,newObj));
@@ -567,7 +567,7 @@ int ViewProviderDocumentObject::replaceObject(
std::vector<App::Property*> props;
o->getPropertyList(props);
for(auto prop : props) {
auto linkProp = Base::freecad_dynamic_cast<App::PropertyLinkBase>(prop);
auto linkProp = freecad_cast<App::PropertyLinkBase>(prop);
if(!linkProp)
continue;
std::unique_ptr<App::Property> copy(linkProp->CopyOnLinkReplace(obj,oldObj,newObj));
@@ -691,7 +691,7 @@ ViewProviderDocumentObject *ViewProviderDocumentObject::getLinkedViewProvider(
auto linked = pcObject->getLinkedObject(recursive);
if(!linked || linked == pcObject)
return self;
auto res = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
auto res = freecad_cast<ViewProviderDocumentObject>(
Application::Instance->getViewProvider(linked));
if(!res)
res = self;
+17 -17
View File
@@ -745,7 +745,7 @@ void ViewProviderLinkObserver::extensionBeforeDelete() {
void ViewProviderLinkObserver::extensionReattach(App::DocumentObject *) {
if(linkInfo) {
linkInfo->pcLinked =
Base::freecad_dynamic_cast<ViewProviderDocumentObject>(getExtendedContainer());
freecad_cast<ViewProviderDocumentObject>(getExtendedContainer());
linkInfo->update();
}
}
@@ -755,7 +755,7 @@ void ViewProviderLinkObserver::extensionOnChanged(const App::Property *prop) {
}
void ViewProviderLinkObserver::extensionModeSwitchChange() {
auto owner = freecad_dynamic_cast<ViewProviderDocumentObject>(getExtendedContainer());
auto owner = freecad_cast<ViewProviderDocumentObject>(getExtendedContainer());
if(owner && linkInfo)
linkInfo->updateSwitch();
}
@@ -1026,7 +1026,7 @@ void LinkView::setMaterial(int index, const App::Material *material) {
}
void LinkView::setLink(App::DocumentObject *obj, const std::vector<std::string> &subs) {
setLinkViewObject(Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
setLinkViewObject(freecad_cast<ViewProviderDocumentObject>(
Application::Instance->getViewProvider(obj)),subs);
}
@@ -1730,7 +1730,7 @@ QIcon ViewProviderLink::getIcon() const {
QPixmap ViewProviderLink::getOverlayPixmap() const {
auto ext = getLinkExtension();
int px = 12 * getMainWindow()->devicePixelRatioF();
constexpr int px = 12;
if(ext && ext->getLinkedObjectProperty() && ext->_getElementCountValue())
return BitmapFactory().pixmapFromSvg("LinkArrayOverlay", QSizeF(px,px));
else if(hasSubElement)
@@ -1743,7 +1743,7 @@ QPixmap ViewProviderLink::getOverlayPixmap() const {
void ViewProviderLink::onChanged(const App::Property* prop) {
if(prop==&ChildViewProvider) {
childVp = freecad_dynamic_cast<ViewProviderDocumentObject>(ChildViewProvider.getObject().get());
childVp = freecad_cast<ViewProviderDocumentObject>(ChildViewProvider.getObject().get());
if(childVp && getObject()) {
if(strcmp(childVp->getTypeId().getName(),getObject()->getViewProviderName())!=0
&& !childVp->allowOverride(*getObject()))
@@ -1864,7 +1864,7 @@ void ViewProviderLink::updateDataPrivate(App::LinkBaseExtension *ext, const App:
}
}else if(prop == ext->getLinkCopyOnChangeGroupProperty()) {
if (auto group = ext->getLinkCopyOnChangeGroupValue()) {
auto vp = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
auto vp = freecad_cast<ViewProviderDocumentObject>(
Application::Instance->getViewProvider(group));
if (vp) {
vp->hide();
@@ -1915,7 +1915,7 @@ void ViewProviderLink::updateDataPrivate(App::LinkBaseExtension *ext, const App:
}else if(prop == ext->_getShowElementProperty()) {
if(!ext->_getShowElementValue()) {
auto linked = freecad_dynamic_cast<ViewProviderDocumentObject>(getLinkedView(true,ext));
auto linked = freecad_cast<ViewProviderDocumentObject>(getLinkedView(true,ext));
if(linked && linked->getDocument()==getDocument())
linked->hide();
@@ -1929,9 +1929,9 @@ void ViewProviderLink::updateDataPrivate(App::LinkBaseExtension *ext, const App:
bool hasMaterial = false;
materials.reserve(elements.size());
for(size_t i=0;i<elements.size();++i) {
auto element = freecad_dynamic_cast<App::LinkElement>(elements[i]);
auto element = freecad_cast<App::LinkElement>(elements[i]);
if(!element) continue;
auto vp = freecad_dynamic_cast<ViewProviderLink>(
auto vp = freecad_cast<ViewProviderLink>(
Application::Instance->getViewProvider(element));
if(!vp) continue;
overrideMaterial = overrideMaterial || vp->OverrideMaterial.getValue();
@@ -2016,7 +2016,7 @@ void ViewProviderLink::updateElementList(App::LinkBaseExtension *ext) {
int i=-1;
for(auto obj : elements) {
++i;
auto vp = freecad_dynamic_cast<ViewProviderLink>(
auto vp = freecad_cast<ViewProviderLink>(
Application::Instance->getViewProvider(obj));
if(!vp) continue;
if(OverrideMaterialList.getSize()>i)
@@ -2234,7 +2234,7 @@ bool ViewProviderLink::canDropObjectEx(App::DocumentObject *obj,
if(!hasSubName && linkView->isLinked()) {
auto linked = getLinkedView(false,ext);
if(linked) {
auto linkedVdp = freecad_dynamic_cast<ViewProviderDocumentObject>(linked);
auto linkedVdp = freecad_cast<ViewProviderDocumentObject>(linked);
if(linkedVdp) {
if(linkedVdp->getObject()==obj || linkedVdp->getObject()==owner)
return false;
@@ -2243,7 +2243,7 @@ bool ViewProviderLink::canDropObjectEx(App::DocumentObject *obj,
}
}
if(obj->getDocument() != getObject()->getDocument() &&
!freecad_dynamic_cast<App::PropertyXLink>(ext->getLinkedObjectProperty()))
!freecad_cast<App::PropertyXLink>(ext->getLinkedObjectProperty()))
return false;
return true;
@@ -2600,7 +2600,7 @@ void ViewProviderLink::_setupContextMenu(
App::LinkBaseExtension *ext, QMenu* menu, QObject* receiver, const char* member)
{
if(linkEdit(ext)) {
if (auto linkvp = Base::freecad_dynamic_cast<ViewProviderLink>(linkView->getLinkedView()))
if (auto linkvp = freecad_cast<ViewProviderLink>(linkView->getLinkedView()))
linkvp->_setupContextMenu(ext, menu, receiver, member);
else
linkView->getLinkedView()->setupContextMenu(menu,receiver,member);
@@ -2743,7 +2743,7 @@ bool ViewProviderLink::initDraggingPlacement() {
{
App::PropertyPlacement *propPla = nullptr;
if(ext->getLinkTransformValue() && ext->getLinkedObjectValue()) {
propPla = Base::freecad_dynamic_cast<App::PropertyPlacement>(
propPla = freecad_cast<App::PropertyPlacement>(
ext->getLinkedObjectValue()->getPropertyByName("Placement"));
}
if(propPla) {
@@ -2828,7 +2828,7 @@ ViewProvider *ViewProviderLink::startEditing(int mode) {
FC_ERR("no linked object");
return nullptr;
}
auto vpd = freecad_dynamic_cast<ViewProviderDocumentObject>(
auto vpd = freecad_cast<ViewProviderDocumentObject>(
Application::Instance->getViewProvider(linked));
if(!vpd) {
FC_ERR("no linked viewprovider");
@@ -3018,7 +3018,7 @@ std::map<std::string, Base::Color> ViewProviderLink::getElementColors(const char
auto link = vp->getObject()->getLinkedObject(false);
if(!link || link==vp->getObject())
break;
auto next = freecad_dynamic_cast<ViewProviderLink>(
auto next = freecad_cast<ViewProviderLink>(
Application::Instance->getViewProvider(link));
if(!next)
break;
@@ -3341,7 +3341,7 @@ ViewProviderDocumentObject *ViewProviderLink::getLinkedViewProvider(
linked = ext->getTrueLinkedObject(recursive);
if(!linked)
return self;
auto res = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
auto res = freecad_cast<ViewProviderDocumentObject>(
Application::Instance->getViewProvider(linked));
if(res)
return res;
+2 -2
View File
@@ -319,9 +319,9 @@ void PropertyEditor::openEditor(const QModelIndex& index)
}
auto prop = items[0];
auto parent = prop->getContainer();
auto obj = Base::freecad_dynamic_cast<App::DocumentObject>(parent);
auto obj = freecad_cast<App::DocumentObject>(parent);
if (!obj) {
auto view = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(parent);
auto view = freecad_cast<ViewProviderDocumentObject>(parent);
if (view) {
obj = view->getObject();
}
+7 -8
View File
@@ -158,7 +158,7 @@ void PropertyItem::setPropertyData(const std::vector<App::Property*>& items)
try {
// Check for 'DocumentObject' as parent because otherwise 'ObjectIdentifier' raises an
// exception
auto* docObj = Base::freecad_dynamic_cast<App::DocumentObject>(prop.getContainer());
auto* docObj = freecad_cast<App::DocumentObject>(prop.getContainer());
if (docObj && !docObj->isReadOnly(&prop)) {
App::ObjectIdentifier id(prop);
std::vector<App::ObjectIdentifier> paths;
@@ -296,7 +296,7 @@ int PropertyItem::childCount() const
int PropertyItem::columnCount() const
{
return 2;
return PropertyItem::ColumnCount;
}
void PropertyItem::setReadOnly(bool ro)
@@ -650,7 +650,7 @@ void PropertyItem::setPropertyValue(const QString& value)
setPropertyValue(value.toStdString());
}
QVariant PropertyItem::dataProperty(int role) const
QVariant PropertyItem::dataPropertyName(int role) const
{
if (role == Qt::ForegroundRole && linked) {
return QVariant::fromValue(QColor(0x20, 0xaa, 0x20)); // NOLINT
@@ -743,9 +743,8 @@ QVariant PropertyItem::dataValue(int role) const
QVariant PropertyItem::data(int column, int role) const
{
// property name
if (column == 0) {
return dataProperty(role);
if (column == PropertyItem::NameColumn) {
return dataPropertyName(role);
}
return dataValue(role);
@@ -4559,7 +4558,7 @@ void LinkLabel::updatePropertyLink()
{
QString text;
auto owner = objProp.getObject();
auto prop = Base::freecad_dynamic_cast<App::PropertyLinkBase>(objProp.getProperty());
auto prop = freecad_cast<App::PropertyLinkBase>(objProp.getProperty());
link = QVariant();
@@ -4674,7 +4673,7 @@ QVariant PropertyLinkItem::data(int column, int role) const
QVariant PropertyLinkItem::value(const App::Property* prop) const
{
auto propLink = Base::freecad_dynamic_cast<App::PropertyLinkBase>(prop);
auto propLink = freecad_cast<App::PropertyLinkBase>(prop);
if (!propLink) {
return {};
}
+7 -1
View File
@@ -130,6 +130,12 @@ class GuiExport PropertyItem: public QObject, public ExpressionBinding
PROPERTYITEM_HEADER
public:
enum Column {
NameColumn = 0,
ValueColumn = 1,
ColumnCount
};
~PropertyItem() override;
/** Sets the current property objects. */
@@ -216,7 +222,7 @@ protected:
void onChange() override;
private:
QVariant dataProperty(int role) const;
QVariant dataPropertyName(int role) const;
QVariant dataValue(int role) const;
QString toString(const Py::Object&) const;
QString asNone(const Py::Object&) const;
-3
View File
@@ -179,9 +179,6 @@ int main(int argc, char** argv)
}
argv_.push_back(0); // 0-terminated string
}
// https://www.qt.io/blog/dark-mode-on-windows-11-with-qt-6.5
_putenv("QT_QPA_PLATFORM=windows:darkmode=1");
#endif
// Name and Version of the Application
@@ -1,35 +0,0 @@
# This file lists the Python packages that the Addon Manager allows to be installed
# automatically via pip. To request that a package be added to this list, submit a
# pull request to the FreeCAD git repository with the requested package added. Only
# packages in this list will be processed from the metadata.txt and requirements.txt
# files specified by an Addon. Note that this is NOT a requirements.txt-format file,
# no version information may be specified, and no wildcards are supported.
# Allow these packages to be installed:
aiofiles
autobahn
ezdxf
gmsh
gmsh-dev
lxml
markdown
matplotlib
msgpack
numpy
ocp
olefile
openpyxl
pandas
pillow
ply
py-slvs
pycollada
pygit2
pynastran
requests
rhino3dm
scipy
xlrd
xlutils
xlwt
PyYAML
-863
View File
@@ -1,863 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2022-2023 FreeCAD Project Association *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD 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 *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
"""Defines the Addon class to encapsulate information about FreeCAD Addons"""
import os
import re
from datetime import datetime
from urllib.parse import urlparse
from typing import Dict, Set, List, Optional
from threading import Lock
from enum import IntEnum, auto
import xml.etree.ElementTree
import addonmanager_freecad_interface as fci
from addonmanager_macro import Macro
import addonmanager_utilities as utils
from addonmanager_utilities import construct_git_url, process_date_string_to_python_datetime
from addonmanager_metadata import (
Metadata,
MetadataReader,
UrlType,
Version,
DependencyType,
)
from AddonStats import AddonStats
translate = fci.translate
# A list of internal workbenches that can be used as a dependency of an Addon
INTERNAL_WORKBENCHES = {
"bim": "BIM",
"assembly": "Assembly",
"draft": "Draft",
"fem": "FEM",
"mesh": "Mesh",
"openscad": "OpenSCAD",
"part": "Part",
"partdesign": "PartDesign",
"cam": "CAM",
"plot": "Plot",
"points": "Points",
"robot": "Robot",
"sketcher": "Sketcher",
"spreadsheet": "Spreadsheet",
"techdraw": "TechDraw",
}
class Addon:
"""Encapsulates information about a FreeCAD addon"""
class Kind(IntEnum):
"""The type of Addon: Workbench, macro, or package"""
WORKBENCH = 1
MACRO = 2
PACKAGE = 3
def __str__(self) -> str:
if self.value == 1:
return "Workbench"
if self.value == 2:
return "Macro"
if self.value == 3:
return "Package"
return "ERROR_TYPE"
class Status(IntEnum):
"""The installation status of an Addon"""
NOT_INSTALLED = 0
UNCHECKED = 1
NO_UPDATE_AVAILABLE = 2
UPDATE_AVAILABLE = 3
PENDING_RESTART = 4
CANNOT_CHECK = 5 # If we don't have git, etc.
UNKNOWN = 100
def __lt__(self, other):
if self.__class__ is other.__class__:
return self.value < other.value
return NotImplemented
def __str__(self) -> str:
if self.value == 0:
result = "Not installed"
elif self.value == 1:
result = "Unchecked"
elif self.value == 2:
result = "No update available"
elif self.value == 3:
result = "Update available"
elif self.value == 4:
result = "Restart required"
elif self.value == 5:
result = "Can't check"
else:
result = "ERROR_STATUS"
return result
class Dependencies:
"""Addon dependency information"""
def __init__(self):
self.required_external_addons = [] # A list of Addons
self.blockers = [] # A list of Addons
self.replaces = [] # A list of Addons
self.internal_workbenches: Set[str] = set() # Required internal workbenches
self.python_requires: Set[str] = set()
self.python_optional: Set[str] = set()
self.python_min_version = {"major": 3, "minor": 0}
class DependencyType(IntEnum):
"""Several types of dependency information is stored"""
INTERNAL_WORKBENCH = auto()
REQUIRED_ADDON = auto()
BLOCKED_ADDON = auto()
REPLACED_ADDON = auto()
REQUIRED_PYTHON = auto()
OPTIONAL_PYTHON = auto()
class ResolutionFailed(RuntimeError):
"""An exception type for dependency resolution failure."""
# The location of Addon Manager cache files: overridden by testing code
cache_directory = os.path.join(fci.DataPaths().cache_dir, "AddonManager")
# The location of the Mod directory: overridden by testing code
mod_directory = fci.DataPaths().mod_dir
# The location of the Macro directory: overridden by testing code
macro_directory = fci.DataPaths().macro_dir
def __init__(
self,
name: str,
url: str = "",
status: Status = Status.UNKNOWN,
branch: str = "",
):
self.name = name.strip()
self.display_name = self.name
self.url = url.strip()
self.branch = branch.strip()
self.python2 = False
self.obsolete = False
self.rejected = False
self.repo_type = Addon.Kind.WORKBENCH
self.description = None
self.tags = set() # Just a cache, loaded from Metadata
self.last_updated = None
self.stats = AddonStats()
self.score = 0
# To prevent multiple threads from running git actions on this repo at the
# same time
self.git_lock = Lock()
# To prevent multiple threads from accessing the status at the same time
self.status_lock = Lock()
self.update_status = status
self._clean_url()
if utils.recognized_git_location(self):
self.metadata_url = construct_git_url(self, "package.xml")
else:
self.metadata_url = None
self.metadata: Optional[Metadata] = None
self.icon = None # A QIcon version of this Addon's icon
self.icon_file: str = "" # Absolute local path to cached icon file
self.best_icon_relative_path = ""
self.macro = None # Bridge to Gaël Écorchard's macro management class
self.updated_timestamp = None
self.installed_version = None
self.installed_metadata = None
# Each repo is also a node in a directed dependency graph (referenced by name so
# they can be serialized):
self.requires: Set[str] = set()
self.blocks: Set[str] = set()
# And maintains a list of required and optional Python dependencies
self.python_requires: Set[str] = set()
self.python_optional: Set[str] = set()
self.python_min_version = {"major": 3, "minor": 0}
self._icon_file = None
self._cached_license: str = ""
self._cached_update_date = None
def _clean_url(self):
# The url should never end in ".git", so strip it if it's there
parsed_url = urlparse(self.url)
if parsed_url.path.endswith(".git"):
self.url = parsed_url.scheme + "://" + parsed_url.netloc + parsed_url.path[:-4]
if parsed_url.query:
self.url += "?" + parsed_url.query
if parsed_url.fragment:
self.url += "#" + parsed_url.fragment
def __str__(self) -> str:
result = f"FreeCAD {self.repo_type}\n"
result += f"Name: {self.name}\n"
result += f"URL: {self.url}\n"
result += "Has metadata\n" if self.metadata is not None else "No metadata found\n"
if self.macro is not None:
result += "Has linked Macro object\n"
return result
@property
def license(self):
if not self._cached_license:
self._cached_license = "UNLICENSED"
if self.metadata and self.metadata.license:
self._cached_license = self.metadata.license
elif self.stats and self.stats.license:
self._cached_license = self.stats.license
elif self.macro:
if self.macro.license:
self._cached_license = self.macro.license
elif self.macro.on_wiki:
self._cached_license = "CC-BY-3.0"
return self._cached_license
@property
def update_date(self):
if self._cached_update_date is None:
self._cached_update_date = 0
if self.stats and self.stats.last_update_time:
self._cached_update_date = self.stats.last_update_time
elif self.macro and self.macro.date:
# Try to parse the date:
try:
self._cached_update_date = process_date_string_to_python_datetime(
self.macro.date
)
except ValueError as e:
fci.Console.PrintWarning(str(e) + "\n")
else:
fci.Console.PrintWarning(f"No update date info for {self.name}\n")
return self._cached_update_date
@classmethod
def from_macro(cls, macro: Macro):
"""Create an Addon object from a Macro wrapper object"""
if macro.is_installed():
status = Addon.Status.UNCHECKED
else:
status = Addon.Status.NOT_INSTALLED
instance = Addon(macro.name, macro.url, status, "master")
instance.macro = macro
instance.repo_type = Addon.Kind.MACRO
instance.description = macro.desc
return instance
@classmethod
def from_cache(cls, cache_dict: Dict):
"""Load basic data from cached dict data. Does not include Macro or Metadata
information, which must be populated separately."""
mod_dir = os.path.join(cls.mod_directory, cache_dict["name"])
if os.path.isdir(mod_dir):
status = Addon.Status.UNCHECKED
else:
status = Addon.Status.NOT_INSTALLED
instance = Addon(cache_dict["name"], cache_dict["url"], status, cache_dict["branch"])
for key, value in cache_dict.items():
if not str(key).startswith("_"):
instance.__dict__[key] = value
instance.repo_type = Addon.Kind(cache_dict["repo_type"])
if instance.repo_type == Addon.Kind.PACKAGE:
# There must be a cached metadata file, too
cached_package_xml_file = os.path.join(
instance.cache_directory,
"PackageMetadata",
instance.name,
)
if os.path.isfile(cached_package_xml_file):
instance.load_metadata_file(cached_package_xml_file)
instance._load_installed_metadata()
if "requires" in cache_dict:
instance.requires = set(cache_dict["requires"])
instance.blocks = set(cache_dict["blocks"])
instance.python_requires = set(cache_dict["python_requires"])
instance.python_optional = set(cache_dict["python_optional"])
instance._clean_url()
return instance
def to_cache(self) -> Dict:
"""Returns a dictionary with cache information that can be used later with
from_cache to recreate this object."""
return {
"name": self.name,
"display_name": self.display_name,
"url": self.url,
"branch": self.branch,
"repo_type": int(self.repo_type),
"description": self.description,
"cached_icon_filename": self.get_cached_icon_filename(),
"best_icon_relative_path": self.get_best_icon_relative_path(),
"python2": self.python2,
"obsolete": self.obsolete,
"rejected": self.rejected,
"requires": list(self.requires),
"blocks": list(self.blocks),
"python_requires": list(self.python_requires),
"python_optional": list(self.python_optional),
}
def load_metadata_file(self, file: str) -> None:
"""Read a given metadata file and set it as this object's metadata"""
if os.path.exists(file):
try:
metadata = MetadataReader.from_file(file)
except xml.etree.ElementTree.ParseError:
fci.Console.PrintWarning(
"An invalid or corrupted package.xml file was found in the cache for"
)
fci.Console.PrintWarning(f" {self.name}... ignoring the bad data.\n")
return
self.set_metadata(metadata)
self._clean_url()
else:
fci.Console.PrintLog(f"Internal error: {file} does not exist")
def _load_installed_metadata(self) -> None:
# If it is actually installed, there is a SECOND metadata file, in the actual installation,
# that may not match the cached one if the Addon has not been updated but the cache has.
mod_dir = os.path.join(self.mod_directory, self.name)
installed_metadata_path = os.path.join(mod_dir, "package.xml")
if os.path.isfile(installed_metadata_path):
try:
self.installed_metadata = MetadataReader.from_file(installed_metadata_path)
except xml.etree.ElementTree.ParseError:
fci.Console.PrintWarning(
"An invalid or corrupted package.xml file was found in installation of"
)
fci.Console.PrintWarning(f" {self.name}... ignoring the bad data.\n")
return
def set_metadata(self, metadata: Metadata) -> None:
"""Set the given metadata object as this object's metadata, updating the
object's display name and package type information to match, as well as
updating any dependency information, etc.
"""
self.metadata = metadata
self.display_name = metadata.name
self.repo_type = Addon.Kind.PACKAGE
self.description = metadata.description
for url in metadata.url:
if url.type == UrlType.repository:
self.url = url.location
self.branch = url.branch if url.branch else "master"
self._clean_url()
self.extract_tags(self.metadata)
self.extract_metadata_dependencies(self.metadata)
@staticmethod
def version_is_ok(metadata: Metadata) -> bool:
"""Checks to see if the current running version of FreeCAD meets the
requirements set by the passed-in metadata parameter."""
from_fci = list(fci.Version())
fc_version = Version(from_list=from_fci)
dep_fc_min = metadata.freecadmin if metadata.freecadmin else fc_version
dep_fc_max = metadata.freecadmax if metadata.freecadmax else fc_version
return dep_fc_min <= fc_version <= dep_fc_max
def extract_metadata_dependencies(self, metadata: Metadata):
"""Read dependency information from a metadata object and store it in this
Addon"""
# Version check: if this piece of metadata doesn't apply to this version of
# FreeCAD, just skip it.
if not Addon.version_is_ok(metadata):
return
if metadata.pythonmin:
self.python_min_version["major"] = metadata.pythonmin.version_as_list[0]
self.python_min_version["minor"] = metadata.pythonmin.version_as_list[1]
for dep in metadata.depend:
if dep.dependency_type == DependencyType.internal:
if dep.package in INTERNAL_WORKBENCHES:
self.requires.add(dep.package)
else:
fci.Console.PrintWarning(
translate(
"AddonsInstaller",
"{}: Unrecognized internal workbench '{}'",
).format(self.name, dep.package)
)
elif dep.dependency_type == DependencyType.addon:
self.requires.add(dep.package)
elif dep.dependency_type == DependencyType.python:
if dep.optional:
self.python_optional.add(dep.package)
else:
self.python_requires.add(dep.package)
else:
# Automatic resolution happens later, once we have a complete list of
# Addons
self.requires.add(dep.package)
for dep in metadata.conflict:
self.blocks.add(dep.package)
# Recurse
content = metadata.content
for _, value in content.items():
for item in value:
self.extract_metadata_dependencies(item)
def verify_url_and_branch(self, url: str, branch: str) -> None:
"""Print diagnostic information for Addon Developers if their metadata is
inconsistent with the actual fetch location. Most often this is due to using
the wrong branch name."""
if self.url != url:
fci.Console.PrintWarning(
translate(
"AddonsInstaller",
"Addon Developer Warning: Repository URL set in package.xml file for addon {} ({}) does not match the URL it was fetched from ({})",
).format(self.display_name, self.url, url)
+ "\n"
)
if self.branch != branch:
fci.Console.PrintWarning(
translate(
"AddonsInstaller",
"Addon Developer Warning: Repository branch set in package.xml file for addon {} ({}) does not match the branch it was fetched from ({})",
).format(self.display_name, self.branch, branch)
+ "\n"
)
def extract_tags(self, metadata: Metadata) -> None:
"""Read the tags from the metadata object"""
# Version check: if this piece of metadata doesn't apply to this version of
# FreeCAD, just skip it.
if not Addon.version_is_ok(metadata):
return
for new_tag in metadata.tag:
self.tags.add(new_tag)
content = metadata.content
for _, value in content.items():
for item in value:
self.extract_tags(item)
def contains_workbench(self) -> bool:
"""Determine if this package contains (or is) a workbench"""
if self.repo_type == Addon.Kind.WORKBENCH:
return True
return self.contains_packaged_content("workbench")
def contains_macro(self) -> bool:
"""Determine if this package contains (or is) a macro"""
if self.repo_type == Addon.Kind.MACRO:
return True
return self.contains_packaged_content("macro")
def contains_packaged_content(self, content_type: str):
"""Determine if the package contains content_type"""
if self.repo_type == Addon.Kind.PACKAGE:
if self.metadata is None:
fci.Console.PrintLog(
f"Addon Manager internal error: lost metadata for package {self.name}\n"
)
return False
content = self.metadata.content
return content_type in content
return False
def contains_preference_pack(self) -> bool:
"""Determine if this package contains a preference pack"""
return self.contains_packaged_content("preferencepack")
def contains_bundle(self) -> bool:
"""Determine if this package contains a bundle"""
return self.contains_packaged_content("bundle")
def contains_other(self) -> bool:
"""Determine if this package contains an "other" content item"""
return self.contains_packaged_content("other")
def get_best_icon_relative_path(self) -> str:
"""Get the path within the repo the addon's icon. Usually specified by
top-level metadata, but some authors omit it and specify only icons for the
contents. Find the first one of those, in such cases."""
if self.best_icon_relative_path:
return self.best_icon_relative_path
if not self.metadata:
return ""
real_icon = self.metadata.icon
if not real_icon:
# If there is no icon set for the entire package, see if there are any
# workbenches, which are required to have icons, and grab the first one
# we find:
content = self.metadata.content
if "workbench" in content:
wb = content["workbench"][0]
if wb.icon:
if wb.subdirectory:
subdir = wb.subdirectory
else:
subdir = wb.name
real_icon = subdir + wb.icon
self.best_icon_relative_path = real_icon
return self.best_icon_relative_path
def get_cached_icon_filename(self) -> str:
"""NOTE: This function is deprecated and will be removed in a coming update."""
if hasattr(self, "cached_icon_filename") and self.cached_icon_filename:
return self.cached_icon_filename
if not self.metadata:
return ""
real_icon = self.metadata.icon
if not real_icon:
# If there is no icon set for the entire package, see if there are any
# workbenches, which are required to have icons, and grab the first one
# we find:
content = self.metadata.content
if "workbench" in content:
wb = content["workbench"][0]
if wb.icon:
if wb.subdirectory:
subdir = wb.subdirectory
else:
subdir = wb.name
real_icon = subdir + wb.icon
real_icon = real_icon.replace(
"/", os.path.sep
) # Required path separator in the metadata.xml file to local separator
_, file_extension = os.path.splitext(real_icon)
store = os.path.join(self.cache_directory, "PackageMetadata")
self.cached_icon_filename = os.path.join(store, self.name, "cached_icon" + file_extension)
return self.cached_icon_filename
def walk_dependency_tree(self, all_repos, deps):
"""Compute the total dependency tree for this repo (recursive)
- all_repos is a dictionary of repos, keyed on the name of the repo
- deps is an Addon.Dependency object encapsulating all the types of dependency
information that may be needed.
"""
deps.python_requires |= self.python_requires
deps.python_optional |= self.python_optional
deps.python_min_version["major"] = max(
deps.python_min_version["major"], self.python_min_version["major"]
)
if deps.python_min_version["major"] == 3:
deps.python_min_version["minor"] = max(
deps.python_min_version["minor"], self.python_min_version["minor"]
)
else:
fci.Console.PrintWarning("Unrecognized Python version information")
for dep in self.requires:
if dep in all_repos:
if dep not in deps.required_external_addons:
deps.required_external_addons.append(all_repos[dep])
all_repos[dep].walk_dependency_tree(all_repos, deps)
else:
# See if this is an internal workbench:
if dep.upper().endswith("WB"):
real_name = dep[:-2].strip().lower()
elif dep.upper().endswith("WORKBENCH"):
real_name = dep[:-9].strip().lower()
else:
real_name = dep.strip().lower()
if real_name in INTERNAL_WORKBENCHES:
deps.internal_workbenches.add(INTERNAL_WORKBENCHES[real_name])
else:
# Assume it's a Python requirement of some kind:
deps.python_requires.add(dep)
for dep in self.blocks:
if dep in all_repos:
deps.blockers[dep] = all_repos[dep]
def status(self):
"""Threadsafe access to the current update status"""
with self.status_lock:
return self.update_status
def set_status(self, status):
"""Threadsafe setting of the update status"""
with self.status_lock:
self.update_status = status
def is_disabled(self):
"""Check to see if the disabling stopfile exists"""
stopfile = os.path.join(self.mod_directory, self.name, "ADDON_DISABLED")
return os.path.exists(stopfile)
def disable(self):
"""Disable this addon from loading when FreeCAD starts up by creating a
stopfile"""
stopfile = os.path.join(self.mod_directory, self.name, "ADDON_DISABLED")
with open(stopfile, "w", encoding="utf-8") as f:
f.write(
"The existence of this file prevents FreeCAD from loading this Addon. To re-enable, delete the file."
)
if self.contains_workbench():
self.disable_workbench()
def enable(self):
"""Re-enable loading this addon by deleting the stopfile"""
stopfile = os.path.join(self.mod_directory, self.name, "ADDON_DISABLED")
try:
os.unlink(stopfile)
except FileNotFoundError:
pass
if self.contains_workbench():
self.enable_workbench()
def enable_workbench(self):
wbName = self.get_workbench_name()
# Remove from the list of disabled.
self.remove_from_disabled_wbs(wbName)
def disable_workbench(self):
pref = fci.ParamGet("User parameter:BaseApp/Preferences/Workbenches")
wbName = self.get_workbench_name()
# Add the wb to the list of disabled if it was not already
disabled_wbs = pref.GetString("Disabled", "NoneWorkbench,TestWorkbench")
# print(f"start disabling {disabled_wbs}")
disabled_wbs_list = disabled_wbs.split(",")
if not (wbName in disabled_wbs_list):
disabled_wbs += "," + wbName
pref.SetString("Disabled", disabled_wbs)
# print(f"done disabling : {disabled_wbs} \n")
def desinstall_workbench(self):
pref = fci.ParamGet("User parameter:BaseApp/Preferences/Workbenches")
wbName = self.get_workbench_name()
# Remove from the list of ordered.
ordered_wbs = pref.GetString("Ordered", "")
# print(f"start remove from ordering {ordered_wbs}")
ordered_wbs_list = ordered_wbs.split(",")
ordered_wbs = ""
for wb in ordered_wbs_list:
if wb != wbName:
if ordered_wbs != "":
ordered_wbs += ","
ordered_wbs += wb
pref.SetString("Ordered", ordered_wbs)
# print(f"end remove from ordering {ordered_wbs}")
# Remove from the list of disabled.
self.remove_from_disabled_wbs(wbName)
def remove_from_disabled_wbs(self, wbName: str):
pref = fci.ParamGet("User parameter:BaseApp/Preferences/Workbenches")
disabled_wbs = pref.GetString("Disabled", "NoneWorkbench,TestWorkbench")
# print(f"start enabling : {disabled_wbs}")
disabled_wbs_list = disabled_wbs.split(",")
disabled_wbs = ""
for wb in disabled_wbs_list:
if wb != wbName:
if disabled_wbs != "":
disabled_wbs += ","
disabled_wbs += wb
pref.SetString("Disabled", disabled_wbs)
# print(f"Done enabling {disabled_wbs} \n")
def get_workbench_name(self) -> str:
"""Find the name of the workbench class (ie the name under which it's
registered in freecad core)'"""
wb_name = ""
if self.repo_type == Addon.Kind.PACKAGE:
for wb in self.metadata.content["workbench"]: # we may have more than one wb.
if wb_name != "":
wb_name += ","
wb_name += wb.classname
if self.repo_type == Addon.Kind.WORKBENCH or wb_name == "":
wb_name = self.try_find_wbname_in_files()
if wb_name == "":
wb_name = self.name
return wb_name
def try_find_wbname_in_files(self) -> str:
"""Attempt to locate a line with an addWorkbench command in the workbench's
Python files. If it is directly instantiating a workbench, then we can use
the line to determine classname for this workbench. If it uses a variable,
or if the line doesn't exist at all, an empty string is returned."""
mod_dir = os.path.join(self.mod_directory, self.name)
for root, _, files in os.walk(mod_dir):
for f in files:
current_file = os.path.join(root, f)
if not os.path.isdir(current_file):
filename, extension = os.path.splitext(current_file)
if extension == ".py":
wb_classname = self._find_classname_in_file(current_file)
if wb_classname:
return wb_classname
return ""
@staticmethod
def _find_classname_in_file(current_file) -> str:
try:
with open(current_file, "r", encoding="utf-8") as python_file:
content = python_file.read()
search_result = re.search(r"Gui.addWorkbench\s*\(\s*(\w+)\s*\(\s*\)\s*\)", content)
if search_result:
return search_result.group(1)
except OSError:
pass
return ""
# @dataclass(frozen)
class MissingDependencies:
"""Encapsulates a group of four types of dependencies:
* Internal workbenches -> wbs
* External addons -> external_addons
* Required Python packages -> python_requires
* Optional Python packages -> python_optional
"""
def __init__(self, repo: Addon, all_repos: List[Addon]):
deps = Addon.Dependencies()
repo_name_dict = {}
for r in all_repos:
repo_name_dict[r.name] = r
if hasattr(r, "display_name"):
# Test harness might not provide a display name
repo_name_dict[r.display_name] = r
if hasattr(repo, "walk_dependency_tree"):
# Sometimes the test harness doesn't provide this function, to override
# any dependency checking
repo.walk_dependency_tree(repo_name_dict, deps)
self.external_addons = []
for dep in deps.required_external_addons:
if dep.status() == Addon.Status.NOT_INSTALLED:
self.external_addons.append(dep.name)
# Now check the loaded addons to see if we are missing an internal workbench:
if fci.FreeCADGui:
wbs = [wb.lower() for wb in fci.FreeCADGui.listWorkbenches()]
else:
wbs = []
self.wbs = []
for dep in deps.internal_workbenches:
if dep.lower() + "workbench" not in wbs:
if dep.lower() == "plot":
# Special case for plot, which is no longer a full workbench:
try:
__import__("Plot")
except ImportError:
# Plot might fail for a number of reasons
self.wbs.append(dep)
fci.Console.PrintLog("Failed to import Plot module")
else:
self.wbs.append(dep)
# Check the Python dependencies:
self.python_min_version = deps.python_min_version
self.python_requires = []
for py_dep in deps.python_requires:
if py_dep not in self.python_requires:
try:
__import__(py_dep)
except ImportError:
self.python_requires.append(py_dep)
except (OSError, NameError, TypeError, RuntimeError) as e:
fci.Console.PrintWarning(
translate(
"AddonsInstaller",
"Got an error when trying to import {}",
).format(py_dep)
+ ":\n"
+ str(e)
)
self.python_optional = []
for py_dep in deps.python_optional:
try:
__import__(py_dep)
except ImportError:
self.python_optional.append(py_dep)
except (OSError, NameError, TypeError, RuntimeError) as e:
fci.Console.PrintWarning(
translate(
"AddonsInstaller",
"Got an error when trying to import {}",
).format(py_dep)
+ ":\n"
+ str(e)
)
self.wbs.sort()
self.external_addons.sort()
self.python_requires.sort()
self.python_optional.sort()
self.python_optional = [
option for option in self.python_optional if option not in self.python_requires
]
-143
View File
@@ -1,143 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2025 The FreeCAD project association AISBL *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD 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 *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
"""The Addon Catalog is the main list of all Addons along with their various
sources and compatible versions. Added in FreeCAD 1.1 to replace .gitmodules."""
from dataclasses import dataclass
from hashlib import sha256
from typing import Any, Dict, List, Optional, Tuple
from addonmanager_metadata import Version
from Addon import Addon
import addonmanager_freecad_interface as fci
@dataclass
class AddonCatalogEntry:
"""Each individual entry in the catalog, storing data about a particular version of an
Addon. Note that this class needs to be identical to the one that is used in the remote cache
generation, so don't make changes here without ensuring that the classes are synchronized."""
freecad_min: Optional[Version] = None
freecad_max: Optional[Version] = None
repository: Optional[str] = None
git_ref: Optional[str] = None
zip_url: Optional[str] = None
note: Optional[str] = None
branch_display_name: Optional[str] = None
def __init__(self, raw_data: Dict[str, str]) -> None:
"""Create an AddonDictionaryEntry from the raw JSON data"""
super().__init__()
for key, value in raw_data.items():
if hasattr(self, key):
if key in ("freecad_min", "freecad_max"):
value = Version(from_string=value)
setattr(self, key, value)
def is_compatible(self) -> bool:
"""Check whether this AddonCatalogEntry is compatible with the current version of FreeCAD"""
if self.freecad_min is None and self.freecad_max is None:
return True
current_version = Version(from_list=fci.Version())
if self.freecad_min is None:
return current_version <= self.freecad_max
if self.freecad_max is None:
return current_version >= self.freecad_min
return self.freecad_min <= current_version <= self.freecad_max
def unique_identifier(self) -> str:
"""Return a unique identifier of the AddonCatalogEntry, guaranteed to be repeatable: when
given the same basic information, the same ID is created. Used as the key when storing
the metadata for a given AddonCatalogEntry."""
sha256_hash = sha256()
sha256_hash.update(str(self).encode("utf-8"))
return sha256_hash.hexdigest()
class AddonCatalog:
"""A catalog of addons grouped together into sets representing versions that are
compatible with different versions of FreeCAD and/or represent different available branches
of a given addon (e.g. a Development branch that users are presented)."""
def __init__(self, data: Dict[str, Any]):
self._original_data = data
self._dictionary = {}
self._parse_raw_data()
def _parse_raw_data(self):
self._dictionary = {} # Clear pre-existing contents
for key, value in self._original_data.items():
if key == "_meta": # Don't add the documentation object to the tree
continue
self._dictionary[key] = []
for entry in value:
self._dictionary[key].append(AddonCatalogEntry(entry))
def load_metadata_cache(self, cache: Dict[str, Any]):
"""Given the raw dictionary, couple that with the remote metadata cache to create the
final working addon dictionary. Only create Addons that are compatible with the current
version of FreeCAD."""
for value in self._dictionary.values():
for entry in value:
sha256_hash = entry.unique_identifier()
print(sha256_hash)
if sha256_hash in cache and entry.is_compatible():
entry.addon = Addon.from_cache(cache[sha256_hash])
def get_available_addon_ids(self) -> List[str]:
"""Get a list of IDs that have at least one entry compatible with the current version of
FreeCAD"""
id_list = []
for key, value in self._dictionary.items():
for entry in value:
if entry.is_compatible():
id_list.append(key)
break
return id_list
def get_available_branches(self, addon_id: str) -> List[Tuple[str, str]]:
"""For a given ID, get the list of available branches compatible with this version of
FreeCAD along with the branch display name. Either field may be empty, but not both. The
first entry in the list is expected to be the "primary"."""
if addon_id not in self._dictionary:
return []
result = []
for entry in self._dictionary[addon_id]:
if entry.is_compatible():
result.append((entry.git_ref, entry.branch_display_name))
return result
def get_addon_from_id(self, addon_id: str, branch: Optional[Tuple[str, str]] = None) -> Addon:
"""Get the instantiated Addon object for the given ID and optionally branch. If no
branch is provided, whichever branch is the "primary" branch will be returned (i.e. the
first branch that matches). Raises a ValueError if no addon matches the request."""
if addon_id not in self._dictionary:
raise ValueError(f"Addon '{addon_id}' not found")
for entry in self._dictionary[addon_id]:
if not entry.is_compatible():
continue
if not branch or entry.branch_display_name == branch:
return entry.addon
raise ValueError(f"Addon '{addon_id}' has no compatible branches named '{branch}'")
-951
View File
@@ -1,951 +0,0 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2022-2023 FreeCAD Project Association *
# * Copyright (c) 2015 Yorik van Havre <[email protected]> *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD 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 *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
import os
import functools
import tempfile
import threading
import json
from datetime import date
from typing import Dict
from PySide import QtGui, QtCore, QtWidgets
import FreeCAD
import FreeCADGui
from addonmanager_workers_startup import (
CreateAddonListWorker,
LoadPackagesFromCacheWorker,
LoadMacrosFromCacheWorker,
CheckWorkbenchesForUpdatesWorker,
CacheMacroCodeWorker,
GetBasicAddonStatsWorker,
GetAddonScoreWorker,
)
from addonmanager_workers_installation import (
UpdateMetadataCacheWorker,
)
from addonmanager_installer_gui import AddonInstallerGUI, MacroInstallerGUI
from addonmanager_uninstaller_gui import AddonUninstallerGUI
from addonmanager_update_all_gui import UpdateAllGUI
import addonmanager_utilities as utils
import addonmanager_freecad_interface as fci
import AddonManager_rc # pylint: disable=unused-import
from composite_view import CompositeView
from Widgets.addonmanager_widget_global_buttons import WidgetGlobalButtonBar
from Widgets.addonmanager_widget_progress_bar import Progress
from package_list import PackageListItemModel
from Addon import Addon
from addonmanager_python_deps_gui import (
PythonPackageManager,
)
from addonmanager_cache import local_cache_needs_update
from addonmanager_devmode import DeveloperMode
from addonmanager_firstrun import FirstRunDialog
from addonmanager_connection_checker import ConnectionCheckerGUI
from addonmanager_devmode_metadata_checker import MetadataValidators
import NetworkManager
from AddonManagerOptions import AddonManagerOptions
translate = FreeCAD.Qt.translate
def QT_TRANSLATE_NOOP(_, txt):
return txt
__title__ = "FreeCAD Addon Manager Module"
__author__ = "Yorik van Havre", "Jonathan Wiedemann", "Kurt Kremitzki", "Chris Hennes"
__url__ = "https://www.freecad.org"
"""
FreeCAD Addon Manager Module
Fetches various types of addons from a variety of sources. Built-in sources are:
* https://github.com/FreeCAD/FreeCAD-addons
* https://github.com/FreeCAD/FreeCAD-macros
* https://wiki.freecad.org/
Additional git sources may be configure via user preferences.
You need a working internet connection, and optionally git -- if git is not available, ZIP archives
are downloaded instead.
"""
# \defgroup ADDONMANAGER AddonManager
# \ingroup ADDONMANAGER
# \brief The Addon Manager allows users to install workbenches and macros made by other users
# @{
INSTANCE = None
def get_icon(repo: Addon, update: bool = False) -> QtGui.QIcon:
"""Returns an icon for an Addon. Uses a cached icon if possible, unless update is True,
in which case the icon is regenerated."""
if not update and repo.icon and not repo.icon.isNull() and repo.icon.isValid():
return repo.icon
path = ":/icons/" + repo.name.replace(" ", "_")
default_icon = QtGui.QIcon(":/icons/document-package.svg")
if repo.repo_type == Addon.Kind.WORKBENCH:
path += "_workbench_icon.svg"
default_icon = QtGui.QIcon(":/icons/document-package.svg")
elif repo.repo_type == Addon.Kind.MACRO:
if repo.macro and repo.macro.icon:
if os.path.isabs(repo.macro.icon):
path = repo.macro.icon
default_icon = QtGui.QIcon(":/icons/document-python.svg")
else:
path = os.path.join(os.path.dirname(repo.macro.src_filename), repo.macro.icon)
default_icon = QtGui.QIcon(":/icons/document-python.svg")
elif repo.macro and repo.macro.xpm:
cache_path = FreeCAD.getUserCachePath()
am_path = os.path.join(cache_path, "AddonManager", "MacroIcons")
os.makedirs(am_path, exist_ok=True)
path = os.path.join(am_path, repo.name + "_icon.xpm")
if not os.path.exists(path):
with open(path, "w") as f:
f.write(repo.macro.xpm)
default_icon = QtGui.QIcon(repo.macro.xpm)
else:
path += "_macro_icon.svg"
default_icon = QtGui.QIcon(":/icons/document-python.svg")
elif repo.repo_type == Addon.Kind.PACKAGE:
# The cache might not have been downloaded yet, check to see if it's there...
if os.path.isfile(repo.get_cached_icon_filename()):
path = repo.get_cached_icon_filename()
elif repo.contains_workbench():
path += "_workbench_icon.svg"
default_icon = QtGui.QIcon(":/icons/document-package.svg")
elif repo.contains_macro():
path += "_macro_icon.svg"
default_icon = QtGui.QIcon(":/icons/document-python.svg")
else:
default_icon = QtGui.QIcon(":/icons/document-package.svg")
if QtCore.QFile.exists(path):
addon_icon = QtGui.QIcon(path)
else:
addon_icon = default_icon
repo.icon = addon_icon
return addon_icon
class CommandAddonManager(QtCore.QObject):
"""The main Addon Manager class and FreeCAD command"""
workers = [
"create_addon_list_worker",
"check_worker",
"show_worker",
"showmacro_worker",
"macro_worker",
"update_metadata_cache_worker",
"load_macro_metadata_worker",
"update_all_worker",
"check_for_python_package_updates_worker",
"get_basic_addon_stats_worker",
"get_addon_score_worker",
]
lock = threading.Lock()
restart_required = False
finished = QtCore.Signal()
def __init__(self):
super().__init__()
QT_TRANSLATE_NOOP("QObject", "Addon Manager")
FreeCADGui.addPreferencePage(
AddonManagerOptions,
"Addon Manager",
)
self.item_model = None
self.developer_mode = None
self.installer_gui = None
self.composite_view = None
self.button_bar = None
self.update_cache = False
self.dialog = None
self.startup_sequence = []
self.packages_with_updates = set()
self.macro_repo_dir = None
self.number_of_progress_regions = 0
self.current_progress_region = 0
self.check_worker = None
self.check_for_python_package_updates_worker = None
self.update_all_worker = None
self.update_metadata_cache_worker = None
self.macro_worker = None
self.create_addon_list_worker = None
self.get_addon_score_worker = None
self.get_basic_addon_stats_worker = None
self.load_macro_metadata_worker = None
self.macro_cache = []
self.package_cache = {}
self.manage_python_packages_dialog = None
# Set up the connection checker
self.connection_checker = ConnectionCheckerGUI()
self.connection_checker.connection_available.connect(self.launch)
# Give other parts of the AM access to the current instance
global INSTANCE
INSTANCE = self
def GetResources(self) -> Dict[str, str]:
"""FreeCAD-required function: get the core resource information for this Mod."""
return {
"Pixmap": "AddonManager",
"MenuText": QT_TRANSLATE_NOOP("Std_AddonMgr", "&Addon manager"),
"ToolTip": QT_TRANSLATE_NOOP(
"Std_AddonMgr",
"Manage external workbenches, macros, and preference packs",
),
"Group": "Tools",
}
def Activated(self) -> None:
"""FreeCAD-required function: called when the command is activated."""
NetworkManager.InitializeNetworkManager()
first_run_dialog = FirstRunDialog()
if not first_run_dialog.exec():
return
self.connection_checker.start()
def launch(self) -> None:
"""Shows the Addon Manager UI"""
# create the dialog
self.dialog = FreeCADGui.PySideUic.loadUi(
os.path.join(os.path.dirname(__file__), "AddonManager.ui")
)
self.dialog.setObjectName("AddonManager_Main_Window")
# self.dialog.setWindowFlag(QtCore.Qt.WindowStaysOnTopHint, True)
# cleanup the leftovers from previous runs
self.macro_repo_dir = FreeCAD.getUserMacroDir(True)
self.packages_with_updates = set()
self.startup_sequence = []
self.cleanup_workers()
self.update_cache = local_cache_needs_update()
# restore window geometry from stored state
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
w = pref.GetInt("WindowWidth", 800)
h = pref.GetInt("WindowHeight", 600)
self.composite_view = CompositeView(self.dialog)
self.button_bar = WidgetGlobalButtonBar(self.dialog)
# If we are checking for updates automatically, hide the Check for updates button:
autocheck = pref.GetBool("AutoCheck", True)
if autocheck:
self.button_bar.check_for_updates.hide()
else:
self.button_bar.update_all_addons.hide()
# Set up the listing of packages using the model-view-controller architecture
self.item_model = PackageListItemModel()
self.composite_view.setModel(self.item_model)
self.dialog.layout().addWidget(self.composite_view)
self.dialog.layout().addWidget(self.button_bar)
# set nice icons to everything, by theme with fallback to FreeCAD icons
self.dialog.setWindowIcon(QtGui.QIcon(":/icons/AddonManager.svg"))
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
dev_mode_active = pref.GetBool("developerMode", False)
# enable/disable stuff
self.button_bar.update_all_addons.setEnabled(False)
self.hide_progress_widgets()
self.button_bar.refresh_local_cache.setEnabled(False)
self.button_bar.refresh_local_cache.setText(translate("AddonsInstaller", "Starting up..."))
if dev_mode_active:
self.button_bar.developer_tools.show()
else:
self.button_bar.developer_tools.hide()
# connect slots
self.dialog.rejected.connect(self.reject)
self.dialog.accepted.connect(self.accept)
self.button_bar.update_all_addons.clicked.connect(self.update_all)
self.button_bar.close.clicked.connect(self.dialog.reject)
self.button_bar.refresh_local_cache.clicked.connect(self.on_button_update_cache_clicked)
self.button_bar.check_for_updates.clicked.connect(
lambda: self.force_check_updates(standalone=True)
)
self.button_bar.python_dependencies.clicked.connect(self.show_python_updates_dialog)
self.button_bar.developer_tools.clicked.connect(self.show_developer_tools)
self.composite_view.package_list.stop_loading.connect(self.stop_update)
self.composite_view.package_list.setEnabled(False)
self.composite_view.execute.connect(self.execute_macro)
self.composite_view.install.connect(self.launch_installer_gui)
self.composite_view.uninstall.connect(self.remove)
self.composite_view.update.connect(self.update)
self.composite_view.update_status.connect(self.status_updated)
# center the dialog over the FreeCAD window
self.dialog.resize(w, h)
mw = FreeCADGui.getMainWindow()
self.dialog.move(
mw.frameGeometry().topLeft() + mw.rect().center() - self.dialog.rect().center()
)
# begin populating the table in a set of sub-threads
self.startup()
# rock 'n roll!!!
self.dialog.exec()
def cleanup_workers(self) -> None:
"""Ensure that no workers are running by explicitly asking them to stop and waiting for
them until they do"""
for worker in self.workers:
if hasattr(self, worker):
thread = getattr(self, worker)
if thread:
if not thread.isFinished():
thread.blockSignals(True)
thread.requestInterruption()
for worker in self.workers:
if hasattr(self, worker):
thread = getattr(self, worker)
if thread:
if not thread.isFinished():
finished = thread.wait(500)
if not finished:
FreeCAD.Console.PrintWarning(
translate(
"AddonsInstaller",
"Worker process {} is taking a long time to stop...",
).format(worker)
+ "\n"
)
def accept(self) -> None:
self.finished.emit()
def reject(self) -> None:
"""called when the window has been closed"""
# save window geometry for next use
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
pref.SetInt("WindowWidth", self.dialog.width())
pref.SetInt("WindowHeight", self.dialog.height())
# ensure all threads are finished before closing
ok_to_close = True
worker_killed = False
self.startup_sequence = []
for worker in self.workers:
if hasattr(self, worker):
thread = getattr(self, worker)
if thread:
if not thread.isFinished():
thread.blockSignals(True)
thread.requestInterruption()
worker_killed = True
ok_to_close = False
while not ok_to_close:
ok_to_close = True
for worker in self.workers:
if hasattr(self, worker):
thread = getattr(self, worker)
if thread:
thread.wait(25)
if not thread.isFinished():
ok_to_close = False
QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents)
# Write the cache data if it's safe to do so:
if not worker_killed:
for repo in self.item_model.repos:
if repo.repo_type == Addon.Kind.MACRO:
self.cache_macro(repo)
else:
self.cache_package(repo)
self.write_package_cache()
self.write_macro_cache()
else:
self.write_cache_stopfile()
FreeCAD.Console.PrintLog(
"Not writing the cache because a process was forcibly terminated and the state is "
"unknown.\n"
)
if self.restart_required:
# display restart dialog
m = QtWidgets.QMessageBox()
m.setWindowTitle(translate("AddonsInstaller", "Addon manager"))
m.setWindowIcon(QtGui.QIcon(":/icons/AddonManager.svg"))
m.setText(
translate(
"AddonsInstaller",
"You must restart FreeCAD for changes to take effect.",
)
)
m.setIcon(QtWidgets.QMessageBox.Icon.Warning)
m.setStandardButtons(
QtWidgets.QMessageBox.StandardButton.Ok
| QtWidgets.QMessageBox.StandardButton.Cancel
)
m.setDefaultButton(QtWidgets.QMessageBox.StandardButton.Cancel)
ok_btn = m.button(QtWidgets.QMessageBox.StandardButton.Ok)
cancel_btn = m.button(QtWidgets.QMessageBox.StandardButton.Cancel)
ok_btn.setText(translate("AddonsInstaller", "Restart now"))
cancel_btn.setText(translate("AddonsInstaller", "Restart later"))
ret = m.exec_()
if ret == QtWidgets.QMessageBox.StandardButton.Ok:
# restart FreeCAD after a delay to give time to this dialog to close
QtCore.QTimer.singleShot(1000, utils.restart_freecad)
self.finished.emit()
def startup(self) -> None:
"""Downloads the available packages listings and populates the table
This proceeds in four stages: first, the main GitHub repository is queried for a list of
possible addons. Each addon is specified as a git submodule with name and branch
information. The actual specific commit ID of the submodule (as listed on GitHub) is
ignored. Any extra repositories specified by the user are appended to this list.
Second, the list of macros is downloaded from the FreeCAD/FreeCAD-macros repository and
the wiki.
Third, each of these items is queried for a package.xml metadata file. If that file exists
it is downloaded, cached, and any icons that it references are also downloaded and cached.
Finally, for workbenches that are not contained within a package (e.g. they provide no
metadata), an additional git query is made to see if an update is available. Macros are
checked for file changes.
Each of these stages is launched in a separate thread to ensure that the UI remains
responsive, and the operation can be cancelled.
Each stage is also subject to caching, so may return immediately, if no cache update has
been requested."""
# Each function in this list is expected to launch a thread and connect its completion
# signal to self.do_next_startup_phase, or to shortcut to calling
# self.do_next_startup_phase if it is not launching a worker
self.startup_sequence = [
self.populate_packages_table,
self.activate_table_widgets,
self.populate_macros,
self.update_metadata_cache,
self.check_updates,
self.check_python_updates,
self.fetch_addon_stats,
self.fetch_addon_score,
self.select_addon,
]
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
if pref.GetBool("DownloadMacros", True):
self.startup_sequence.append(self.load_macro_metadata)
self.number_of_progress_regions = len(self.startup_sequence)
self.current_progress_region = 0
self.do_next_startup_phase()
def do_next_startup_phase(self) -> None:
"""Pop the top item in self.startup_sequence off the list and run it"""
if len(self.startup_sequence) > 0:
phase_runner = self.startup_sequence.pop(0)
self.current_progress_region += 1
phase_runner()
else:
self.hide_progress_widgets()
self.update_cache = False
self.button_bar.refresh_local_cache.setEnabled(True)
self.button_bar.refresh_local_cache.setText(
translate("AddonsInstaller", "Refresh local cache")
)
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
pref.SetString("LastCacheUpdate", date.today().isoformat())
self.composite_view.package_list.item_filter.invalidateFilter()
def populate_packages_table(self) -> None:
self.item_model.clear()
use_cache = not self.update_cache
if use_cache:
if os.path.isfile(utils.get_cache_file_name("package_cache.json")):
with open(utils.get_cache_file_name("package_cache.json"), encoding="utf-8") as f:
data = f.read()
try:
from_json = json.loads(data)
if len(from_json) == 0:
use_cache = False
except json.JSONDecodeError:
use_cache = False
else:
use_cache = False
if not use_cache:
self.update_cache = True # Make sure to trigger the other cache updates, if the json
# file was missing
self.create_addon_list_worker = CreateAddonListWorker()
self.create_addon_list_worker.addon_repo.connect(self.add_addon_repo)
self.update_progress_bar(translate("AddonsInstaller", "Creating addon list"), 10, 100)
self.create_addon_list_worker.finished.connect(
self.do_next_startup_phase
) # Link to step 2
self.create_addon_list_worker.start()
else:
self.create_addon_list_worker = LoadPackagesFromCacheWorker(
utils.get_cache_file_name("package_cache.json")
)
self.create_addon_list_worker.addon_repo.connect(self.add_addon_repo)
self.update_progress_bar(translate("AddonsInstaller", "Loading addon list"), 10, 100)
self.create_addon_list_worker.finished.connect(
self.do_next_startup_phase
) # Link to step 2
self.create_addon_list_worker.start()
def cache_package(self, repo: Addon):
if not hasattr(self, "package_cache"):
self.package_cache = {}
self.package_cache[repo.name] = repo.to_cache()
def write_package_cache(self):
if hasattr(self, "package_cache"):
package_cache_path = utils.get_cache_file_name("package_cache.json")
with open(package_cache_path, "w", encoding="utf-8") as f:
f.write(json.dumps(self.package_cache, indent=" "))
def activate_table_widgets(self) -> None:
self.composite_view.package_list.setEnabled(True)
self.composite_view.package_list.ui.view_bar.search.setFocus()
self.do_next_startup_phase()
def populate_macros(self) -> None:
macro_cache_file = utils.get_cache_file_name("macro_cache.json")
cache_is_bad = True
if os.path.isfile(macro_cache_file):
size = os.path.getsize(macro_cache_file)
if size > 1000: # Make sure there is actually data in there
cache_is_bad = False
if cache_is_bad:
if not self.update_cache:
self.update_cache = True # Make sure to trigger the other cache updates, if the
# json file was missing
self.create_addon_list_worker = CreateAddonListWorker()
self.create_addon_list_worker.addon_repo.connect(self.add_addon_repo)
self.update_progress_bar(
translate("AddonsInstaller", "Creating macro list"), 10, 100
)
self.create_addon_list_worker.finished.connect(
self.do_next_startup_phase
) # Link to step 2
self.create_addon_list_worker.start()
else:
# It's already been done in the previous step (TODO: Refactor to eliminate this
# step)
self.do_next_startup_phase()
else:
self.macro_worker = LoadMacrosFromCacheWorker(
utils.get_cache_file_name("macro_cache.json")
)
self.macro_worker.add_macro_signal.connect(self.add_addon_repo)
self.macro_worker.finished.connect(self.do_next_startup_phase)
self.macro_worker.start()
def cache_macro(self, repo: Addon):
if not hasattr(self, "macro_cache"):
self.macro_cache = []
if repo.macro is not None:
self.macro_cache.append(repo.macro.to_cache())
else:
FreeCAD.Console.PrintError(
f"Addon Manager: Internal error, cache_macro called on non-macro {repo.name}\n"
)
def write_macro_cache(self):
if not hasattr(self, "macro_cache"):
return
macro_cache_path = utils.get_cache_file_name("macro_cache.json")
with open(macro_cache_path, "w", encoding="utf-8") as f:
f.write(json.dumps(self.macro_cache, indent=" "))
self.macro_cache = []
def update_metadata_cache(self) -> None:
if self.update_cache:
self.update_metadata_cache_worker = UpdateMetadataCacheWorker(self.item_model.repos)
self.update_metadata_cache_worker.finished.connect(
self.do_next_startup_phase
) # Link to step 4
self.update_metadata_cache_worker.progress_made.connect(self.update_progress_bar)
self.update_metadata_cache_worker.package_updated.connect(self.on_package_updated)
self.update_metadata_cache_worker.start()
else:
self.do_next_startup_phase()
def on_button_update_cache_clicked(self) -> None:
self.update_cache = True
cache_path = FreeCAD.getUserCachePath()
am_path = os.path.join(cache_path, "AddonManager")
utils.rmdir(am_path)
self.button_bar.refresh_local_cache.setEnabled(False)
self.button_bar.refresh_local_cache.setText(
translate("AddonsInstaller", "Updating cache...")
)
self.startup()
# Re-caching implies checking for updates, regardless of the user's autocheck option
if self.check_updates in self.startup_sequence:
self.startup_sequence.remove(self.check_updates)
self.startup_sequence.append(self.force_check_updates)
def on_package_updated(self, repo: Addon) -> None:
"""Called when the named package has either new metadata or a new icon (or both)"""
with self.lock:
repo.icon = get_icon(repo, update=True)
self.item_model.reload_item(repo)
def load_macro_metadata(self) -> None:
if self.update_cache:
self.load_macro_metadata_worker = CacheMacroCodeWorker(self.item_model.repos)
self.load_macro_metadata_worker.update_macro.connect(self.on_package_updated)
self.load_macro_metadata_worker.progress_made.connect(self.update_progress_bar)
self.load_macro_metadata_worker.finished.connect(self.do_next_startup_phase)
self.load_macro_metadata_worker.start()
else:
self.do_next_startup_phase()
def select_addon(self) -> None:
prefs = fci.Preferences()
selection = prefs.get("SelectedAddon")
if selection:
self.composite_view.package_list.select_addon(selection)
prefs.set("SelectedAddon", "")
self.do_next_startup_phase()
def check_updates(self) -> None:
"""checks every installed addon for available updates"""
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
autocheck = pref.GetBool("AutoCheck", True)
if not autocheck:
FreeCAD.Console.PrintLog(
"Addon Manager: Skipping update check because AutoCheck user preference is False\n"
)
self.do_next_startup_phase()
return
if not self.packages_with_updates:
self.force_check_updates(standalone=False)
else:
self.do_next_startup_phase()
def force_check_updates(self, standalone=False) -> None:
if hasattr(self, "check_worker"):
thread = self.check_worker
if thread:
if not thread.isFinished():
self.do_next_startup_phase()
return
self.button_bar.update_all_addons.setText(
translate("AddonsInstaller", "Checking for updates...")
)
self.packages_with_updates.clear()
self.button_bar.update_all_addons.show()
self.button_bar.check_for_updates.setDisabled(True)
self.check_worker = CheckWorkbenchesForUpdatesWorker(self.item_model.repos)
self.check_worker.finished.connect(self.do_next_startup_phase)
self.check_worker.finished.connect(self.update_check_complete)
self.check_worker.progress_made.connect(self.update_progress_bar)
if standalone:
self.current_progress_region = 1
self.number_of_progress_regions = 1
self.check_worker.update_status.connect(self.status_updated)
self.check_worker.start()
self.enable_updates(len(self.packages_with_updates))
def status_updated(self, repo: Addon) -> None:
self.item_model.reload_item(repo)
if repo.status() == Addon.Status.UPDATE_AVAILABLE:
self.packages_with_updates.add(repo)
self.enable_updates(len(self.packages_with_updates))
elif repo.status() == Addon.Status.PENDING_RESTART:
self.restart_required = True
def enable_updates(self, number_of_updates: int) -> None:
"""enables the update button"""
if number_of_updates:
self.button_bar.set_number_of_available_updates(number_of_updates)
elif (
hasattr(self, "check_worker")
and self.check_worker is not None
and self.check_worker.isRunning()
):
self.button_bar.update_all_addons.setText(
translate("AddonsInstaller", "Checking for updates...")
)
else:
self.button_bar.set_number_of_available_updates(0)
def update_check_complete(self) -> None:
self.enable_updates(len(self.packages_with_updates))
self.button_bar.check_for_updates.setEnabled(True)
def check_python_updates(self) -> None:
PythonPackageManager.migrate_old_am_installations() # Migrate 0.20 to 0.21
self.do_next_startup_phase()
def show_python_updates_dialog(self) -> None:
if not self.manage_python_packages_dialog:
self.manage_python_packages_dialog = PythonPackageManager(self.item_model.repos)
self.manage_python_packages_dialog.show()
def fetch_addon_stats(self) -> None:
"""Fetch the Addon Stats JSON data from a URL"""
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
url = pref.GetString("AddonsStatsURL", "https://freecad.org/addon_stats.json")
if url and url != "NONE":
self.get_basic_addon_stats_worker = GetBasicAddonStatsWorker(
url, self.item_model.repos, self.dialog
)
self.get_basic_addon_stats_worker.finished.connect(self.do_next_startup_phase)
self.get_basic_addon_stats_worker.update_addon_stats.connect(self.update_addon_stats)
self.get_basic_addon_stats_worker.start()
else:
self.do_next_startup_phase()
def update_addon_stats(self, addon: Addon):
self.item_model.reload_item(addon)
def fetch_addon_score(self) -> None:
"""Fetch the Addon score JSON data from a URL"""
prefs = fci.Preferences()
url = prefs.get("AddonsScoreURL")
if url and url != "NONE":
self.get_addon_score_worker = GetAddonScoreWorker(
url, self.item_model.repos, self.dialog
)
self.get_addon_score_worker.finished.connect(self.score_fetched_successfully)
self.get_addon_score_worker.finished.connect(self.do_next_startup_phase)
self.get_addon_score_worker.update_addon_score.connect(self.update_addon_score)
self.get_addon_score_worker.start()
else:
self.composite_view.package_list.ui.view_bar.set_rankings_available(False)
self.do_next_startup_phase()
def update_addon_score(self, addon: Addon):
self.item_model.reload_item(addon)
def score_fetched_successfully(self):
self.composite_view.package_list.ui.view_bar.set_rankings_available(True)
def show_developer_tools(self) -> None:
"""Display the developer tools dialog"""
if not self.developer_mode:
self.developer_mode = DeveloperMode()
self.developer_mode.show()
checker = MetadataValidators()
checker.validate_all(self.item_model.repos)
def add_addon_repo(self, addon_repo: Addon) -> None:
"""adds a workbench to the list"""
if addon_repo.icon is None or addon_repo.icon.isNull():
addon_repo.icon = get_icon(addon_repo)
for repo in self.item_model.repos:
if repo.name == addon_repo.name:
# self.item_model.reload_item(repo) # If we want to have later additions superseded
# earlier
return
self.item_model.append_item(addon_repo)
def append_to_repos_list(self, repo: Addon) -> None:
"""this function allows threads to update the main list of workbenches"""
self.item_model.append_item(repo)
def update(self, repo: Addon) -> None:
self.launch_installer_gui(repo)
def mark_repo_update_available(self, repo: Addon, available: bool) -> None:
if available:
repo.set_status(Addon.Status.UPDATE_AVAILABLE)
else:
repo.set_status(Addon.Status.NO_UPDATE_AVAILABLE)
self.item_model.reload_item(repo)
self.composite_view.package_details_controller.show_repo(repo)
def launch_installer_gui(self, addon: Addon) -> None:
if self.installer_gui is not None:
FreeCAD.Console.PrintError(
translate(
"AddonsInstaller",
"Cannot launch a new installer until the previous one has finished.",
)
)
return
if addon.macro is not None:
self.installer_gui = MacroInstallerGUI(addon)
else:
self.installer_gui = AddonInstallerGUI(addon, self.item_model.repos)
self.installer_gui.success.connect(self.on_package_status_changed)
self.installer_gui.finished.connect(self.cleanup_installer)
self.installer_gui.run() # Does not block
def cleanup_installer(self) -> None:
QtCore.QTimer.singleShot(500, self.no_really_clean_up_the_installer)
def no_really_clean_up_the_installer(self) -> None:
self.installer_gui = None
def update_all(self) -> None:
"""Asynchronously apply all available updates: individual failures are noted, but do not
stop other updates"""
if self.installer_gui is not None:
FreeCAD.Console.PrintError(
translate(
"AddonsInstaller",
"Cannot launch a new installer until the previous one has finished.",
)
)
return
self.installer_gui = UpdateAllGUI(self.item_model.repos)
self.installer_gui.addon_updated.connect(self.on_package_status_changed)
self.installer_gui.finished.connect(self.cleanup_installer)
self.installer_gui.run() # Does not block
def hide_progress_widgets(self) -> None:
"""hides the progress bar and related widgets"""
self.composite_view.package_list.set_loading(False)
def show_progress_widgets(self) -> None:
self.composite_view.package_list.set_loading(True)
def update_progress_bar(self, message: str, current_value: int, max_value: int) -> None:
"""Update the progress bar, showing it if it's hidden"""
max_value = max_value if max_value > 0 else 1
if current_value < 0:
current_value = 0
elif current_value > max_value:
current_value = max_value
self.show_progress_widgets()
progress = Progress(
status_text=message,
number_of_tasks=self.number_of_progress_regions,
current_task=self.current_progress_region - 1,
current_task_progress=current_value / max_value,
)
self.composite_view.package_list.update_loading_progress(progress)
def stop_update(self) -> None:
self.cleanup_workers()
self.hide_progress_widgets()
self.write_cache_stopfile()
self.button_bar.refresh_local_cache.setEnabled(True)
self.button_bar.refresh_local_cache.setText(
translate("AddonsInstaller", "Refresh local cache")
)
@staticmethod
def write_cache_stopfile() -> None:
stopfile = utils.get_cache_file_name("CACHE_UPDATE_INTERRUPTED")
with open(stopfile, "w", encoding="utf8") as f:
f.write(
"This file indicates that a cache operation was interrupted, and "
"the cache is in an unknown state. It will be deleted next time "
"AddonManager re-caches."
)
def on_package_status_changed(self, repo: Addon) -> None:
if repo.status() == Addon.Status.PENDING_RESTART:
self.restart_required = True
self.item_model.reload_item(repo)
self.composite_view.package_details_controller.show_repo(repo)
if repo in self.packages_with_updates:
self.packages_with_updates.remove(repo)
self.enable_updates(len(self.packages_with_updates))
def execute_macro(self, repo: Addon) -> None:
"""executes a selected macro"""
macro = repo.macro
if not macro or not macro.code:
return
if macro.is_installed():
macro_path = os.path.join(self.macro_repo_dir, macro.filename)
FreeCADGui.open(str(macro_path))
self.dialog.hide()
FreeCADGui.SendMsgToActiveView("Run")
else:
with tempfile.TemporaryDirectory() as temp_dir:
temp_install_succeeded = macro.install(temp_dir)
if not temp_install_succeeded:
FreeCAD.Console.PrintError(
translate("AddonsInstaller", "Temporary installation of macro failed.")
)
return
macro_path = os.path.join(temp_dir, macro.filename)
FreeCADGui.open(str(macro_path))
self.dialog.hide()
FreeCADGui.SendMsgToActiveView("Run")
def remove(self, addon: Addon) -> None:
"""Remove this addon."""
if self.installer_gui is not None:
FreeCAD.Console.PrintError(
translate(
"AddonsInstaller",
"Cannot launch a new installer until the previous one has finished.",
)
)
return
self.installer_gui = AddonUninstallerGUI(addon)
self.installer_gui.finished.connect(self.cleanup_installer)
self.installer_gui.finished.connect(
functools.partial(self.on_package_status_changed, addon)
)
self.installer_gui.run() # Does not block
# @}
-20
View File
@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>928</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Addon Manager</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4"/>
</widget>
<resources/>
<connections/>
</ui>
-314
View File
@@ -1,314 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2022 FreeCAD Project Association *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD 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 *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
"""Contains a the Addon Manager's preferences dialog management class"""
import os
import FreeCAD
import FreeCADGui
from PySide import QtCore
from PySide.QtGui import QIcon
from PySide.QtWidgets import (
QWidget,
QCheckBox,
QComboBox,
QDialog,
QHeaderView,
QRadioButton,
QLineEdit,
QTextEdit,
)
translate = FreeCAD.Qt.translate
# pylint: disable=too-few-public-methods
class AddonManagerOptions:
"""A class containing a form element that is inserted as a FreeCAD preference page."""
def __init__(self, _=None):
self.form = FreeCADGui.PySideUic.loadUi(
os.path.join(os.path.dirname(__file__), "AddonManagerOptions.ui")
)
self.table_model = CustomRepoDataModel()
self.form.customRepositoriesTableView.setModel(self.table_model)
self.form.addCustomRepositoryButton.setIcon(
QIcon.fromTheme("add", QIcon(":/icons/list-add.svg"))
)
self.form.removeCustomRepositoryButton.setIcon(
QIcon.fromTheme("remove", QIcon(":/icons/list-remove.svg"))
)
self.form.customRepositoriesTableView.horizontalHeader().setStretchLastSection(False)
self.form.customRepositoriesTableView.horizontalHeader().setSectionResizeMode(
0, QHeaderView.Stretch
)
self.form.customRepositoriesTableView.horizontalHeader().setSectionResizeMode(
1, QHeaderView.ResizeToContents
)
self.form.addCustomRepositoryButton.clicked.connect(self._add_custom_repo_clicked)
self.form.removeCustomRepositoryButton.clicked.connect(self._remove_custom_repo_clicked)
self.form.customRepositoriesTableView.doubleClicked.connect(self._row_double_clicked)
def saveSettings(self):
"""Required function: called by the preferences dialog when Apply or Save is clicked,
saves out the preference data by reading it from the widgets."""
for widget in self.form.children():
self.recursive_widget_saver(widget)
self.table_model.save_model()
def recursive_widget_saver(self, widget):
"""Writes out the data for this widget and all of its children, recursively."""
if isinstance(widget, QWidget):
# See if it's one of ours:
pref_path = widget.property("prefPath")
pref_entry = widget.property("prefEntry")
if pref_path and pref_entry:
pref_path = pref_path.data()
pref_entry = pref_entry.data()
pref_access_string = f"User parameter:BaseApp/Preferences/{str(pref_path,'utf-8')}"
pref = FreeCAD.ParamGet(pref_access_string)
if isinstance(widget, QCheckBox):
checked = widget.isChecked()
pref.SetBool(str(pref_entry, "utf-8"), checked)
elif isinstance(widget, QRadioButton):
checked = widget.isChecked()
pref.SetBool(str(pref_entry, "utf-8"), checked)
elif isinstance(widget, QComboBox):
new_index = widget.currentIndex()
pref.SetInt(str(pref_entry, "utf-8"), new_index)
elif isinstance(widget, QTextEdit):
text = widget.toPlainText()
pref.SetString(str(pref_entry, "utf-8"), text)
elif isinstance(widget, QLineEdit):
text = widget.text()
pref.SetString(str(pref_entry, "utf-8"), text)
elif widget.metaObject().className() == "Gui::PrefFileChooser":
filename = str(widget.property("fileName"))
filename = pref.SetString(str(pref_entry, "utf-8"), filename)
# Recurse over children
if isinstance(widget, QtCore.QObject):
for child in widget.children():
self.recursive_widget_saver(child)
def loadSettings(self):
"""Required function: called by the preferences dialog when it is launched,
loads the preference data and assigns it to the widgets."""
for widget in self.form.children():
self.recursive_widget_loader(widget)
self.table_model.load_model()
def recursive_widget_loader(self, widget):
"""Loads the data for this widget and all of its children, recursively."""
if isinstance(widget, QWidget):
# See if it's one of ours:
pref_path = widget.property("prefPath")
pref_entry = widget.property("prefEntry")
if pref_path and pref_entry:
pref_path = pref_path.data()
pref_entry = pref_entry.data()
pref_access_string = f"User parameter:BaseApp/Preferences/{str(pref_path,'utf-8')}"
pref = FreeCAD.ParamGet(pref_access_string)
if isinstance(widget, QCheckBox):
widget.setChecked(pref.GetBool(str(pref_entry, "utf-8")))
elif isinstance(widget, QRadioButton):
if pref.GetBool(str(pref_entry, "utf-8")):
widget.setChecked(True)
elif isinstance(widget, QComboBox):
new_index = pref.GetInt(str(pref_entry, "utf-8"))
widget.setCurrentIndex(new_index)
elif isinstance(widget, QTextEdit):
text = pref.GetString(str(pref_entry, "utf-8"))
widget.setText(text)
elif isinstance(widget, QLineEdit):
text = pref.GetString(str(pref_entry, "utf-8"))
widget.setText(text)
elif widget.metaObject().className() == "Gui::PrefFileChooser":
filename = pref.GetString(str(pref_entry, "utf-8"))
widget.setProperty("fileName", filename)
# Recurse over children
if isinstance(widget, QtCore.QObject):
for child in widget.children():
self.recursive_widget_loader(child)
def _add_custom_repo_clicked(self):
"""Callback: show the Add custom repo dialog"""
dlg = CustomRepositoryDialog()
url, branch = dlg.exec()
if url and branch:
self.table_model.appendData(url, branch)
def _remove_custom_repo_clicked(self):
"""Callback: when the remove button is clicked, get the current selection and remove it."""
item = self.form.customRepositoriesTableView.currentIndex()
if not item.isValid():
return
row = item.row()
self.table_model.removeRows(row, 1, QtCore.QModelIndex())
def _row_double_clicked(self, item):
"""Edit the row that was double-clicked"""
row = item.row()
dlg = CustomRepositoryDialog()
url_index = self.table_model.createIndex(row, 0)
branch_index = self.table_model.createIndex(row, 1)
dlg.dialog.urlLineEdit.setText(self.table_model.data(url_index))
dlg.dialog.branchLineEdit.setText(self.table_model.data(branch_index))
url, branch = dlg.exec()
if url and branch:
self.table_model.setData(url_index, url)
self.table_model.setData(branch_index, branch)
class CustomRepoDataModel(QtCore.QAbstractTableModel):
"""The model for the custom repositories: wraps the underlying preference data and uses that
as its main data store."""
def __init__(self):
super().__init__()
pref_access_string = "User parameter:BaseApp/Preferences/Addons"
self.pref = FreeCAD.ParamGet(pref_access_string)
self.load_model()
def load_model(self):
"""Load the data from the preferences entry"""
pref_entry: str = self.pref.GetString("CustomRepositories", "")
# The entry is saved as a space- and newline-delimited text block: break it into its
# constituent parts
lines = pref_entry.split("\n")
self.model = []
for line in lines:
if not line:
continue
split_data = line.split()
if len(split_data) > 1:
branch = split_data[1]
else:
branch = "master"
url = split_data[0]
self.model.append([url, branch])
def save_model(self):
"""Save the data into a preferences entry"""
entry = ""
for row in self.model:
entry += f"{row[0]} {row[1]}\n"
self.pref.SetString("CustomRepositories", entry)
def rowCount(self, parent: QtCore.QModelIndex = QtCore.QModelIndex()) -> int:
"""The number of rows"""
if parent.isValid():
return 0
return len(self.model)
def columnCount(self, parent: QtCore.QModelIndex = QtCore.QModelIndex()) -> int:
"""The number of columns (which is always 2)"""
if parent.isValid():
return 0
return 2
def data(self, index, role=QtCore.Qt.DisplayRole):
"""The data at an index."""
if role != QtCore.Qt.DisplayRole:
return None
row = index.row()
column = index.column()
if row > len(self.model):
return None
if column > 1:
return None
return self.model[row][column]
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
"""Get the row and column header data."""
if role != QtCore.Qt.DisplayRole:
return None
if orientation == QtCore.Qt.Vertical:
return section + 1
if section == 0:
return translate(
"AddonsInstaller",
"Repository URL",
"Preferences header for custom repositories",
)
if section == 1:
return translate(
"AddonsInstaller",
"Branch name",
"Preferences header for custom repositories",
)
return None
def removeRows(self, row, count, parent):
"""Remove rows"""
self.beginRemoveRows(parent, row, row + count - 1)
for _ in range(count):
self.model.pop(row)
self.endRemoveRows()
def insertRows(self, row, count, parent):
"""Insert blank rows"""
self.beginInsertRows(parent, row, row + count - 1)
for _ in range(count):
self.model.insert(["", ""])
self.endInsertRows()
def appendData(self, url, branch):
"""Append this url and branch to the end of the list"""
row = self.rowCount()
self.beginInsertRows(QtCore.QModelIndex(), row, row)
self.model.append([url, branch])
self.endInsertRows()
def setData(self, index, value, role=QtCore.Qt.EditRole):
"""Set the data at this index"""
if role != QtCore.Qt.EditRole:
return
self.model[index.row()][index.column()] = value
self.dataChanged.emit(index, index)
class CustomRepositoryDialog:
"""A dialog for setting up a custom repository, with branch information"""
def __init__(self):
self.dialog = FreeCADGui.PySideUic.loadUi(
os.path.join(os.path.dirname(__file__), "AddonManagerOptions_AddCustomRepository.ui")
)
def exec(self):
"""Run the dialog modally, and return either None or a tuple or (url,branch)"""
result = self.dialog.exec()
if result == QDialog.Accepted:
url = self.dialog.urlLineEdit.text()
branch = self.dialog.branchLineEdit.text()
return (url, branch)
return (None, None)
-482
View File
@@ -1,482 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Gui::Dialog::DlgSettingsAddonManager</class>
<widget class="QWidget" name="Gui::Dialog::DlgSettingsAddonManager">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>757</width>
<height>783</height>
</rect>
</property>
<property name="windowTitle">
<string>Addon manager options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="Gui::PrefCheckBox" name="guiprefcheckboxcheckupdates">
<property name="toolTip">
<string>If this option is selected, when launching the Addon Manager,
installed addons will be checked for available updates</string>
</property>
<property name="text">
<string>Automatically check for updates at start (requires Git)</string>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>AutoCheck</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="guiprefcheckboxdownloadmacros">
<property name="text">
<string>Download Macro metadata (approximately 10MB)</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>DownloadMacros</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Cache update frequency</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefComboBox" name="guiprefcomboboxupdatefrequency">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="prefEntry" stdset="0">
<cstring>UpdateFrequencyComboEntry</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
<item>
<property name="text">
<string>Manual (no automatic updates)</string>
</property>
</item>
<item>
<property name="text">
<string>Daily</string>
</property>
</item>
<item>
<property name="text">
<string>Weekly</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="guiprefcheckboxhideunlicensed">
<property name="text">
<string>Hide Addons without a license</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>HideUnlicensed</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="guiprefcheckboxhidenonfsf">
<property name="text">
<string>Hide Addons with non-FSF Free/Libre license</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>HideNonFSFFreeLibre</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="guiprefcheckboxnonosi">
<property name="text">
<string>Hide Addons with non-OSI-approved license</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>HideNonOSIApproved</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="guiprefcheckboxhidepy2">
<property name="text">
<string>Hide Addons marked Python 2 Only</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>HidePy2</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="guiprefcheckboxhideobsolete">
<property name="text">
<string>Hide Addons marked Obsolete</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>HideObsolete</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="guiprefcheckboxhidenewerfreecadrequired">
<property name="text">
<string>Hide Addons that require a newer version of FreeCAD</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
<property name="prefEntry" stdset="0">
<cstring>HideNewerFreeCADRequired</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Custom repositories</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="customRepositoriesTableView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
</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="QToolButton" name="addCustomRepositoryButton">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="removeCustomRepositoryButton">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Proxy</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="Gui::PrefRadioButton" name="guiprefradiobuttonnoproxy">
<property name="text">
<string>No proxy</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>NoProxyCheck</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefRadioButton" name="guiprefradiobuttonsystemproxy">
<property name="text">
<string>User system proxy</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>SystemProxyCheck</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefRadioButton" name="guiprefradiobuttonuserproxy">
<property name="text">
<string>User-defined proxy:</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>UserProxyCheck</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefLineEdit" name="guipreflineedituserproxy">
<property name="prefEntry" stdset="0">
<cstring>ProxyUrl</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayoutScore">
<item>
<widget class="QLabel" name="label_score">
<property name="text">
<string>Score source URL</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefLineEdit" name="guipreflineeditscoresourceurl">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>The URL for the Addon Score data (see Addon Manager wiki page for formatting and hosting details).</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>AddonsScoreURL</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Path to Git executable (optional):</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::PrefFileChooser" name="gui::preffilechooser" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>The path to the Git executable. Autodetected if needed and not specified.</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>GitExecutable</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="advanced">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Advanced Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="Gui::PrefCheckBox" name="guiprefcheckboxShowBranchSwitcher">
<property name="text">
<string>Show option to change branches (requires Git)</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>ShowBranchSwitcher</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="guiprefcheckboxDisableGit">
<property name="text">
<string>Disable Git (fall back to ZIP downloads only)</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>disableGit</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="guiprefcheckboxDeveloperMode">
<property name="toolTip">
<string>Activate Addon Manager options intended for developers of new Addons.</string>
</property>
<property name="text">
<string>Addon developer mode</string>
</property>
<property name="prefPath" stdset="0">
<cstring>Addons</cstring>
</property>
<property name="prefEntry" stdset="0">
<cstring>developerMode</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::PrefCheckBox</class>
<extends>QCheckBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefComboBox</class>
<extends>QComboBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefRadioButton</class>
<extends>QRadioButton</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefLineEdit</class>
<extends>QLineEdit</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefFileChooser</class>
<extends>QWidget</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -1,84 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AddCustomRepositoryDialog</class>
<widget class="QDialog" name="AddCustomRepositoryDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>95</height>
</rect>
</property>
<property name="windowTitle">
<string>Custom repository</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Repository URL</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="urlLineEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Branch</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<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>
<item row="1" column="1">
<widget class="QLineEdit" name="branchLineEdit"/>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AddCustomRepositoryDialog</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>AddCustomRepositoryDialog</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>

Some files were not shown because too many files have changed in this diff Show More