Merge branch 'main' into bim-cleanup-license
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -9,7 +9,6 @@ files: |
|
||||
src/Main|
|
||||
src/Tools|
|
||||
tests/src|
|
||||
src/Mod/AddonManager|
|
||||
src/Mod/Assembly|
|
||||
src/Mod/CAM|
|
||||
src/Mod/Cloud|
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
},
|
||||
"CMAKE_POLICY_VERSION_MINIMUM": {
|
||||
"type": "STRING",
|
||||
"value": "3.5"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+3
-3
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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 ′ ′ ′
|
||||
"\xE2\x80\xB2" COUNTCHARS; yylval.quantity.scaler = Quantity::AngMinute; yylval.quantity.unitStr = yytext; return UNIT; // angminute U+2032 ′ ′ ′
|
||||
"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 ″ ″ ″
|
||||
"\xE2\x80\xB3" COUNTCHARS; yylval.quantity.scaler = Quantity::AngSecond; yylval.quantity.unitStr = yytext; return UNIT; // angsecond U+2033 ″ ″ ″
|
||||
|
||||
{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);}
|
||||
;
|
||||
|
||||
%%
|
||||
@@ -649,7 +649,7 @@ public:
|
||||
};
|
||||
|
||||
#define YYSTYPE semantic_type
|
||||
#include "ExpressionParser.tab.h"
|
||||
#include "Expression.tab.h"
|
||||
#undef YYTOKENTYPE
|
||||
#undef YYSTYPE
|
||||
#undef YYSTYPE_ISDECLARED
|
||||
|
||||
@@ -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 \
|
||||
)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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; }
|
||||
;
|
||||
|
||||
%%
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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; }
|
||||
;
|
||||
|
||||
|
||||
%%
|
||||
@@ -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
@@ -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}
|
||||
)
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 "?";
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, "");
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) \
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Submodule
+1
Submodule src/Mod/AddonManager added at 34d433a02c
@@ -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
|
||||
@@ -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
|
||||
]
|
||||
@@ -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}'")
|
||||
@@ -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
|
||||
|
||||
|
||||
# @}
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
@@ -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
Reference in New Issue
Block a user