Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: ['https://www.patreon.com/yorikvanhavre', 'https://www.patreon.com/kkremitzki', 'https://www.patreon.com/thundereal']
|
||||
+8
-4
@@ -239,6 +239,7 @@ before_install:
|
||||
curl -L https://github.com/frerich/clcache/releases/download/v4.2.0/clcache-4.2.0.zip --output clcache-4.2.0.zip
|
||||
7z x clcache-4.2.0.zip > /dev/null
|
||||
export PATH=$CLCACHE_PATH:$PATH
|
||||
export PATH=$TEST_PATH:$PATH
|
||||
#reset clcache hit stats
|
||||
cmd.exe /C 'C:\Users\travis\build\FreeCAD\FreeCAD\clcache.exe -z'
|
||||
# clcache stats before compilation
|
||||
@@ -269,19 +270,22 @@ script:
|
||||
####
|
||||
# Install FreeCAD and run unit tests. Test failures will fail the build
|
||||
##
|
||||
# MSBuild.exe /m FreeCAD_Trunk.sln
|
||||
# MSBuild.exe /m FreeCAD.sln
|
||||
- |
|
||||
if [ "${TRAVIS_OS_NAME}" == "windows" ]; then
|
||||
# call msbuild using clcache
|
||||
#cmd.exe /C '"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" amd64 && MSBuild.exe FreeCAD_Trunk.sln /p:CLToolExe=clcache.exe /p:TrackFileAccess=false /p:CLToolPath=C:\Users\travis\build\FreeCAD\FreeCAD /m:2 /nologo /verbosity:minimal /p:Configuration=Release /p:Platform=x64'
|
||||
#cmd.exe /C '"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" amd64 && MSBuild.exe FreeCAD.sln /p:CLToolExe=clcache.exe /p:TrackFileAccess=false /p:CLToolPath=C:\Users\travis\build\FreeCAD\FreeCAD /m:2 /nologo /verbosity:minimal /p:Configuration=Release /p:Platform=x64'
|
||||
cmd.exe /C 'C:\Users\travis\build\FreeCAD\FreeCAD\.travis\build.bat'
|
||||
# ls -lahR
|
||||
cp -a ../FreeCADLibs/bin/* bin/
|
||||
# show clcache hit stats
|
||||
cmd.exe /C 'C:\Users\travis\build\FreeCAD\FreeCAD\clcache.exe -s'
|
||||
# run the tests
|
||||
cmd.exe /C 'cd C:\Users\travis\build\FreeCAD\FreeCAD\build\bin && FreeCADCmd.exe --run-test 0'
|
||||
cd $HOME
|
||||
#cmd.exe /C 'cd C:\Users\travis\build\FreeCAD\FreeCAD\build\bin && FreeCADCmd.exe --run-test 0'
|
||||
# Make build fail if ANY of the following fails
|
||||
#set -ev
|
||||
winpty.exe -Xallow-non-tty -Xplain /C/Users/travis/build/FreeCAD/FreeCAD/build/bin/FreeCADCmd.exe --run-test 0 | tee runlog.txt
|
||||
grep -q FAILED runlog.txt ; [ $? == 1 ] && echo "Build succeeded and tests passed!" || ( echo "Tests failed!" && false )
|
||||
else
|
||||
sudo make -j2 install
|
||||
${INSTALLED_APP_PATH} --console --run-test 0
|
||||
|
||||
+1
-1
@@ -2,5 +2,5 @@ echo on
|
||||
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" amd64
|
||||
|
||||
MSBuild.exe FreeCAD_Trunk.sln /p:CLToolExe=clcache.exe /p:TrackFileAccess=false /p:CLToolPath=C:\Users\travis\build\FreeCAD\FreeCAD /m:2 /nologo /verbosity:minimal /p:Configuration=Release /p:Platform=x64
|
||||
MSBuild.exe FreeCAD.sln /p:CLToolExe=clcache.exe /p:TrackFileAccess=false /p:CLToolPath=C:\Users\travis\build\FreeCAD\FreeCAD /m:2 /nologo /verbosity:minimal /p:Configuration=Release /p:Platform=x64
|
||||
|
||||
|
||||
+717
-628
File diff suppressed because it is too large
Load Diff
+13
-19
@@ -1,24 +1,21 @@
|
||||
#version:
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- generator: "Visual Studio 12 Win64"
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
# APPVEYOR_SAVE_CACHE_ON_ERROR: true
|
||||
generator: "Visual Studio 15 2017 Win64"
|
||||
ARCH: "Win64"
|
||||
Compiler: "MSVC2013"
|
||||
- generator: "Visual Studio 12"
|
||||
ARCH: "Win32"
|
||||
Compiler: "MSVC2013"
|
||||
Compiler: "MSVC2017"
|
||||
|
||||
#cache: #
|
||||
cache: c:\users\appveyor\clcache
|
||||
cache: c:\users\appveyor\clcache -> appveyor.yml
|
||||
|
||||
configuration:
|
||||
#- Debug
|
||||
- Release
|
||||
|
||||
#branches:
|
||||
# only:
|
||||
|
||||
clone_depth: 1600
|
||||
|
||||
# scripts that are called at very beginning, before repo cloning
|
||||
@@ -30,15 +27,10 @@ clone_depth: 1600
|
||||
#todo cleanup libpacks on the FreeCAD-ports-cache repo
|
||||
install:
|
||||
- cd C:\projects\freecad
|
||||
- if [%ARCH%] == [Win64] (appveyor DownloadFile https://github.com/FreeCAD/FreeCAD-ports-cache/releases/download/v0.17/FreeCADLibs_11.5.1_x64_VC12.7z)
|
||||
- if [%ARCH%] == [Win64] (appveyor DownloadFile https://github.com/apeltauer/FreeCAD/releases/download/LibPack_12.1/FreeCADLibs_12.1.2_x64_VC15.7z)
|
||||
- if [%ARCH%] == [Win64] (
|
||||
powershell -Command 7z x FreeCADLibs_11.5.1_x64_VC12.7z > nul &&
|
||||
ren FreeCADLibs_11.5_x64_VC12 FreeCADLibs)
|
||||
- if [%ARCH%] == [Win32] (appveyor DownloadFile https://github.com/FreeCAD/FreeCAD-ports-cache/releases/download/v0.17/FreeCADLibs_11.5.1_x86_VC12.7z)
|
||||
- if [%ARCH%] == [Win32] (
|
||||
powershell -Command 7z x FreeCADLibs_11.5.1_x86_VC12.7z > nul &&
|
||||
ren FreeCADLibs_11.5_x86_VC12 FreeCADLibs)
|
||||
- dir
|
||||
powershell -Command 7z x FreeCADLibs_12.1.2_x64_VC15.7z -oFreeCADLibs > nul &&
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat")
|
||||
- set PATH=c:\Python37;c:\Python37\Scripts;%PATH%
|
||||
- pip install clcache
|
||||
- clcache -z
|
||||
@@ -54,7 +46,9 @@ build_script:
|
||||
-G "%generator%" ..
|
||||
- mkdir bin
|
||||
- xcopy C:\projects\freecad\FreeCADLibs\bin C:\projects\freecad\build\bin /E /Q
|
||||
- msbuild FreeCAD_Trunk.sln /p:TrackFileAccess=false /p:CLToolExe=clcache.exe /p:CLToolPath=c:\Python37\Scripts\ /m
|
||||
- ps: fsutil behavior set disablelastaccess 0 # Enable Access time feature on Windows (for clcache)
|
||||
- msbuild FreeCAD.sln /p:TrackFileAccess=false /p:CLToolExe=clcache.exe /p:CLToolPath=c:\Python37\Scripts\ /m
|
||||
- ps: fsutil behavior set disablelastaccess 1
|
||||
- clcache -s
|
||||
|
||||
#after_build:
|
||||
|
||||
@@ -237,8 +237,10 @@ MACRO(SET_BIN_DIR ProjectName OutputName)
|
||||
# FreeCADBase, SMDS, Driver, MEFISTO2 and area-native libs don't depend on parts from CMAKE_INSTALL_LIBDIR
|
||||
if(NOT ${ProjectName} MATCHES "^(FreeCADBase|SMDS|Driver|MEFISTO2|area-native)$")
|
||||
if(${ARGC} STREQUAL 4)
|
||||
set_property(TARGET ${ProjectName} APPEND PROPERTY INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}${ARGV3})
|
||||
else(${ARGC} STREQUAL 4)
|
||||
set_property(TARGET ${ProjectName} APPEND PROPERTY INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${ARGV3})
|
||||
elseif(NOT IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
|
||||
set_property(TARGET ${ProjectName} APPEND PROPERTY INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
|
||||
else()
|
||||
set_property(TARGET ${ProjectName} APPEND PROPERTY INSTALL_RPATH ${CMAKE_INSTALL_LIBDIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -96,6 +96,7 @@ set(QT_INCLUDE_DIR
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.8.6/QtTest
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.8.6/QtUiTools
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.8.6/QtXml
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.8.6/QtXmlPatterns
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.8.6/QtOpenGl
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.8.6/QtWebKit
|
||||
)
|
||||
@@ -118,6 +119,7 @@ set(QT_LIBRARIES
|
||||
optimized QtSql4.lib
|
||||
optimized QtTest4.lib
|
||||
optimized QtXml4.lib
|
||||
optimized QtXmlPatterns4.lib
|
||||
optimized QtOpenGl4.lib
|
||||
optimized QtWebKit4.lib
|
||||
debug QtCored4.lib
|
||||
@@ -128,6 +130,7 @@ set(QT_LIBRARIES
|
||||
debug QtSqld4.lib
|
||||
debug QtTestd4.lib
|
||||
debug QtXmld4.lib
|
||||
debug QtXmlPatternsd4.lib
|
||||
debug QtOpenGld4.lib
|
||||
debug QtWebKitd4.lib
|
||||
)
|
||||
|
||||
@@ -84,6 +84,7 @@ set(QT_INCLUDE_DIR
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtTest
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtUiTools
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtXml
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtXmlPatterns
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtOpenGl
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtWebKit
|
||||
)
|
||||
@@ -102,6 +103,7 @@ set(QT_LIBRARIES
|
||||
optimized QtSql4.lib
|
||||
optimized QtTest4.lib
|
||||
optimized QtXml4.lib
|
||||
optimized QtXmlPatterns4.lib
|
||||
optimized QtOpenGl4.lib
|
||||
optimized QtWebKit4.lib
|
||||
debug QtCored4.lib
|
||||
@@ -112,6 +114,7 @@ set(QT_LIBRARIES
|
||||
debug QtSqld4.lib
|
||||
debug QtTestd4.lib
|
||||
debug QtXmld4.lib
|
||||
debug QtXmlPatternsd4.lib
|
||||
debug QtOpenGld4.lib
|
||||
debug QtWebKitd4.lib
|
||||
)
|
||||
@@ -316,10 +319,10 @@ set(OCC_LIBRARIES
|
||||
TKFeat
|
||||
)
|
||||
set(OCC_OCAF_LIBRARIES
|
||||
TKCAF
|
||||
TKXCAF
|
||||
TKLCAF
|
||||
TKXDESTEP
|
||||
TKXDEIGES
|
||||
TKCAF
|
||||
TKXCAF
|
||||
TKLCAF
|
||||
TKXDESTEP
|
||||
TKXDEIGES
|
||||
)
|
||||
set(OCC_FOUND TRUE)
|
||||
|
||||
@@ -93,6 +93,7 @@ set(QT_INCLUDE_DIR
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtTest
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtUiTools
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtXml
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtXmlPatterns
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtOpenGl
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtWebKit
|
||||
)
|
||||
@@ -111,6 +112,7 @@ set(QT_LIBRARIES
|
||||
optimized QtSql4.lib
|
||||
optimized QtTest4.lib
|
||||
optimized QtXml4.lib
|
||||
optimized QtXmlPatterns4.lib
|
||||
optimized QtOpenGl4.lib
|
||||
optimized QtWebKit4.lib
|
||||
debug QtCored4.lib
|
||||
@@ -121,6 +123,7 @@ set(QT_LIBRARIES
|
||||
debug QtSqld4.lib
|
||||
debug QtTestd4.lib
|
||||
debug QtXmld4.lib
|
||||
debug QtXmlPatternsd4.lib
|
||||
debug QtOpenGld4.lib
|
||||
debug QtWebKitd4.lib
|
||||
)
|
||||
@@ -327,11 +330,11 @@ set(OCC_LIBRARIES
|
||||
TKFeat
|
||||
)
|
||||
set(OCC_OCAF_LIBRARIES
|
||||
TKCAF
|
||||
TKXCAF
|
||||
TKLCAF
|
||||
TKXDESTEP
|
||||
TKXDEIGES
|
||||
TKCAF
|
||||
TKXCAF
|
||||
TKLCAF
|
||||
TKXDESTEP
|
||||
TKXDEIGES
|
||||
)
|
||||
set(OCC_FOUND TRUE)
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ set(QT_INCLUDE_DIR
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtTest
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtUiTools
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtXml
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtXmlPatterns
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtOpenGl
|
||||
${FREECAD_LIBPACK_DIR}/include/QT/QtWebKit
|
||||
)
|
||||
@@ -123,6 +124,7 @@ set(QT_LIBRARIES
|
||||
optimized QtSql4.lib
|
||||
optimized QtTest4.lib
|
||||
optimized QtXml4.lib
|
||||
optimized QtXmlPatterns4.lib
|
||||
optimized QtOpenGl4.lib
|
||||
optimized QtWebKit4.lib
|
||||
debug QtCored4.lib
|
||||
@@ -133,6 +135,7 @@ set(QT_LIBRARIES
|
||||
debug QtSqld4.lib
|
||||
debug QtTestd4.lib
|
||||
debug QtXmld4.lib
|
||||
debug QtXmlPatternsd4.lib
|
||||
debug QtOpenGld4.lib
|
||||
debug QtWebKitd4.lib
|
||||
)
|
||||
|
||||
@@ -95,6 +95,7 @@ set(QT_INCLUDE_DIR
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.8.5/QtTest
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.8.5/QtUiTools
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.8.5/QtXml
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.8.5/QtXmlPatterns
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.8.5/QtOpenGl
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.8.5/QtWebKit
|
||||
)
|
||||
@@ -121,6 +122,7 @@ set(QT_LIBRARIES
|
||||
optimized QtSql4.lib
|
||||
optimized QtTest4.lib
|
||||
optimized QtXml4.lib
|
||||
optimized QtXmlPatterns4.lib
|
||||
optimized QtOpenGl4.lib
|
||||
optimized QtWebKit4.lib
|
||||
debug QtCored4.lib
|
||||
@@ -131,6 +133,7 @@ set(QT_LIBRARIES
|
||||
debug QtSqld4.lib
|
||||
debug QtTestd4.lib
|
||||
debug QtXmld4.lib
|
||||
debug QtXmlPatternsd4.lib
|
||||
debug QtOpenGld4.lib
|
||||
debug QtWebKitd4.lib
|
||||
)
|
||||
|
||||
@@ -104,6 +104,7 @@ set(QT_INCLUDE_DIR
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.5.3/QtTest
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.5.3/QtUiTools
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.5.3/QtXml
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.5.3/QtXmlPatterns
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.5.3/QtOpenGl
|
||||
${FREECAD_LIBPACK_DIR}/include/Qt-4.5.3/QtWebKit
|
||||
)
|
||||
@@ -126,6 +127,7 @@ set(QT_LIBRARIES
|
||||
optimized QtSql4.lib
|
||||
optimized QtTest4.lib
|
||||
optimized QtXml4.lib
|
||||
optimized QtXmlPatterns4.lib
|
||||
optimized QtOpenGl4.lib
|
||||
optimized QtWebKit4.lib
|
||||
debug QtCored4.lib
|
||||
@@ -136,6 +138,7 @@ set(QT_LIBRARIES
|
||||
debug QtSqld4.lib
|
||||
debug QtTestd4.lib
|
||||
debug QtXmld4.lib
|
||||
debug QtXmlPatternsd4.lib
|
||||
debug QtOpenGld4.lib
|
||||
debug QtWebKitd4.lib
|
||||
)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-16000
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
path_classifiers:
|
||||
generated:
|
||||
- "src/Mod/Import/App/ap203_configuration_controlled_3d_design_of_mechanical_parts_and_assemblies_mim_lf.py"
|
||||
- "src/Mod/Import/App/automotive_design.py"
|
||||
- "src/Mod/Import/App/config_control_design.py"
|
||||
- "src/Mod/Import/App/ifc2x3.py"
|
||||
- "src/Mod/Import/App/ifc4.py"
|
||||
extraction:
|
||||
javascript:
|
||||
index:
|
||||
filters:
|
||||
exclude: "**/translations/*.ts"
|
||||
@@ -47,7 +47,9 @@ BuildRequires: netgen-mesher-devel-private
|
||||
BuildRequires: pyside-tools
|
||||
BuildRequires: python
|
||||
BuildRequires: python-matplotlib
|
||||
%if 0%{?fedora} < 30
|
||||
BuildRequires: python-pivy
|
||||
%endif
|
||||
BuildRequires: python-pyside
|
||||
BuildRequires: python-pyside-devel
|
||||
BuildRequires: python2-devel
|
||||
|
||||
+5
-5
@@ -7,7 +7,7 @@ SET(SMESH_VERSION_PATCH 1)
|
||||
SET(SMESH_VERSION_TWEAK 0)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -Wno-sign-compare -Wno-reorder -Wno-switch -Wno-unused-variable -Wno-unused-but-set-variable -Wno-comment -Wno-unused-parameter")
|
||||
set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -Wno-sign-compare -Wno-reorder -Wno-switch -Wno-unused-variable -Wno-unused-but-set-variable -Wno-comment -Wno-unused-parameter -Wno-empty-body")
|
||||
elseif(CMAKE_COMPILER_IS_CLANGXX)
|
||||
set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -Wno-sign-compare -Wno-reorder -Wno-switch -Wno-unused-variable -Wno-unused-private-field -Wno-unused-function -Wno-sometimes-uninitialized -Wno-overloaded-virtual -Wno-dynamic-class-memaccess -Wno-comment -Wno-unused-parameter")
|
||||
endif()
|
||||
@@ -403,8 +403,8 @@ endif (BUILD_FEM_NETGEN)
|
||||
|
||||
if(WIN32)
|
||||
INSTALL(TARGETS SMDS Driver DriverSTL DriverDAT DriverUNV SMESHDS SMESH MEFISTO2 StdMeshers
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
else(WIN32)
|
||||
INSTALL(TARGETS SMDS Driver DriverSTL DriverDAT DriverUNV SMESHDS SMESH MEFISTO2 StdMeshers
|
||||
@@ -415,8 +415,8 @@ endif(WIN32)
|
||||
if (BUILD_FEM_NETGEN)
|
||||
if(WIN32)
|
||||
INSTALL(TARGETS NETGENPlugin
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
else(WIN32)
|
||||
INSTALL(TARGETS NETGENPlugin
|
||||
|
||||
Vendored
+2
-2
@@ -180,12 +180,12 @@ class R4: public R3
|
||||
{
|
||||
friend std::ostream& operator <<(std::ostream& f, const R4 & P )
|
||||
{ f << P.x << ' ' << P.y << ' ' << P.z << ' ' << P.omega; return f; }
|
||||
friend istream& operator >>(istream& f, R4 & P)
|
||||
friend std::istream& operator >>(std::istream& f, R4 & P)
|
||||
{ f >> P.x >> P.y >> P.z >> P.omega ; return f; }
|
||||
|
||||
friend std::ostream& operator <<(std::ostream& f, const R4 * P )
|
||||
{ f << P->x << ' ' << P->y << ' ' << P->z << ' ' << P->omega; return f; }
|
||||
friend istream& operator >>(istream& f, R4 * P)
|
||||
friend std::istream& operator >>(std::istream& f, R4 * P)
|
||||
{ f >> P->x >> P->y >> P->z >> P->omega ; return f; }
|
||||
|
||||
public:
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
\defgroup SMESH Salome SMESH
|
||||
\ingroup EMBEDDED
|
||||
\brief The Salome SMESH module is used in Fem and Mesh workbenches
|
||||
|
||||
*/
|
||||
@@ -20,6 +20,12 @@
|
||||
// See http://www.salome-platform.org/ or email : [email protected]
|
||||
//
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdelete-non-virtual-dtor"
|
||||
# pragma clang diagnostic ignored "-Winconsistent-missing-override"
|
||||
#endif
|
||||
|
||||
#include "SMESH_ControlsDef.hxx"
|
||||
|
||||
#include "SMDS_BallElement.hxx"
|
||||
@@ -4765,3 +4771,7 @@ bool TMeshModifTracer::IsMeshModified()
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
@@ -26,6 +26,11 @@
|
||||
#pragma warning(disable:4786)
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Winconsistent-missing-override"
|
||||
#endif
|
||||
|
||||
#include "SMDS_FaceOfEdges.hxx"
|
||||
#include "SMDS_FaceOfNodes.hxx"
|
||||
#include "SMDS_Mesh.hxx"
|
||||
@@ -4851,3 +4856,7 @@ bool SMDS_Mesh::isCompacted()
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
@@ -360,6 +360,7 @@ struct XYZ {
|
||||
XYZ() { x = 0; y = 0; z = 0; }
|
||||
XYZ( double X, double Y, double Z ) { x = X; y = Y; z = Z; }
|
||||
XYZ( const XYZ& other ) { x = other.x; y = other.y; z = other.z; }
|
||||
XYZ& operator=( const XYZ& other ) = default;
|
||||
XYZ( const SMDS_MeshNode* n ) { x = n->X(); y = n->Y(); z = n->Z(); }
|
||||
inline XYZ operator-( const XYZ& other );
|
||||
inline XYZ operator+( const XYZ& other );
|
||||
|
||||
@@ -157,7 +157,7 @@ bool DriverMED_W_Field::Set(SMESHDS_Mesh * mesh,
|
||||
else
|
||||
_dblValues.reserve( nbElems * nbComps );
|
||||
|
||||
return nbElems * nbComps;
|
||||
return nbElems * nbComps > 0;
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
|
||||
+3
-2
@@ -45,8 +45,9 @@ MED::PrefixPrinter::~PrefixPrinter()
|
||||
{
|
||||
if(myIsActive){
|
||||
myCounter--;
|
||||
if(myCounter < 0)
|
||||
EXCEPTION(runtime_error,"PrefixPrinter::~PrefixPrinter() - myCounter("<<myCounter<<") < 0");
|
||||
//Do not throw exceptions from inside destructors
|
||||
//if(myCounter < 0)
|
||||
// EXCEPTION(runtime_error,"PrefixPrinter::~PrefixPrinter() - myCounter("<<myCounter<<") < 0");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
// Created : Mon Apr 12 16:10:22 2004
|
||||
// Author : Edward AGAPOV (eap)
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdelete-non-virtual-dtor"
|
||||
#endif
|
||||
|
||||
#include "SMESH_MeshEditor.hxx"
|
||||
|
||||
#include "SMDS_FaceOfNodes.hxx"
|
||||
@@ -12868,3 +12873,7 @@ void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
@@ -3575,7 +3575,7 @@ namespace { // Structures used by FixQuadraticElements()
|
||||
|
||||
bool Contains( const SMDS_MeshNode* node ) const { return count(node); }
|
||||
|
||||
bool IsSpoiled(const QLink* bentLink ) const;
|
||||
//bool IsSpoiled(const QLink* bentLink ) const;
|
||||
|
||||
TLinkInSet GetBoundaryLink( const TLinkSet& links,
|
||||
const TChainLink& avoidLink,
|
||||
@@ -3970,7 +3970,7 @@ namespace { // Structures used by FixQuadraticElements()
|
||||
* \brief Checks if the face is distorted due to bentLink
|
||||
*/
|
||||
//================================================================================
|
||||
|
||||
#if 0
|
||||
bool QFace::IsSpoiled(const QLink* bentLink ) const
|
||||
{
|
||||
// code is valid for convex faces only
|
||||
@@ -3995,7 +3995,7 @@ namespace { // Structures used by FixQuadraticElements()
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
//================================================================================
|
||||
/*!
|
||||
* \brief Find pairs of continues faces
|
||||
|
||||
@@ -348,7 +348,9 @@ namespace
|
||||
}
|
||||
return _surfaceInt;
|
||||
}
|
||||
#ifdef WITH_TBB
|
||||
bool IsThreadSafe(set< const Standard_Transient* >& noSafeTShapes) const;
|
||||
#endif
|
||||
};
|
||||
// --------------------------------------------------------------------------
|
||||
/*!
|
||||
@@ -1350,6 +1352,7 @@ namespace
|
||||
addIntPoint(/*toClassify=*/false);
|
||||
}
|
||||
}
|
||||
#ifdef WITH_TBB
|
||||
//================================================================================
|
||||
/*
|
||||
* check if its face can be safely intersected in a thread
|
||||
@@ -1414,6 +1417,7 @@ namespace
|
||||
}
|
||||
return isSafe;
|
||||
}
|
||||
#endif
|
||||
//================================================================================
|
||||
/*!
|
||||
* \brief Creates topology of the hexahedron
|
||||
|
||||
+506
-124
@@ -112,6 +112,7 @@
|
||||
#include "Transactions.h"
|
||||
#include <App/MaterialPy.h>
|
||||
#include <Base/GeometryPyCXX.h>
|
||||
#include "Link.h"
|
||||
|
||||
// If you stumble here, run the target "BuildExtractRevision" on Windows systems
|
||||
// or the Python script "SubWCRev.py" on Linux based systems which builds
|
||||
@@ -148,41 +149,13 @@ using namespace boost::program_options;
|
||||
# include <new>
|
||||
#endif
|
||||
|
||||
FC_LOG_LEVEL_INIT("App",true,true);
|
||||
|
||||
//using Base::GetConsole;
|
||||
using namespace Base;
|
||||
using namespace App;
|
||||
using namespace std;
|
||||
|
||||
/** Observer that watches relabeled objects and make sure that the labels inside
|
||||
* a document are unique.
|
||||
* @note In the FreeCAD design it is explicitly allowed to have duplicate labels
|
||||
* (i.e. the user visible text e.g. in the tree view) while the internal names
|
||||
* are always guaranteed to be unique.
|
||||
*/
|
||||
class ObjectLabelObserver
|
||||
{
|
||||
public:
|
||||
/// The one and only instance.
|
||||
static ObjectLabelObserver* instance();
|
||||
/// Destructs the sole instance.
|
||||
static void destruct ();
|
||||
|
||||
/** Checks the new label of the object and relabel it if needed
|
||||
* to make it unique document-wide
|
||||
*/
|
||||
void slotRelabelObject(const App::DocumentObject&, const App::Property&);
|
||||
|
||||
private:
|
||||
static ObjectLabelObserver* _singleton;
|
||||
|
||||
ObjectLabelObserver();
|
||||
~ObjectLabelObserver();
|
||||
const App::DocumentObject* current;
|
||||
ParameterGrp::handle _hPGrp;
|
||||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
// Application
|
||||
//==========================================================================
|
||||
@@ -194,6 +167,7 @@ Base::ConsoleObserverFile *Application::_pConsoleObserverFile =0;
|
||||
|
||||
AppExport std::map<std::string,std::string> Application::mConfig;
|
||||
BaseExport extern PyObject* Base::BaseExceptionFreeCADError;
|
||||
BaseExport extern PyObject* Base::BaseExceptionFreeCADAbort;
|
||||
|
||||
//**************************************************************************
|
||||
// Construction and destruction
|
||||
@@ -248,7 +222,9 @@ init_freecad_module(void)
|
||||
#endif
|
||||
|
||||
Application::Application(std::map<std::string,std::string> &mConfig)
|
||||
: _mConfig(mConfig), _pActiveDoc(0)
|
||||
: _mConfig(mConfig), _pActiveDoc(0), _isRestoring(false),_allowPartial(false)
|
||||
, _isClosingAll(false), _objCount(-1), _activeTransactionID(0)
|
||||
, _activeTransactionGuard(0), _activeTransactionTmpName(false)
|
||||
{
|
||||
//_hApp = new ApplicationOCC;
|
||||
mpcPramManager["System parameter"] = _pcSysParamMngr;
|
||||
@@ -314,6 +290,10 @@ Application::Application(std::map<std::string,std::string> &mConfig)
|
||||
Py_INCREF(Base::BaseExceptionFreeCADError);
|
||||
PyModule_AddObject(pBaseModule, "FreeCADError", Base::BaseExceptionFreeCADError);
|
||||
|
||||
Base::BaseExceptionFreeCADAbort = PyErr_NewException("Base.FreeCADAbort", PyExc_BaseException, NULL);
|
||||
Py_INCREF(Base::BaseExceptionFreeCADAbort);
|
||||
PyModule_AddObject(pBaseModule, "FreeCADAbort", Base::BaseExceptionFreeCADAbort);
|
||||
|
||||
// Python types
|
||||
Base::Interpreter().addType(&Base::VectorPy ::Type,pBaseModule,"Vector");
|
||||
Base::Interpreter().addType(&Base::MatrixPy ::Type,pBaseModule,"Matrix");
|
||||
@@ -377,6 +357,11 @@ Application::~Application()
|
||||
/// get called by the document when the name is changing
|
||||
void Application::renameDocument(const char *OldName, const char *NewName)
|
||||
{
|
||||
#if 1
|
||||
(void)OldName;
|
||||
(void)NewName;
|
||||
throw Base::RuntimeError("Renaming document internal name is no longer allowed!");
|
||||
#else
|
||||
std::map<std::string,Document*>::iterator pos;
|
||||
pos = DocMap.find(OldName);
|
||||
|
||||
@@ -390,9 +375,10 @@ void Application::renameDocument(const char *OldName, const char *NewName)
|
||||
else {
|
||||
throw Base::RuntimeError("Application::renameDocument(): no document with this name to rename!");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Document* Application::newDocument(const char * Name, const char * UserName)
|
||||
Document* Application::newDocument(const char * Name, const char * UserName, bool createView)
|
||||
{
|
||||
// get a valid name anyway!
|
||||
if (!Name || Name[0] == '\0')
|
||||
@@ -417,7 +403,7 @@ Document* Application::newDocument(const char * Name, const char * UserName)
|
||||
}
|
||||
|
||||
// create the FreeCAD document
|
||||
std::unique_ptr<Document> newDoc(new Document());
|
||||
std::unique_ptr<Document> newDoc(new Document(name.c_str()));
|
||||
|
||||
// add the document to the internal list
|
||||
DocMap[name] = newDoc.release(); // now owned by the Application
|
||||
@@ -437,11 +423,14 @@ Document* Application::newDocument(const char * Name, const char * UserName)
|
||||
_pActiveDoc->signalRedo.connect(boost::bind(&App::Application::slotRedoDocument, this, _1));
|
||||
_pActiveDoc->signalRecomputedObject.connect(boost::bind(&App::Application::slotRecomputedObject, this, _1));
|
||||
_pActiveDoc->signalRecomputed.connect(boost::bind(&App::Application::slotRecomputed, this, _1));
|
||||
_pActiveDoc->signalBeforeRecompute.connect(boost::bind(&App::Application::slotBeforeRecompute, this, _1));
|
||||
_pActiveDoc->signalOpenTransaction.connect(boost::bind(&App::Application::slotOpenTransaction, this, _1, _2));
|
||||
_pActiveDoc->signalCommitTransaction.connect(boost::bind(&App::Application::slotCommitTransaction, this, _1));
|
||||
_pActiveDoc->signalAbortTransaction.connect(boost::bind(&App::Application::slotAbortTransaction, this, _1));
|
||||
_pActiveDoc->signalStartSave.connect(boost::bind(&App::Application::slotStartSaveDocument, this, _1, _2));
|
||||
_pActiveDoc->signalFinishSave.connect(boost::bind(&App::Application::slotFinishSaveDocument, this, _1, _2));
|
||||
_pActiveDoc->signalChangePropertyEditor.connect(
|
||||
boost::bind(&App::Application::slotChangePropertyEditor, this, _1, _2));
|
||||
|
||||
// make sure that the active document is set in case no GUI is up
|
||||
{
|
||||
@@ -450,7 +439,7 @@ Document* Application::newDocument(const char * Name, const char * UserName)
|
||||
Py::Module("FreeCAD").setAttr(std::string("ActiveDocument"),active);
|
||||
}
|
||||
|
||||
signalNewDocument(*_pActiveDoc);
|
||||
signalNewDocument(*_pActiveDoc,createView);
|
||||
|
||||
// set the UserName after notifying all observers
|
||||
_pActiveDoc->Label.setValue(userName);
|
||||
@@ -476,6 +465,8 @@ bool Application::closeDocument(const char* name)
|
||||
std::unique_ptr<Document> delDoc (pos->second);
|
||||
DocMap.erase( pos );
|
||||
|
||||
_objCount = -1;
|
||||
|
||||
// Trigger observers after removing the document from the internal map.
|
||||
signalDeletedDocument();
|
||||
|
||||
@@ -484,6 +475,7 @@ bool Application::closeDocument(const char* name)
|
||||
|
||||
void Application::closeAllDocuments(void)
|
||||
{
|
||||
Base::FlagToggler<bool> flag(_isClosingAll);
|
||||
std::map<std::string,Document*>::iterator pos;
|
||||
while((pos = DocMap.begin()) != DocMap.end())
|
||||
closeDocument(pos->first.c_str());
|
||||
@@ -542,7 +534,185 @@ std::string Application::getUniqueDocumentName(const char *Name) const
|
||||
}
|
||||
}
|
||||
|
||||
Document* Application::openDocument(const char * FileName)
|
||||
int Application::addPendingDocument(const char *FileName, const char *objName, bool allowPartial)
|
||||
{
|
||||
if(!_isRestoring)
|
||||
return 0;
|
||||
if(allowPartial && _allowPartial)
|
||||
return -1;
|
||||
assert(FileName && FileName[0]);
|
||||
assert(objName && objName[0]);
|
||||
auto ret = _pendingDocMap.emplace(FileName,std::set<std::string>());
|
||||
ret.first->second.emplace(objName);
|
||||
if(ret.second) {
|
||||
_pendingDocs.push_back(ret.first->first.c_str());
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Application::isRestoring() const {
|
||||
return _isRestoring || Document::isAnyRestoring();
|
||||
}
|
||||
|
||||
bool Application::isClosingAll() const {
|
||||
return _isClosingAll;
|
||||
}
|
||||
|
||||
struct DocTiming {
|
||||
FC_DURATION_DECLARE(d1);
|
||||
FC_DURATION_DECLARE(d2);
|
||||
DocTiming() {
|
||||
FC_DURATION_INIT(d1);
|
||||
FC_DURATION_INIT(d2);
|
||||
}
|
||||
};
|
||||
|
||||
class DocOpenGuard {
|
||||
public:
|
||||
bool &flag;
|
||||
boost::signals2::signal<void ()> &signal;
|
||||
DocOpenGuard(bool &f, boost::signals2::signal<void ()> &s)
|
||||
:flag(f),signal(s)
|
||||
{
|
||||
flag = true;
|
||||
}
|
||||
~DocOpenGuard() {
|
||||
if(flag) {
|
||||
flag = false;
|
||||
signal();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Document* Application::openDocument(const char * FileName, bool createView) {
|
||||
std::vector<std::string> filenames(1,FileName);
|
||||
auto docs = openDocuments(filenames,0,0,0,createView);
|
||||
if(docs.size())
|
||||
return docs.front();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<Document*> Application::openDocuments(
|
||||
const std::vector<std::string> &filenames,
|
||||
const std::vector<std::string> *paths,
|
||||
const std::vector<std::string> *labels,
|
||||
std::vector<std::string> *errs,
|
||||
bool createView)
|
||||
{
|
||||
std::vector<Document*> res(filenames.size(),nullptr);
|
||||
if(filenames.empty())
|
||||
return res;
|
||||
|
||||
if(errs)
|
||||
errs->resize(filenames.size());
|
||||
|
||||
DocOpenGuard guard(_isRestoring,signalFinishOpenDocument);
|
||||
_pendingDocs.clear();
|
||||
_pendingDocsReopen.clear();
|
||||
_pendingDocMap.clear();
|
||||
|
||||
signalStartOpenDocument();
|
||||
|
||||
ParameterGrp::handle hGrp = GetParameterGroupByPath("User parameter:BaseApp/Preferences/Document");
|
||||
_allowPartial = !hGrp->GetBool("NoPartialLoading",false);
|
||||
|
||||
for(auto &name : filenames)
|
||||
_pendingDocs.push_back(name.c_str());
|
||||
|
||||
std::deque<std::pair<Document *, DocTiming> > newDocs;
|
||||
|
||||
FC_TIME_INIT(t);
|
||||
|
||||
for(std::size_t count=0;;++count) {
|
||||
const char *name = _pendingDocs.front();
|
||||
_pendingDocs.pop_front();
|
||||
bool isMainDoc = count<filenames.size();
|
||||
try {
|
||||
_objCount = -1;
|
||||
std::set<std::string> objNames;
|
||||
if(_allowPartial) {
|
||||
auto it = _pendingDocMap.find(name);
|
||||
if(it!=_pendingDocMap.end())
|
||||
objNames.swap(it->second);
|
||||
}
|
||||
FC_TIME_INIT(t1);
|
||||
DocTiming timing;
|
||||
const char *path = name;
|
||||
const char *label = 0;
|
||||
if(isMainDoc) {
|
||||
if(paths && paths->size()>count)
|
||||
path = (*paths)[count].c_str();
|
||||
if(labels && labels->size()>count)
|
||||
label = (*labels)[count].c_str();
|
||||
}
|
||||
auto doc = openDocumentPrivate(path,name,label,isMainDoc,createView,objNames);
|
||||
FC_DURATION_PLUS(timing.d1,t1);
|
||||
if(doc)
|
||||
newDocs.emplace_front(doc,timing);
|
||||
if(isMainDoc)
|
||||
res[count] = doc;
|
||||
_objCount = -1;
|
||||
}catch(const Base::Exception &e) {
|
||||
if(!errs && isMainDoc)
|
||||
throw;
|
||||
if(errs && isMainDoc)
|
||||
(*errs)[count] = e.what();
|
||||
else
|
||||
Console().Error("Exception opening file: %s [%s]\n", name, e.what());
|
||||
}catch(const std::exception &e) {
|
||||
if(!errs && isMainDoc)
|
||||
throw;
|
||||
if(errs && isMainDoc)
|
||||
(*errs)[count] = e.what();
|
||||
else
|
||||
Console().Error("Exception opening file: %s [%s]\n", name, e.what());
|
||||
}catch(...) {
|
||||
if(errs) {
|
||||
if(isMainDoc)
|
||||
(*errs)[count] = "unknown error";
|
||||
} else {
|
||||
_pendingDocs.clear();
|
||||
_pendingDocsReopen.clear();
|
||||
_pendingDocMap.clear();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
if(_pendingDocs.empty()) {
|
||||
if(_pendingDocsReopen.empty())
|
||||
break;
|
||||
_allowPartial = false;
|
||||
_pendingDocs.swap(_pendingDocsReopen);
|
||||
}
|
||||
}
|
||||
_pendingDocs.clear();
|
||||
_pendingDocsReopen.clear();
|
||||
_pendingDocMap.clear();
|
||||
|
||||
Base::SequencerLauncher seq("Postprocessing...", newDocs.size());
|
||||
for(auto &v : newDocs) {
|
||||
FC_TIME_INIT(t1);
|
||||
v.first->afterRestore(true);
|
||||
FC_DURATION_PLUS(v.second.d2,t1);
|
||||
seq.next();
|
||||
}
|
||||
setActiveDocument(newDocs.back().first);
|
||||
|
||||
for(auto &v : newDocs) {
|
||||
FC_DURATION_LOG(v.second.d1, v.first->getName() << " restore");
|
||||
FC_DURATION_LOG(v.second.d2, v.first->getName() << " postprocess");
|
||||
}
|
||||
FC_TIME_LOG(t,"total");
|
||||
|
||||
_isRestoring = false;
|
||||
signalFinishOpenDocument();
|
||||
return res;
|
||||
}
|
||||
|
||||
Document* Application::openDocumentPrivate(const char * FileName,
|
||||
const char *propFileName, const char *label,
|
||||
bool isMainDoc, bool createView,
|
||||
const std::set<std::string> &objNames)
|
||||
{
|
||||
FileInfo File(FileName);
|
||||
|
||||
@@ -557,23 +727,64 @@ Document* Application::openDocument(const char * FileName)
|
||||
for (std::map<std::string,Document*>::iterator it = DocMap.begin(); it != DocMap.end(); ++it) {
|
||||
// get unique path separators
|
||||
std::string fi = FileInfo(it->second->FileName.getValue()).filePath();
|
||||
if (filepath == fi) {
|
||||
std::stringstream str;
|
||||
str << "The project '" << FileName << "' is already open!";
|
||||
throw Base::FileSystemError(str.str().c_str());
|
||||
if (filepath != fi)
|
||||
continue;
|
||||
if(it->second->testStatus(App::Document::PartialDoc)
|
||||
|| it->second->testStatus(App::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
|
||||
|
||||
if(isMainDoc) {
|
||||
// Main document must be open fully, so close and reopen
|
||||
closeDocument(it->first.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
if(_allowPartial) {
|
||||
bool reopen = false;
|
||||
for(auto &name : objNames) {
|
||||
auto obj = it->second->getObject(name.c_str());
|
||||
if(!obj || obj->testStatus(App::PartialObject)) {
|
||||
reopen = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!reopen)
|
||||
return 0;
|
||||
}
|
||||
auto &names = _pendingDocMap[FileName];
|
||||
names.clear();
|
||||
_pendingDocsReopen.push_back(FileName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!isMainDoc)
|
||||
return 0;
|
||||
std::stringstream str;
|
||||
str << "The project '" << FileName << "' is already open!";
|
||||
throw Base::FileSystemError(str.str().c_str());
|
||||
}
|
||||
|
||||
std::string name;
|
||||
if(propFileName != FileName) {
|
||||
FileInfo fi(propFileName);
|
||||
name = fi.fileNamePure();
|
||||
}else
|
||||
name = File.fileNamePure();
|
||||
|
||||
// Use the same name for the internal and user name.
|
||||
// The file name is UTF-8 encoded which means that the internal name will be modified
|
||||
// to only contain valid ASCII characters but the user name will be kept.
|
||||
Document* newDoc = newDocument(File.fileNamePure().c_str(), File.fileNamePure().c_str());
|
||||
if(!label)
|
||||
label = name.c_str();
|
||||
Document* newDoc = newDocument(name.c_str(),label,isMainDoc && createView);
|
||||
|
||||
newDoc->FileName.setValue(File.filePath());
|
||||
newDoc->FileName.setValue(propFileName==FileName?File.filePath():propFileName);
|
||||
|
||||
try {
|
||||
// read the document
|
||||
newDoc->restore();
|
||||
newDoc->restore(File.filePath().c_str(),true,objNames);
|
||||
return newDoc;
|
||||
}
|
||||
// if the project file itself is corrupt then
|
||||
@@ -638,6 +849,164 @@ void Application::setActiveDocument(const char *Name)
|
||||
}
|
||||
}
|
||||
|
||||
AutoTransaction::AutoTransaction(const char *name, bool tmpName) {
|
||||
auto &app = GetApplication();
|
||||
if(name && app._activeTransactionGuard>=0) {
|
||||
if(!app.getActiveTransaction()
|
||||
|| (!tmpName && app._activeTransactionTmpName))
|
||||
{
|
||||
FC_LOG("auto transaction '" << name << "', " << tmpName);
|
||||
tid = app.setActiveTransaction(name);
|
||||
app._activeTransactionTmpName = tmpName;
|
||||
}
|
||||
}
|
||||
// We use negative transaction guard to disable auto transaction from here
|
||||
// and any stack below. This is to support user setting active transaction
|
||||
// before having any existing AutoTransaction on stack, or 'persist'
|
||||
// transaction that can out live AutoTransaction.
|
||||
if(app._activeTransactionGuard<0)
|
||||
--app._activeTransactionGuard;
|
||||
else if(tid || app._activeTransactionGuard>0)
|
||||
++app._activeTransactionGuard;
|
||||
else if(app.getActiveTransaction()) {
|
||||
FC_LOG("auto transaction disabled because of '" << app._activeTransactionName << "'");
|
||||
--app._activeTransactionGuard;
|
||||
} else
|
||||
++app._activeTransactionGuard;
|
||||
FC_TRACE("construct auto Transaction " << app._activeTransactionGuard);
|
||||
}
|
||||
|
||||
AutoTransaction::~AutoTransaction() {
|
||||
auto &app = GetApplication();
|
||||
FC_TRACE("before destruct auto Transaction " << app._activeTransactionGuard);
|
||||
if(app._activeTransactionGuard<0)
|
||||
++app._activeTransactionGuard;
|
||||
else if(!app._activeTransactionGuard) {
|
||||
#ifdef FC_DEBUG
|
||||
FC_ERR("Transaction guard error");
|
||||
#endif
|
||||
} else if(--app._activeTransactionGuard == 0) {
|
||||
try {
|
||||
// We don't call close() here, because close() only closes
|
||||
// transaction that we opened during construction time. However,
|
||||
// when _activeTransactionGuard reaches zero here, we are supposed
|
||||
// to close any transaction opened.
|
||||
app.closeActiveTransaction();
|
||||
} catch(Base::Exception &e) {
|
||||
e.ReportException();
|
||||
} catch(...)
|
||||
{}
|
||||
}
|
||||
FC_TRACE("destruct auto Transaction " << app._activeTransactionGuard);
|
||||
}
|
||||
|
||||
void AutoTransaction::close(bool abort) {
|
||||
if(tid || abort) {
|
||||
GetApplication().closeActiveTransaction(abort,abort?0:tid);
|
||||
tid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void AutoTransaction::setEnable(bool enable) {
|
||||
auto &app = GetApplication();
|
||||
if(!app._activeTransactionGuard)
|
||||
return;
|
||||
if((enable && app._activeTransactionGuard>0)
|
||||
|| (!enable && app._activeTransactionGuard<0))
|
||||
return;
|
||||
app._activeTransactionGuard = -app._activeTransactionGuard;
|
||||
FC_TRACE("toggle auto Transaction " << app._activeTransactionGuard);
|
||||
if(!enable && app._activeTransactionTmpName) {
|
||||
bool close = true;
|
||||
for(auto &v : app.DocMap) {
|
||||
if(v.second->hasPendingTransaction()) {
|
||||
close = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(close)
|
||||
app.closeActiveTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
int Application::setActiveTransaction(const char *name, bool persist) {
|
||||
if(!name || !name[0])
|
||||
name = "Command";
|
||||
|
||||
if(_activeTransactionGuard>0 && getActiveTransaction()) {
|
||||
if(_activeTransactionTmpName) {
|
||||
FC_LOG("transaction rename to '" << name << "'");
|
||||
for(auto &v : DocMap)
|
||||
v.second->renameTransaction(name,_activeTransactionID);
|
||||
} else {
|
||||
if(persist)
|
||||
AutoTransaction::setEnable(false);
|
||||
return 0;
|
||||
}
|
||||
}else{
|
||||
FC_LOG("set active transaction '" << name << "'");
|
||||
_activeTransactionID = 0;
|
||||
for(auto &v : DocMap)
|
||||
v.second->_commitTransaction();
|
||||
_activeTransactionID = Transaction::getNewID();
|
||||
}
|
||||
_activeTransactionTmpName = false;
|
||||
_activeTransactionName = name;
|
||||
if(persist)
|
||||
AutoTransaction::setEnable(false);
|
||||
return _activeTransactionID;
|
||||
}
|
||||
|
||||
const char *Application::getActiveTransaction(int *id) const {
|
||||
int tid = 0;
|
||||
if(Transaction::getLastID() == _activeTransactionID)
|
||||
tid = _activeTransactionID;
|
||||
if(id) *id = tid;
|
||||
return tid?_activeTransactionName.c_str():0;
|
||||
}
|
||||
|
||||
void Application::closeActiveTransaction(bool abort, int id) {
|
||||
if(!id) id = _activeTransactionID;
|
||||
if(!id) return;
|
||||
|
||||
if(_activeTransactionGuard>0 && !abort) {
|
||||
FC_LOG("ignore close transaction");
|
||||
return;
|
||||
}
|
||||
|
||||
FC_LOG("close transaction '" << _activeTransactionName << "' " << abort);
|
||||
_activeTransactionID = 0;
|
||||
|
||||
TransactionSignaller siganller(abort,false);
|
||||
for(auto &v : DocMap) {
|
||||
if(v.second->getTransactionID(true) != id)
|
||||
continue;
|
||||
if(abort)
|
||||
v.second->_abortTransaction();
|
||||
else
|
||||
v.second->_commitTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
static int _TransSignalCount;
|
||||
static bool _TransSignalled;
|
||||
Application::TransactionSignaller::TransactionSignaller(bool abort, bool signal)
|
||||
:abort(abort)
|
||||
{
|
||||
++_TransSignalCount;
|
||||
if(signal && !_TransSignalled) {
|
||||
_TransSignalled = true;
|
||||
GetApplication().signalBeforeCloseTransaction(abort);
|
||||
}
|
||||
}
|
||||
|
||||
Application::TransactionSignaller::~TransactionSignaller() {
|
||||
if(--_TransSignalCount == 0 && _TransSignalled) {
|
||||
_TransSignalled = false;
|
||||
GetApplication().signalCloseTransaction(abort);
|
||||
}
|
||||
}
|
||||
|
||||
const char* Application::getHomePath(void) const
|
||||
{
|
||||
return _mConfig["AppHomePath"].c_str();
|
||||
@@ -699,6 +1068,51 @@ std::string Application::getHelpDir()
|
||||
#endif
|
||||
}
|
||||
|
||||
int Application::checkLinkDepth(int depth, bool no_throw) {
|
||||
if(_objCount<0) {
|
||||
_objCount = 0;
|
||||
for(auto &v : DocMap)
|
||||
_objCount += v.second->countObjects();
|
||||
}
|
||||
if(depth > _objCount+2) {
|
||||
const char *msg = "Link recursion limit reached. "
|
||||
"Please check for cyclic reference.";
|
||||
if(no_throw) {
|
||||
FC_ERR(msg);
|
||||
return 0;
|
||||
}else
|
||||
throw Base::RuntimeError(msg);
|
||||
}
|
||||
return _objCount+2;
|
||||
}
|
||||
|
||||
std::set<DocumentObject *> Application::getLinksTo(
|
||||
const DocumentObject *obj, int options, int maxCount) const
|
||||
{
|
||||
std::set<DocumentObject *> links;
|
||||
if(!obj) {
|
||||
for(auto &v : DocMap) {
|
||||
v.second->getLinksTo(links,obj,options,maxCount);
|
||||
if(maxCount && (int)links.size()>=maxCount)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
std::set<Document*> docs;
|
||||
for(auto o : obj->getInList()) {
|
||||
if(o && o->getNameInDocument() && docs.insert(o->getDocument()).second) {
|
||||
o->getDocument()->getLinksTo(links,obj,options,maxCount);
|
||||
if(maxCount && (int)links.size()>=maxCount)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return links;
|
||||
}
|
||||
|
||||
bool Application::hasLinksTo(const DocumentObject *obj) const {
|
||||
return !getLinksTo(obj,0,1).empty();
|
||||
}
|
||||
|
||||
ParameterManager & Application::GetSystemParameter(void)
|
||||
{
|
||||
return *_pcSysParamMngr;
|
||||
@@ -1004,11 +1418,13 @@ void Application::slotChangedDocument(const App::Document& doc, const Property&
|
||||
void Application::slotNewObject(const App::DocumentObject&O)
|
||||
{
|
||||
this->signalNewObject(O);
|
||||
_objCount = -1;
|
||||
}
|
||||
|
||||
void Application::slotDeletedObject(const App::DocumentObject&O)
|
||||
{
|
||||
this->signalDeletedObject(O);
|
||||
_objCount = -1;
|
||||
}
|
||||
|
||||
void Application::slotBeforeChangeObject(const DocumentObject& O, const Property& Prop)
|
||||
@@ -1051,6 +1467,11 @@ void Application::slotRecomputed(const Document& doc)
|
||||
this->signalRecomputed(doc);
|
||||
}
|
||||
|
||||
void Application::slotBeforeRecompute(const Document& doc)
|
||||
{
|
||||
this->signalBeforeRecomputeDocument(doc);
|
||||
}
|
||||
|
||||
void Application::slotOpenTransaction(const Document& d, string s)
|
||||
{
|
||||
this->signalOpenTransaction(d, s);
|
||||
@@ -1076,6 +1497,11 @@ void Application::slotFinishSaveDocument(const App::Document& doc, const std::st
|
||||
this->signalFinishSaveDocument(doc, filename);
|
||||
}
|
||||
|
||||
void Application::slotChangePropertyEditor(const App::Document &doc, const App::Property &prop)
|
||||
{
|
||||
this->signalChangePropertyEditor(doc,prop);
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// Init, Destruct and singleton
|
||||
|
||||
@@ -1260,11 +1686,11 @@ void my_se_translator_filter(unsigned int code, EXCEPTION_POINTERS* pExp)
|
||||
{
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
throw Base::AccessViolation();
|
||||
break;
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
throw Base::DivisionByZeroError("Division by zero!");
|
||||
break;
|
||||
//throw Base::DivisionByZeroError("Division by zero!");
|
||||
Base::Console().Error("SEH exception (%u): Division by zero\n", code);
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream str;
|
||||
@@ -1319,6 +1745,7 @@ void Application::initTypes(void)
|
||||
Base::Type ::init();
|
||||
Base::BaseClass ::init();
|
||||
Base::Exception ::init();
|
||||
Base::AbortException ::init();
|
||||
Base::Persistence ::init();
|
||||
|
||||
// Complex data classes
|
||||
@@ -1354,21 +1781,32 @@ void Application::initTypes(void)
|
||||
App ::PropertyIntegerSet ::init();
|
||||
App ::PropertyMap ::init();
|
||||
App ::PropertyString ::init();
|
||||
App ::PropertyPersistentObject ::init();
|
||||
App ::PropertyUUID ::init();
|
||||
App ::PropertyFont ::init();
|
||||
App ::PropertyStringList ::init();
|
||||
App ::PropertyLinkBase ::init();
|
||||
App ::PropertyLinkListBase ::init();
|
||||
App ::PropertyLink ::init();
|
||||
App ::PropertyLinkChild ::init();
|
||||
App ::PropertyLinkGlobal ::init();
|
||||
App ::PropertyLinkHidden ::init();
|
||||
App ::PropertyLinkSub ::init();
|
||||
App ::PropertyLinkSubChild ::init();
|
||||
App ::PropertyLinkSubGlobal ::init();
|
||||
App ::PropertyLinkSubHidden ::init();
|
||||
App ::PropertyLinkList ::init();
|
||||
App ::PropertyLinkListChild ::init();
|
||||
App ::PropertyLinkListGlobal ::init();
|
||||
App ::PropertyLinkListHidden ::init();
|
||||
App ::PropertyLinkSubList ::init();
|
||||
App ::PropertyLinkSubListChild ::init();
|
||||
App ::PropertyLinkSubListGlobal ::init();
|
||||
App ::PropertyLinkSubListHidden ::init();
|
||||
App ::PropertyXLink ::init();
|
||||
App ::PropertyXLinkSub ::init();
|
||||
App ::PropertyXLinkSubList ::init();
|
||||
App ::PropertyXLinkContainer ::init();
|
||||
App ::PropertyMatrix ::init();
|
||||
App ::PropertyVector ::init();
|
||||
App ::PropertyVectorDistance ::init();
|
||||
@@ -1388,6 +1826,7 @@ void Application::initTypes(void)
|
||||
App ::PropertyFile ::init();
|
||||
App ::PropertyFileIncluded ::init();
|
||||
App ::PropertyPythonObject ::init();
|
||||
App ::PropertyExpressionContainer ::init();
|
||||
App ::PropertyExpressionEngine ::init();
|
||||
|
||||
// Extension classes
|
||||
@@ -1400,6 +1839,10 @@ void Application::initTypes(void)
|
||||
App ::GeoFeatureGroupExtensionPython::init();
|
||||
App ::OriginGroupExtension ::init();
|
||||
App ::OriginGroupExtensionPython ::init();
|
||||
App ::LinkBaseExtension ::init();
|
||||
App ::LinkBaseExtensionPython ::init();
|
||||
App ::LinkExtension ::init();
|
||||
App ::LinkExtensionPython ::init();
|
||||
|
||||
// Document classes
|
||||
App ::TransactionalObject ::init();
|
||||
@@ -1422,11 +1865,18 @@ void Application::initTypes(void)
|
||||
App ::MaterialObjectPython ::init();
|
||||
App ::TextDocument ::init();
|
||||
App ::Placement ::init();
|
||||
App ::PlacementPython ::init();
|
||||
App ::OriginFeature ::init();
|
||||
App ::Plane ::init();
|
||||
App ::Line ::init();
|
||||
App ::Part ::init();
|
||||
App ::Origin ::init();
|
||||
App ::Link ::init();
|
||||
App ::LinkPython ::init();
|
||||
App ::LinkElement ::init();
|
||||
App ::LinkElementPython ::init();
|
||||
App ::LinkGroup ::init();
|
||||
App ::LinkGroupPython ::init();
|
||||
|
||||
// Expression classes
|
||||
App ::Expression ::init();
|
||||
@@ -1440,6 +1890,7 @@ void Application::initTypes(void)
|
||||
App ::FunctionExpression ::init();
|
||||
App ::BooleanExpression ::init();
|
||||
App ::RangeExpression ::init();
|
||||
App ::PyObjectExpression ::init();
|
||||
|
||||
// register transaction type
|
||||
new App::TransactionProducer<TransactionDocumentObject>
|
||||
@@ -1461,6 +1912,8 @@ void Application::initTypes(void)
|
||||
new ExceptionProducer<Base::TypeError>;
|
||||
new ExceptionProducer<Base::ValueError>;
|
||||
new ExceptionProducer<Base::IndexError>;
|
||||
new ExceptionProducer<Base::NameError>;
|
||||
new ExceptionProducer<Base::ImportError>;
|
||||
new ExceptionProducer<Base::AttributeError>;
|
||||
new ExceptionProducer<Base::RuntimeError>;
|
||||
new ExceptionProducer<Base::BadGraphError>;
|
||||
@@ -1529,15 +1982,21 @@ void Application::initConfig(int argc, char ** argv)
|
||||
PyImport_AppendInittab ("FreeCAD", init_freecad_module);
|
||||
PyImport_AppendInittab ("__FreeCADBase__", init_freecad_base_module);
|
||||
#endif
|
||||
mConfig["PythonSearchPath"] = Interpreter().init(argc,argv);
|
||||
const char* pythonpath = Interpreter().init(argc,argv);
|
||||
if (pythonpath)
|
||||
mConfig["PythonSearchPath"] = pythonpath;
|
||||
else
|
||||
Base::Console().Warning("Encoding of Python paths failed\n");
|
||||
|
||||
// Parse the options that have impact on the init process
|
||||
ParseOptions(argc,argv);
|
||||
|
||||
// Init console ===========================================================
|
||||
Base::PyGILStateLocker lock;
|
||||
_pConsoleObserverStd = new ConsoleObserverStd();
|
||||
Console().AttachObserver(_pConsoleObserverStd);
|
||||
if (mConfig["LoggingConsole"] == "1") {
|
||||
_pConsoleObserverStd = new ConsoleObserverStd();
|
||||
Console().AttachObserver(_pConsoleObserverStd);
|
||||
}
|
||||
if (mConfig["Verbose"] == "Strict")
|
||||
Console().UnsetConsoleMode(ConsoleSingleton::Verbose);
|
||||
|
||||
@@ -1675,7 +2134,6 @@ void Application::initApplication(void)
|
||||
try {
|
||||
Interpreter().runString(Base::ScriptFactory().ProduceScript("CMakeVariables"));
|
||||
Interpreter().runString(Base::ScriptFactory().ProduceScript("FreeCADInit"));
|
||||
ObjectLabelObserver::instance();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
e.ReportException();
|
||||
@@ -2622,79 +3080,3 @@ std::string Application::FindHomePath(const char* sCall)
|
||||
# error "std::string Application::FindHomePath(const char*) not implemented"
|
||||
#endif
|
||||
|
||||
ObjectLabelObserver* ObjectLabelObserver::_singleton = 0;
|
||||
|
||||
ObjectLabelObserver* ObjectLabelObserver::instance()
|
||||
{
|
||||
if (!_singleton)
|
||||
_singleton = new ObjectLabelObserver;
|
||||
return _singleton;
|
||||
}
|
||||
|
||||
void ObjectLabelObserver::destruct ()
|
||||
{
|
||||
delete _singleton;
|
||||
_singleton = 0;
|
||||
}
|
||||
|
||||
void ObjectLabelObserver::slotRelabelObject(const App::DocumentObject& obj, const App::Property& prop)
|
||||
{
|
||||
// observe only the Label property
|
||||
if (&prop == &obj.Label) {
|
||||
// have we processed this (or another?) object right now?
|
||||
if (current) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string label = obj.Label.getValue();
|
||||
App::Document* doc = obj.getDocument();
|
||||
if (doc && !_hPGrp->GetBool("DuplicateLabels")) {
|
||||
std::vector<std::string> objectLabels;
|
||||
std::vector<App::DocumentObject*>::const_iterator it;
|
||||
std::vector<App::DocumentObject*> objs = doc->getObjects();
|
||||
bool match = false;
|
||||
|
||||
for (it = objs.begin();it != objs.end();++it) {
|
||||
if (*it == &obj)
|
||||
continue; // don't compare object with itself
|
||||
std::string objLabel = (*it)->Label.getValue();
|
||||
if (!match && objLabel == label)
|
||||
match = true;
|
||||
objectLabels.push_back(objLabel);
|
||||
}
|
||||
|
||||
// make sure that there is a name conflict otherwise we don't have to do anything
|
||||
if (match && !label.empty()) {
|
||||
// remove number from end to avoid lengthy names
|
||||
size_t lastpos = label.length()-1;
|
||||
while (label[lastpos] >= 48 && label[lastpos] <= 57) {
|
||||
// if 'lastpos' becomes 0 then all characters are digits. In this case we use
|
||||
// the complete label again
|
||||
if (lastpos == 0) {
|
||||
lastpos = label.length()-1;
|
||||
break;
|
||||
}
|
||||
lastpos--;
|
||||
}
|
||||
|
||||
label = label.substr(0, lastpos+1);
|
||||
label = Base::Tools::getUniqueName(label, objectLabels, 3);
|
||||
this->current = &obj;
|
||||
const_cast<App::DocumentObject&>(obj).Label.setValue(label);
|
||||
this->current = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ObjectLabelObserver::ObjectLabelObserver() : current(0)
|
||||
{
|
||||
App::GetApplication().signalChangedObject.connect(boost::bind
|
||||
(&ObjectLabelObserver::slotRelabelObject, this, _1, _2));
|
||||
_hPGrp = App::GetApplication().GetUserParameter().GetGroup("BaseApp");
|
||||
_hPGrp = _hPGrp->GetGroup("Preferences")->GetGroup("Document");
|
||||
}
|
||||
|
||||
ObjectLabelObserver::~ObjectLabelObserver()
|
||||
{
|
||||
}
|
||||
|
||||
+218
-6
@@ -28,6 +28,7 @@
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include <Base/PyObjectBase.h>
|
||||
#include <Base/Parameter.h>
|
||||
@@ -47,7 +48,18 @@ class Document;
|
||||
class DocumentObject;
|
||||
class ApplicationObserver;
|
||||
class Property;
|
||||
class AutoTransaction;
|
||||
|
||||
enum GetLinkOption {
|
||||
/// Get all links (both directly and in directly) linked to the given object
|
||||
GetLinkRecursive = 1,
|
||||
/// Get link array element instead of the array
|
||||
GetLinkArrayElement = 2,
|
||||
/// Get linked object instead of the link, no effect if GetLinkRecursive
|
||||
GetLinkedObject = 4,
|
||||
/// Get only external links, no effect if GetLinkRecursive
|
||||
GetLinkExternal = 8,
|
||||
};
|
||||
|
||||
|
||||
/** The Application
|
||||
@@ -74,13 +86,35 @@ public:
|
||||
* The second name is a UTF8 name of any kind. It's that name normally shown to
|
||||
* the user and stored in the App::Document::Name property.
|
||||
*/
|
||||
App::Document* newDocument(const char * Name=0l, const char * UserName=0l);
|
||||
App::Document* newDocument(const char * Name=0l, const char * UserName=0l, bool createView=true);
|
||||
/// Closes the document \a name and removes it from the application.
|
||||
bool closeDocument(const char* name);
|
||||
/// find a unique document name
|
||||
std::string getUniqueDocumentName(const char *Name) const;
|
||||
/// Open an existing document from a file
|
||||
App::Document* openDocument(const char * FileName=0l);
|
||||
App::Document* openDocument(const char * FileName=0l, bool createView=true);
|
||||
/** Open multiple documents
|
||||
*
|
||||
* @param filenames: input file names
|
||||
* @param paths: optional input file path in case it is different from
|
||||
* filenames (mainly used during recovery).
|
||||
* @param labels: optional label assign to document (mainly used during recovery).
|
||||
* @param errs: optional output error message corresponding to each input
|
||||
* file name. If errs is given, this function will catch all
|
||||
* Base::Exception and save the error message inside. Otherwise, it will
|
||||
* throw on exception when opening the input files.
|
||||
* @param createView: whether to signal Gui module to create view on restore.
|
||||
*
|
||||
* @return Return opened document object corresponding to each input file
|
||||
* name, which maybe NULL if failed.
|
||||
*
|
||||
* This function will also open any external referenced files.
|
||||
*/
|
||||
std::vector<Document*> openDocuments(const std::vector<std::string> &filenames,
|
||||
const std::vector<std::string> *paths=0,
|
||||
const std::vector<std::string> *labels=0,
|
||||
std::vector<std::string> *errs=0,
|
||||
bool createView = true);
|
||||
/// Retrieve the active document
|
||||
App::Document* getActiveDocument(void) const;
|
||||
/// Retrieve a named document
|
||||
@@ -94,12 +128,51 @@ public:
|
||||
void setActiveDocument(const char *Name);
|
||||
/// close all documents (without saving)
|
||||
void closeAllDocuments(void);
|
||||
/// Add pending document to open together with the current opening document
|
||||
int addPendingDocument(const char *FileName, const char *objName, bool allowPartial);
|
||||
/// Indicate whether the application is opening (restoring) some document
|
||||
bool isRestoring() const;
|
||||
/// Indicate the application is closing all document
|
||||
bool isClosingAll() const;
|
||||
//@}
|
||||
|
||||
/** @name Application-wide trandaction setting */
|
||||
//@{
|
||||
/** Setup a pending application-wide active transaction
|
||||
*
|
||||
* @param name: new transaction name
|
||||
* @param persist: by default, if the calling code is inside any invocation
|
||||
* of a command, it will be auto closed once all command within the current
|
||||
* stack exists. To disable auto closing, set persist=true
|
||||
*
|
||||
* @return The new transaction ID.
|
||||
*
|
||||
* Call this function to setup an application-wide transaction. All current
|
||||
* pending transactions of opening documents will be committed first.
|
||||
* However, no new transaction is created by this call. Any subsequent
|
||||
* changes in any current opening document will auto create a transaction
|
||||
* with the given name and ID. If more than one document is changed, the
|
||||
* transactions will share the same ID, and will be undo/redo together.
|
||||
*/
|
||||
int setActiveTransaction(const char *name, bool persist=false);
|
||||
/// Return the current active transaction name and ID
|
||||
const char *getActiveTransaction(int *tid=0) const;
|
||||
/** Commit/abort current active transactions
|
||||
*
|
||||
* @param abort: whether to abort or commit the transactions
|
||||
*
|
||||
* Bsides calling this function directly, it will be called by automatically
|
||||
* if 1) any new transaction is created with a different ID, or 2) any
|
||||
* transaction with the current active transaction ID is either committed or
|
||||
* aborted
|
||||
*/
|
||||
void closeActiveTransaction(bool abort=false, int id=0);
|
||||
//@}
|
||||
|
||||
/** @name Signals of the Application */
|
||||
//@{
|
||||
/// signal on new Document
|
||||
boost::signals2::signal<void (const Document&)> signalNewDocument;
|
||||
boost::signals2::signal<void (const Document&, bool)> signalNewDocument;
|
||||
/// signal on document getting deleted
|
||||
boost::signals2::signal<void (const Document&)> signalDeleteDocument;
|
||||
/// signal on already deleted Document
|
||||
@@ -122,8 +195,22 @@ public:
|
||||
boost::signals2::signal<void (const Document&, const std::string&)> signalFinishSaveDocument;
|
||||
/// signal on undo in document
|
||||
boost::signals2::signal<void (const Document&)> signalUndoDocument;
|
||||
/// signal on application wide undo
|
||||
boost::signals2::signal<void ()> signalUndo;
|
||||
/// signal on redo in document
|
||||
boost::signals2::signal<void (const Document&)> signalRedoDocument;
|
||||
/// signal on application wide redo
|
||||
boost::signals2::signal<void ()> signalRedo;
|
||||
/// signal before close/abort active transaction
|
||||
boost::signals2::signal<void (bool)> signalBeforeCloseTransaction;
|
||||
/// signal after close/abort active transaction
|
||||
boost::signals2::signal<void (bool)> signalCloseTransaction;
|
||||
/// signal on show hidden items
|
||||
boost::signals2::signal<void (const Document&)> signalShowHidden;
|
||||
/// signal on start opening document(s)
|
||||
boost::signals2::signal<void ()> signalStartOpenDocument;
|
||||
/// signal on finished opening document(s)
|
||||
boost::signals2::signal<void ()> signalFinishOpenDocument;
|
||||
//@}
|
||||
|
||||
|
||||
@@ -149,6 +236,8 @@ public:
|
||||
boost::signals2::signal<void (const App::DocumentObject&)> signalRelabelObject;
|
||||
/// signal on activated Object
|
||||
boost::signals2::signal<void (const App::DocumentObject&)> signalActivatedObject;
|
||||
/// signal before recomputed document
|
||||
boost::signals2::signal<void (const App::Document&)> signalBeforeRecomputeDocument;
|
||||
/// signal on recomputed document
|
||||
boost::signals2::signal<void (const App::Document&)> signalRecomputed;
|
||||
/// signal on recomputed document object
|
||||
@@ -171,7 +260,7 @@ public:
|
||||
/// signal on about removing a dynamic property
|
||||
boost::signals2::signal<void (const App::Property&)> signalRemoveDynamicProperty;
|
||||
/// signal on about changing the editor mode of a property
|
||||
boost::signals2::signal<void (const App::Property&)> signalChangePropertyEditor;
|
||||
boost::signals2::signal<void (const App::Document&, const App::Property&)> signalChangePropertyEditor;
|
||||
//@}
|
||||
|
||||
|
||||
@@ -274,6 +363,34 @@ public:
|
||||
static std::string getHelpDir();
|
||||
//@}
|
||||
|
||||
/** @name Link handling */
|
||||
//@{
|
||||
|
||||
/** Check for link recursion depth
|
||||
*
|
||||
* @param depth: current depth
|
||||
* @param no_throw: whether to throw exception
|
||||
*
|
||||
* @return Return the maximum remaining depth.
|
||||
*
|
||||
* The function uses an internal count of all objects in all documents as
|
||||
* the limit of recursion depth.
|
||||
*/
|
||||
int checkLinkDepth(int depth, bool no_throw=true);
|
||||
|
||||
/** Return the links to a given object
|
||||
*
|
||||
* @param obj: the linked object. If NULL, then all links are returned.
|
||||
* @param option: @sa App::GetLinkOptions
|
||||
* @param maxCount: limit the number of links returned, 0 means no limit
|
||||
*/
|
||||
std::set<DocumentObject*> getLinksTo(
|
||||
const DocumentObject *, int options, int maxCount=0) const;
|
||||
|
||||
/// Check if there is any link to the given object
|
||||
bool hasLinksTo(const DocumentObject *obj) const;
|
||||
//@}
|
||||
|
||||
friend class App::Document;
|
||||
|
||||
protected:
|
||||
@@ -296,13 +413,28 @@ protected:
|
||||
void slotRedoDocument(const App::Document&);
|
||||
void slotRecomputedObject(const App::DocumentObject&);
|
||||
void slotRecomputed(const App::Document&);
|
||||
void slotBeforeRecompute(const App::Document&);
|
||||
void slotOpenTransaction(const App::Document&, std::string);
|
||||
void slotCommitTransaction(const App::Document&);
|
||||
void slotAbortTransaction(const App::Document&);
|
||||
void slotStartSaveDocument(const App::Document&, const std::string&);
|
||||
void slotFinishSaveDocument(const App::Document&, const std::string&);
|
||||
void slotChangePropertyEditor(const App::Document&, const App::Property &);
|
||||
//@}
|
||||
|
||||
/// open single document only
|
||||
App::Document* openDocumentPrivate(const char * FileName, const char *propFileName,
|
||||
const char *label, bool isMainDoc, bool createView, const std::set<std::string> &objNames);
|
||||
|
||||
/// Helper class for App::Document to signal on close/abort transaction
|
||||
class AppExport TransactionSignaller {
|
||||
public:
|
||||
TransactionSignaller(bool abort,bool signal);
|
||||
~TransactionSignaller();
|
||||
private:
|
||||
bool abort;
|
||||
};
|
||||
|
||||
private:
|
||||
/// Constructor
|
||||
Application(std::map<std::string,std::string> &mConfig);
|
||||
@@ -315,7 +447,6 @@ private:
|
||||
static ParameterManager *_pcUserParamMngr;
|
||||
//@}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// python exports goes here +++++++++++++++++++++++++++++++++++++++++++
|
||||
//---------------------------------------------------------------------
|
||||
@@ -349,11 +480,21 @@ private:
|
||||
static PyObject* sListDocuments (PyObject *self,PyObject *args);
|
||||
static PyObject* sAddDocObserver (PyObject *self,PyObject *args);
|
||||
static PyObject* sRemoveDocObserver (PyObject *self,PyObject *args);
|
||||
static PyObject *sIsRestoring (PyObject *self,PyObject *args);
|
||||
|
||||
static PyObject *sSetLogLevel (PyObject *self,PyObject *args);
|
||||
static PyObject *sGetLogLevel (PyObject *self,PyObject *args);
|
||||
|
||||
static PyMethodDef Methods[];
|
||||
static PyObject *sCheckLinkDepth (PyObject *self,PyObject *args);
|
||||
static PyObject *sGetLinksTo (PyObject *self,PyObject *args);
|
||||
|
||||
static PyObject *sGetDependentObjects(PyObject *self,PyObject *args);
|
||||
|
||||
static PyObject *sSetActiveTransaction (PyObject *self,PyObject *args);
|
||||
static PyObject *sGetActiveTransaction (PyObject *self,PyObject *args);
|
||||
static PyObject *sCloseActiveTransaction(PyObject *self,PyObject *args);
|
||||
static PyObject *sCheckAbort(PyObject *self,PyObject *args);
|
||||
static PyMethodDef Methods[];
|
||||
|
||||
friend class ApplicationObserver;
|
||||
|
||||
@@ -401,6 +542,23 @@ private:
|
||||
std::map<std::string,std::string> &_mConfig;
|
||||
App::Document* _pActiveDoc;
|
||||
|
||||
std::deque<const char *> _pendingDocs;
|
||||
std::deque<const char *> _pendingDocsReopen;
|
||||
std::map<std::string,std::set<std::string> > _pendingDocMap;
|
||||
bool _isRestoring;
|
||||
bool _allowPartial;
|
||||
bool _isClosingAll;
|
||||
|
||||
// for estimate max link depth
|
||||
int _objCount;
|
||||
|
||||
friend class AutoTransaction;
|
||||
|
||||
std::string _activeTransactionName;
|
||||
int _activeTransactionID;
|
||||
int _activeTransactionGuard;
|
||||
bool _activeTransactionTmpName;
|
||||
|
||||
static Base::ConsoleObserverStd *_pConsoleObserverStd;
|
||||
static Base::ConsoleObserverFile *_pConsoleObserverFile;
|
||||
};
|
||||
@@ -410,6 +568,60 @@ inline App::Application &GetApplication(void){
|
||||
return *App::Application::_pcSingleton;
|
||||
}
|
||||
|
||||
/// Helper class to manager transaction (i.e. undo/redo)
|
||||
class AppExport AutoTransaction {
|
||||
private:
|
||||
/// Private new operator to prevent heap allocation
|
||||
void* operator new(size_t size);
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
*
|
||||
* @param name: optional new transaction name on construction
|
||||
* @param tmpName: if true and a new transaction is setup, the name given is
|
||||
* considered as temporary, and subsequent construction of this class (or
|
||||
* calling Application::setActiveTransaction()) can override the transaction
|
||||
* name.
|
||||
*
|
||||
* The constructor increments an internal counter
|
||||
* (Application::_activeTransactionGuard). The counter prevents any new
|
||||
* active transaction being setup. It also prevents close (i.e. commits) the
|
||||
* current active transaction until it reaches zero. It does not have any
|
||||
* effect on aborting transaction, though.
|
||||
*/
|
||||
AutoTransaction(const char *name=0, bool tmpName=false);
|
||||
|
||||
/** Destructor
|
||||
*
|
||||
* This destructor decrease an internal counter
|
||||
* (Application::_activeTransactionGuard), and will commit any current
|
||||
* active transaction when the counter reaches zero.
|
||||
*/
|
||||
~AutoTransaction();
|
||||
|
||||
/** Close or abort the transaction
|
||||
*
|
||||
* This function can be used to explicitly close (i.e. commit) the
|
||||
* transaction, if the current transaction ID matches the one created inside
|
||||
* the constructor. For aborting, it will abort any current transaction
|
||||
*/
|
||||
void close(bool abort=false);
|
||||
|
||||
/** Enable/Disable any AutoTransaction instance in the current stack
|
||||
*
|
||||
* Once disabled, any empty temporary named transaction is closed. If there
|
||||
* are non-empty or non-temperary named active transaction, it will not be
|
||||
* auto closed.
|
||||
*
|
||||
* This function may be used in, for example, Gui::Document::setEdit() to
|
||||
* allow a transaction live past any command scope.
|
||||
*/
|
||||
static void setEnable(bool enable);
|
||||
|
||||
private:
|
||||
int tid = 0;
|
||||
};
|
||||
|
||||
} // namespace App
|
||||
|
||||
|
||||
|
||||
+177
-9
@@ -35,6 +35,7 @@
|
||||
#include "Document.h"
|
||||
#include "DocumentPy.h"
|
||||
#include "DocumentObserverPython.h"
|
||||
#include "DocumentObjectPy.h"
|
||||
|
||||
// FreeCAD Base header
|
||||
#include <Base/Interpreter.h>
|
||||
@@ -44,6 +45,7 @@
|
||||
#include <Base/Factory.h>
|
||||
#include <Base/FileInfo.h>
|
||||
#include <Base/UnitsApi.h>
|
||||
#include <Base/Sequencer.h>
|
||||
|
||||
//using Base::GetConsole;
|
||||
using namespace Base;
|
||||
@@ -131,8 +133,8 @@ PyMethodDef Application::Methods[] = {
|
||||
"Get a document by its name or raise an exception\n"
|
||||
"if there is no document with the given name."},
|
||||
{"listDocuments", (PyCFunction) Application::sListDocuments, METH_VARARGS,
|
||||
"listDocuments() -> list\n\n"
|
||||
"Return a list of names of all documents."},
|
||||
"listDocuments(sort=False) -> list\n\n"
|
||||
"Return a list of names of all documents, optionally sort in dependency order."},
|
||||
{"addDocumentObserver", (PyCFunction) Application::sAddDocObserver, METH_VARARGS,
|
||||
"addDocumentObserver() -> None\n\n"
|
||||
"Add an observer to get notified about changes on documents."},
|
||||
@@ -144,7 +146,39 @@ PyMethodDef Application::Methods[] = {
|
||||
"'level' can either be string 'Log', 'Msg', 'Wrn', 'Error', or an integer value"},
|
||||
{"getLogLevel", (PyCFunction) Application::sGetLogLevel, METH_VARARGS,
|
||||
"getLogLevel(tag) -- Get the log level of a string tag"},
|
||||
|
||||
{"checkLinkDepth", (PyCFunction) Application::sCheckLinkDepth, METH_VARARGS,
|
||||
"checkLinkDepth(depth) -- check link recursion depth"},
|
||||
{"getLinksTo", (PyCFunction) Application::sGetLinksTo, METH_VARARGS,
|
||||
"getLinksTo(obj,options=0,maxCount=0) -- return the objects linked to 'obj'\n\n"
|
||||
"options: 1: recursive, 2: check link array. Options can combine.\n"
|
||||
"maxCount: to limit the number of links returned\n"},
|
||||
{"getDependentObjects", (PyCFunction) Application::sGetDependentObjects, METH_VARARGS,
|
||||
"getDependentObjects(obj|[obj,...], options=0)\n"
|
||||
"Return a list of dependent objects including the given objects.\n\n"
|
||||
"options: can have the following bit flags,\n"
|
||||
" 1: to sort the list in topological order.\n"
|
||||
" 2: to exclude dependency of Link type object."},
|
||||
{"setActiveTransaction", (PyCFunction) Application::sSetActiveTransaction, METH_VARARGS,
|
||||
"setActiveTransaction(name, persist=False) -- setup active transaction with the given name\n\n"
|
||||
"name: the transaction name\n"
|
||||
"persist(False): by default, if the calling code is inside any invocation of a command, it\n"
|
||||
" will be auto closed once all commands within the current stack exists. To\n"
|
||||
" disable auto closing, set persist=True\n"
|
||||
"Returns the transaction ID for the active transaction. An application-wide\n"
|
||||
"active transaction causes any document changes to open a transaction with\n"
|
||||
"the given name and ID."},
|
||||
{"getActiveTransaction", (PyCFunction) Application::sGetActiveTransaction, METH_VARARGS,
|
||||
"getActiveTransaction() -> (name,id) return the current active transaction name and ID"},
|
||||
{"closeActiveTransaction", (PyCFunction) Application::sCloseActiveTransaction, METH_VARARGS,
|
||||
"closeActiveTransaction(abort=False) -- commit or abort current active transaction"},
|
||||
{"isRestoring", (PyCFunction) Application::sIsRestoring, METH_VARARGS,
|
||||
"isRestoring() -> Bool -- Test if the application is opening some document"},
|
||||
{"checkAbort", (PyCFunction) Application::sCheckAbort, METH_VARARGS,
|
||||
"checkAbort() -- check for user abort in length operation.\n\n"
|
||||
"This only works if there is an active sequencer (or ProgressIndicator in Python).\n"
|
||||
"There is an active sequencer during document restore and recomputation. User may\n"
|
||||
"abort the operation by pressing the ESC key. Once detected, this function will\n"
|
||||
"trigger a BaseExceptionFreeCADAbort exception."},
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
@@ -194,6 +228,12 @@ PyObject* Application::sLoadFile(PyObject * /*self*/, PyObject *args)
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* Application::sIsRestoring(PyObject * /*self*/, PyObject *args) {
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
return Py::new_reference_to(Py::Boolean(GetApplication().isRestoring()));
|
||||
}
|
||||
|
||||
PyObject* Application::sOpenDocument(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
char* Name;
|
||||
@@ -626,22 +666,26 @@ PyObject* Application::sGetHomePath(PyObject * /*self*/, PyObject *args)
|
||||
|
||||
PyObject* Application::sListDocuments(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) // convert args: Python->C
|
||||
PyObject *sort = Py_False;
|
||||
if (!PyArg_ParseTuple(args, "|O",&sort)) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
PY_TRY {
|
||||
PyObject *pDict = PyDict_New();
|
||||
PyObject *pKey;
|
||||
Base::PyObjectBase* pValue;
|
||||
|
||||
for (std::map<std::string,Document*>::const_iterator It = GetApplication().DocMap.begin();
|
||||
It != GetApplication().DocMap.end();++It) {
|
||||
std::vector<Document*> docs = GetApplication().getDocuments();;
|
||||
if(PyObject_IsTrue(sort))
|
||||
docs = Document::getDependentDocuments(docs,true);
|
||||
|
||||
for (auto doc : docs) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
pKey = PyUnicode_FromString(It->first.c_str());
|
||||
pKey = PyUnicode_FromString(doc->getName());
|
||||
#else
|
||||
pKey = PyString_FromString(It->first.c_str());
|
||||
pKey = PyString_FromString(doc->getName());
|
||||
#endif
|
||||
// GetPyObject() increments
|
||||
pValue = static_cast<Base::PyObjectBase*>(It->second->getPyObject());
|
||||
pValue = static_cast<Base::PyObjectBase*>(doc->getPyObject());
|
||||
PyDict_SetItem(pDict, pKey, pValue);
|
||||
// now we can decrement again as PyDict_SetItem also has incremented
|
||||
pValue->DecRef();
|
||||
@@ -763,4 +807,128 @@ PyObject *Application::sGetLogLevel(PyObject * /*self*/, PyObject *args)
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject *Application::sCheckLinkDepth(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
short depth = 0;
|
||||
if (!PyArg_ParseTuple(args, "h", &depth))
|
||||
return NULL;
|
||||
|
||||
PY_TRY {
|
||||
return Py::new_reference_to(Py::Int(GetApplication().checkLinkDepth(depth,false)));
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject *Application::sGetLinksTo(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyobj = Py_None;
|
||||
int options = 0;
|
||||
short count = 0;
|
||||
if (!PyArg_ParseTuple(args, "|Oih",&pyobj,&options, &count))
|
||||
return NULL;
|
||||
|
||||
PY_TRY {
|
||||
DocumentObject *obj = 0;
|
||||
if(pyobj!=Py_None) {
|
||||
if(!PyObject_TypeCheck(pyobj,&DocumentObjectPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expect the first argument of type document object");
|
||||
return 0;
|
||||
}
|
||||
obj = static_cast<DocumentObjectPy*>(pyobj)->getDocumentObjectPtr();
|
||||
}
|
||||
auto links = GetApplication().getLinksTo(obj,options,count);
|
||||
Py::Tuple ret(links.size());
|
||||
int i=0;
|
||||
for(auto o : links)
|
||||
ret.setItem(i++,Py::Object(o->getPyObject(),true));
|
||||
return Py::new_reference_to(ret);
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject *Application::sGetDependentObjects(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *obj;
|
||||
int options = 0;
|
||||
if (!PyArg_ParseTuple(args, "O|i", &obj,&options))
|
||||
return 0;
|
||||
|
||||
std::vector<App::DocumentObject*> objs;
|
||||
if(PySequence_Check(obj)) {
|
||||
Py::Sequence seq(obj);
|
||||
for(size_t i=0;i<seq.size();++i) {
|
||||
if(!PyObject_TypeCheck(seq[i].ptr(),&DocumentObjectPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expect element in sequence to be of type document object");
|
||||
return 0;
|
||||
}
|
||||
objs.push_back(static_cast<DocumentObjectPy*>(seq[i].ptr())->getDocumentObjectPtr());
|
||||
}
|
||||
}else if(!PyObject_TypeCheck(obj,&DocumentObjectPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Expect first argument to be either a document object or sequence of document objects");
|
||||
return 0;
|
||||
}else
|
||||
objs.push_back(static_cast<DocumentObjectPy*>(obj)->getDocumentObjectPtr());
|
||||
|
||||
PY_TRY {
|
||||
auto ret = App::Document::getDependencyList(objs,options);
|
||||
|
||||
Py::Tuple tuple(ret.size());
|
||||
for(size_t i=0;i<ret.size();++i)
|
||||
tuple.setItem(i,Py::Object(ret[i]->getPyObject(),true));
|
||||
return Py::new_reference_to(tuple);
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
|
||||
PyObject *Application::sSetActiveTransaction(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
char *name;
|
||||
PyObject *persist = Py_False;
|
||||
if (!PyArg_ParseTuple(args, "s|O", &name,&persist))
|
||||
return 0;
|
||||
|
||||
PY_TRY {
|
||||
Py::Int ret(GetApplication().setActiveTransaction(name,PyObject_IsTrue(persist)));
|
||||
return Py::new_reference_to(ret);
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject *Application::sGetActiveTransaction(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
return 0;
|
||||
|
||||
PY_TRY {
|
||||
int id = 0;
|
||||
const char *name = GetApplication().getActiveTransaction(&id);
|
||||
if(!name || id<=0)
|
||||
Py_Return;
|
||||
Py::Tuple ret(2);
|
||||
ret.setItem(0,Py::String(name));
|
||||
ret.setItem(1,Py::Int(id));
|
||||
return Py::new_reference_to(ret);
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject *Application::sCloseActiveTransaction(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *abort = Py_False;
|
||||
int id = 0;
|
||||
if (!PyArg_ParseTuple(args, "|Oi", &abort,&id))
|
||||
return 0;
|
||||
|
||||
PY_TRY {
|
||||
GetApplication().closeActiveTransaction(PyObject_IsTrue(abort),id);
|
||||
Py_Return;
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject *Application::sCheckAbort(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
return 0;
|
||||
|
||||
PY_TRY {
|
||||
Base::Sequencer().checkAbort();
|
||||
Py_Return;
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ generate_from_xml(ExtensionPy)
|
||||
generate_from_xml(ExtensionContainerPy)
|
||||
generate_from_xml(DocumentObjectExtensionPy)
|
||||
generate_from_xml(GroupExtensionPy)
|
||||
generate_from_xml(LinkBaseExtensionPy)
|
||||
generate_from_xml(DocumentObjectGroupPy)
|
||||
generate_from_xml(GeoFeaturePy)
|
||||
generate_from_xml(GeoFeatureGroupExtensionPy)
|
||||
@@ -108,6 +109,7 @@ SET(FreeCADApp_XML_SRCS
|
||||
ExtensionContainerPy.xml
|
||||
DocumentObjectExtensionPy.xml
|
||||
GroupExtensionPy.xml
|
||||
LinkBaseExtensionPy.xml
|
||||
DocumentObjectGroupPy.xml
|
||||
DocumentObjectPy.xml
|
||||
GeoFeaturePy.xml
|
||||
@@ -165,6 +167,8 @@ SET(Document_CPP_SRCS
|
||||
MaterialObject.cpp
|
||||
MergeDocuments.cpp
|
||||
TextDocument.cpp
|
||||
Link.cpp
|
||||
LinkBaseExtensionPyImp.cpp
|
||||
)
|
||||
|
||||
SET(Document_HPP_SRCS
|
||||
@@ -203,6 +207,7 @@ SET(Document_HPP_SRCS
|
||||
MaterialObject.h
|
||||
MergeDocuments.h
|
||||
TextDocument.h
|
||||
Link.h
|
||||
)
|
||||
SET(Document_SRCS
|
||||
${Document_CPP_SRCS}
|
||||
@@ -288,11 +293,13 @@ add_library(FreeCADApp SHARED ${FreeCADApp_SRCS})
|
||||
|
||||
target_link_libraries(FreeCADApp ${FreeCADApp_LIBS})
|
||||
|
||||
add_dependencies(FreeCADApp fc_version)
|
||||
|
||||
SET_BIN_DIR(FreeCADApp FreeCADApp)
|
||||
|
||||
if(WIN32)
|
||||
INSTALL(TARGETS FreeCADApp
|
||||
RUNTIME DESTINATION bin
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
else(WIN32)
|
||||
|
||||
@@ -27,7 +27,10 @@
|
||||
# include <cstdlib>
|
||||
#endif
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Console.h>
|
||||
#include "ComplexGeoData.h"
|
||||
|
||||
using namespace Data;
|
||||
@@ -39,6 +42,7 @@ TYPESYSTEM_SOURCE_ABSTRACT(Data::ComplexGeoData , Base::Persistence);
|
||||
|
||||
|
||||
ComplexGeoData::ComplexGeoData(void)
|
||||
:Tag(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -153,3 +157,97 @@ bool ComplexGeoData::getCenterOfGravity(Base::Vector3d&) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string &ComplexGeoData::elementMapPrefix() {
|
||||
static std::string prefix(";");
|
||||
return prefix;
|
||||
}
|
||||
|
||||
const char *ComplexGeoData::isMappedElement(const char *name) {
|
||||
if(name && boost::starts_with(name,elementMapPrefix()))
|
||||
return name+elementMapPrefix().size();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string ComplexGeoData::newElementName(const char *name) {
|
||||
if(!name) return std::string();
|
||||
const char *dot = strrchr(name,'.');
|
||||
if(!dot || dot==name) return name;
|
||||
const char *c = dot-1;
|
||||
for(;c!=name;--c) {
|
||||
if(*c == '.') {
|
||||
++c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isMappedElement(c))
|
||||
return std::string(name,dot-name);
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string ComplexGeoData::oldElementName(const char *name) {
|
||||
if(!name) return std::string();
|
||||
const char *dot = strrchr(name,'.');
|
||||
if(!dot || dot==name) return name;
|
||||
const char *c = dot-1;
|
||||
for(;c!=name;--c) {
|
||||
if(*c == '.') {
|
||||
++c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isMappedElement(c))
|
||||
return std::string(name,c-name)+(dot+1);
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string ComplexGeoData::noElementName(const char *name) {
|
||||
auto element = findElementName(name);
|
||||
if(element)
|
||||
return std::string(name,element-name);
|
||||
return name;
|
||||
}
|
||||
|
||||
const char *ComplexGeoData::findElementName(const char *subname) {
|
||||
if(!subname || !subname[0] || isMappedElement(subname))
|
||||
return subname;
|
||||
const char *dot = strrchr(subname,'.');
|
||||
if(!dot)
|
||||
return subname;
|
||||
const char *element = dot+1;
|
||||
if(dot==subname || isMappedElement(element))
|
||||
return element;
|
||||
for(--dot;dot!=subname;--dot) {
|
||||
if(*dot == '.') {
|
||||
++dot;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isMappedElement(dot))
|
||||
return dot;
|
||||
return element;
|
||||
}
|
||||
|
||||
const std::string &ComplexGeoData::tagPostfix() {
|
||||
static std::string postfix(elementMapPrefix() + ":T");
|
||||
return postfix;
|
||||
}
|
||||
|
||||
const std::string &ComplexGeoData::indexPostfix() {
|
||||
static std::string postfix(elementMapPrefix() + ":I");
|
||||
return postfix;
|
||||
}
|
||||
|
||||
const std::string &ComplexGeoData::missingPrefix() {
|
||||
static std::string prefix("?");
|
||||
return prefix;
|
||||
}
|
||||
|
||||
bool ComplexGeoData::hasMissingElement(const char *subname) {
|
||||
if(!subname)
|
||||
return false;
|
||||
auto dot = strrchr(subname,'.');
|
||||
if(dot)
|
||||
subname = dot+1;
|
||||
return boost::starts_with(subname,missingPrefix());
|
||||
}
|
||||
|
||||
@@ -156,6 +156,41 @@ public:
|
||||
virtual bool getCenterOfGravity(Base::Vector3d& center) const;
|
||||
//@}
|
||||
|
||||
/** @name Element name mapping */
|
||||
//@{
|
||||
/// Special prefix to mark the beginning of a mapped sub-element name
|
||||
static const std::string &elementMapPrefix();
|
||||
/// Special postfix to mark the following tag
|
||||
static const std::string &tagPostfix();
|
||||
/// Speical postfix to mark the index of an array element
|
||||
static const std::string &indexPostfix();
|
||||
/// Speical prefix to mark a missing element
|
||||
static const std::string &missingPrefix();
|
||||
/// Check if a subname contains missing element
|
||||
static bool hasMissingElement(const char *subname);
|
||||
/** Check if the name starts with elementMapPrefix()
|
||||
*
|
||||
* @param name: input name
|
||||
* @return Returns the name stripped with elementMapPrefix(), or 0 if not
|
||||
* start with the prefix
|
||||
*/
|
||||
static const char *isMappedElement(const char *name);
|
||||
|
||||
/// Strip out the trailing element name if there is mapped element name precedes it.
|
||||
static std::string newElementName(const char *name);
|
||||
/// Strip out the mapped element name if there is one.
|
||||
static std::string oldElementName(const char *name);
|
||||
/// Strip out the old and new element name if there is one.
|
||||
static std::string noElementName(const char *name);
|
||||
|
||||
/// Find the start of an element name in a subname
|
||||
static const char *findElementName(const char *subname);
|
||||
|
||||
static inline const char *hasMappedElementName(const char *subname) {
|
||||
return isMappedElement(findElementName(subname));
|
||||
}
|
||||
//@}
|
||||
|
||||
protected:
|
||||
|
||||
/// from local to outside
|
||||
@@ -171,6 +206,8 @@ protected:
|
||||
Base::Vector3d tmp = tmpM * vec;
|
||||
return Base::Vector3f((float)tmp.x,(float)tmp.y,(float)tmp.z);
|
||||
}
|
||||
public:
|
||||
mutable long Tag;
|
||||
};
|
||||
|
||||
} //namespace App
|
||||
|
||||
@@ -37,5 +37,11 @@
|
||||
</Documentation>
|
||||
<Parameter Name="Matrix" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="Tag">
|
||||
<Documentation>
|
||||
<UserDocu>Geometry Tag</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Tag" Type="Int"/>
|
||||
</Attribute>
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
|
||||
@@ -126,6 +126,14 @@ void ComplexGeoDataPy::setMatrix(Py::Object arg)
|
||||
}
|
||||
}
|
||||
|
||||
Py::Int ComplexGeoDataPy::getTag() const {
|
||||
return Py::Int(getComplexGeoDataPtr()->Tag);
|
||||
}
|
||||
|
||||
void ComplexGeoDataPy::setTag(Py::Int tag) {
|
||||
getComplexGeoDataPtr()->Tag = tag;
|
||||
}
|
||||
|
||||
PyObject *ComplexGeoDataPy::getCustomAttributes(const char* /*attr*/) const
|
||||
{
|
||||
return 0;
|
||||
|
||||
+1465
-514
File diff suppressed because it is too large
Load Diff
+184
-43
@@ -60,7 +60,7 @@ namespace App
|
||||
/// The document class
|
||||
class AppExport Document : public App::PropertyContainer
|
||||
{
|
||||
PROPERTY_HEADER(App::Document);
|
||||
PROPERTY_HEADER_WITH_OVERRIDE(App::Document);
|
||||
|
||||
public:
|
||||
enum Status {
|
||||
@@ -69,7 +69,10 @@ public:
|
||||
Closable = 2,
|
||||
Restoring = 3,
|
||||
Recomputing = 4,
|
||||
PartialRestore = 5
|
||||
PartialRestore = 5,
|
||||
Importing = 6,
|
||||
PartialDoc = 7,
|
||||
AllowPartialRecompute = 8, // allow recomputing editing object if SkipRecompute is set
|
||||
};
|
||||
|
||||
/** @name Properties */
|
||||
@@ -109,6 +112,8 @@ public:
|
||||
PropertyLink Tip;
|
||||
/// Tip object of the document (if any)
|
||||
PropertyString TipName;
|
||||
/// Whether to show hidden items in TreeView
|
||||
PropertyBool ShowHidden;
|
||||
//@}
|
||||
|
||||
/** @name Signals of the document */
|
||||
@@ -126,6 +131,8 @@ public:
|
||||
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalBeforeChangeObject;
|
||||
/// signal on changed Object
|
||||
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalChangedObject;
|
||||
/// signal on manually called DocumentObject::touch()
|
||||
boost::signals2::signal<void (const App::DocumentObject&)> signalTouchedObject;
|
||||
/// signal on relabeled Object
|
||||
boost::signals2::signal<void (const App::DocumentObject&)> signalRelabelObject;
|
||||
/// signal on activated Object
|
||||
@@ -153,11 +160,13 @@ public:
|
||||
Base::XMLReader&)> signalImportObjects;
|
||||
boost::signals2::signal<void (const std::vector<App::DocumentObject*>&, Base::Reader&,
|
||||
const std::map<std::string, std::string>&)> signalImportViewObjects;
|
||||
boost::signals2::signal<void (const std::vector<App::DocumentObject*>&)> signalFinishImportObjects;
|
||||
//signal starting a save action to a file
|
||||
boost::signals2::signal<void (const App::Document&, const std::string&)> signalStartSave;
|
||||
//signal finishing a save action to a file
|
||||
boost::signals2::signal<void (const App::Document&, const std::string&)> signalFinishSave;
|
||||
boost::signals2::signal<void (const App::Document&)> signalRecomputed;
|
||||
boost::signals2::signal<void (const App::Document&)> signalBeforeRecompute;
|
||||
boost::signals2::signal<void (const App::Document&, const std::vector<App::DocumentObject*>&)> signalRecomputed;
|
||||
boost::signals2::signal<void (const App::DocumentObject&)> signalRecomputedObject;
|
||||
//signal a new opened transaction
|
||||
boost::signals2::signal<void (const App::Document&, std::string)> signalOpenTransaction;
|
||||
@@ -165,6 +174,9 @@ public:
|
||||
boost::signals2::signal<void (const App::Document&)> signalCommitTransaction;
|
||||
// signal an aborted transaction
|
||||
boost::signals2::signal<void (const App::Document&)> signalAbortTransaction;
|
||||
boost::signals2::signal<void (const App::Document&, const std::vector<App::DocumentObject*>&)> signalSkipRecompute;
|
||||
boost::signals2::signal<void (const App::DocumentObject&)> signalFinishRestoreObject;
|
||||
boost::signals2::signal<void (const App::Document&,const App::Property&)> signalChangePropertyEditor;
|
||||
//@}
|
||||
|
||||
/** @name File handling of the document */
|
||||
@@ -176,10 +188,32 @@ public:
|
||||
bool saveAs(const char* file);
|
||||
bool saveCopy(const char* file) const;
|
||||
/// Restore the document from the file in Property Path
|
||||
void restore (void);
|
||||
void restore (const char *filename=0,
|
||||
bool delaySignal=false, const std::set<std::string> &objNames={});
|
||||
void afterRestore(bool checkPartial=false);
|
||||
bool afterRestore(const std::vector<App::DocumentObject *> &, bool checkPartial=false);
|
||||
enum ExportStatus {
|
||||
NotExporting,
|
||||
Exporting,
|
||||
};
|
||||
ExportStatus isExporting(const App::DocumentObject *obj) const;
|
||||
void exportObjects(const std::vector<App::DocumentObject*>&, std::ostream&);
|
||||
void exportGraphviz(std::ostream&) const;
|
||||
std::vector<App::DocumentObject*> importObjects(Base::XMLReader& reader);
|
||||
/** Import any externally linked objects
|
||||
*
|
||||
* @param objs: input list of objects. Only objects belonging to this document will
|
||||
* be checked for external links. And all found external linked object will be imported
|
||||
* to this document. Link type properties of those input objects will be automatically
|
||||
* reassigned to the imported objects. Note that the link properties of other objects
|
||||
* in the document but not included in the input list, will not be affected even if they
|
||||
* point to some object beining imported. To import all objects, simply pass in all objects
|
||||
* of this document.
|
||||
*
|
||||
* @return the list of imported objects
|
||||
*/
|
||||
std::vector<App::DocumentObject*> importLinks(
|
||||
const std::vector<App::DocumentObject*> &objs = {});
|
||||
/// Opens the document from its file name
|
||||
//void open (void);
|
||||
/// Is the document already saved to a file?
|
||||
@@ -188,11 +222,11 @@ public:
|
||||
const char* getName() const;
|
||||
//@}
|
||||
|
||||
virtual void Save (Base::Writer &writer) const;
|
||||
virtual void Restore(Base::XMLReader &reader);
|
||||
virtual void Save (Base::Writer &writer) const override;
|
||||
virtual void Restore(Base::XMLReader &reader) override;
|
||||
|
||||
/// returns the complete document memory consumption, including all managed DocObjects and Undo Redo.
|
||||
unsigned int getMemSize (void) const;
|
||||
unsigned int getMemSize (void) const override;
|
||||
|
||||
/** @name Object handling */
|
||||
//@{
|
||||
@@ -201,8 +235,11 @@ public:
|
||||
* @param sType the type of created object
|
||||
* @param pObjectName if nonNULL use that name otherwise generate a new unique name based on the \a sType
|
||||
* @param isNew if false don't call the \c DocumentObject::setupObject() callback (default is true)
|
||||
* @param viewType override object's view provider name
|
||||
* @param isPartial indicate if this object is meant to be partially loaded
|
||||
*/
|
||||
DocumentObject *addObject(const char* sType, const char* pObjectName=0, bool isNew=true);
|
||||
DocumentObject *addObject(const char* sType, const char* pObjectName=0,
|
||||
bool isNew=true, const char *viewType=0, bool isPartial=false);
|
||||
/** Add an array of features of the given types and names.
|
||||
* Unicode names are set through the Label property.
|
||||
* @param sType The type of created object
|
||||
@@ -222,12 +259,15 @@ public:
|
||||
void addObject(DocumentObject*, const char* pObjectName=0);
|
||||
|
||||
|
||||
/** Copy an object from another document to this document
|
||||
* If \a recursive is true then all objects this object depends on
|
||||
* are copied as well. By default \a recursive is false.
|
||||
* Returns the copy of the object or 0 if the creation failed.
|
||||
/** Copy objects from another document to this document
|
||||
*
|
||||
* @param recursive: if true, then all objects this object depends on are
|
||||
* copied as well. By default \a recursive is false.
|
||||
*
|
||||
* @return Returns the list of objects copied.
|
||||
*/
|
||||
DocumentObject* copyObject(DocumentObject* obj, bool recursive=false);
|
||||
std::vector<DocumentObject*> copyObject(
|
||||
const std::vector<DocumentObject*> &objs, bool recursive=false);
|
||||
/** Move an object from another document to this document
|
||||
* If \a recursive is true then all objects this object depends on
|
||||
* are moved as well. By default \a recursive is false.
|
||||
@@ -239,6 +279,8 @@ public:
|
||||
DocumentObject *getActiveObject(void) const;
|
||||
/// Returns a Object of this document
|
||||
DocumentObject *getObject(const char *Name) const;
|
||||
/// Returns a Object of this document by its id
|
||||
DocumentObject *getObjectByID(long id) const;
|
||||
/// Returns true if the DocumentObject is contained in this document
|
||||
bool isIn(const DocumentObject *pFeat) const;
|
||||
/// Returns a Name of an Object or 0
|
||||
@@ -247,8 +289,10 @@ public:
|
||||
std::string getUniqueObjectName(const char *Name) const;
|
||||
/// Returns a name of the form prefix_number. d specifies the number of digits.
|
||||
std::string getStandardObjectName(const char *Name, int d) const;
|
||||
/// Returns a list of document's objects including the dependencies
|
||||
std::vector<DocumentObject*> getDependingObjects() const;
|
||||
/// Returns a list of all Objects
|
||||
std::vector<DocumentObject*> getObjects() const;
|
||||
const std::vector<DocumentObject*> &getObjects() const;
|
||||
std::vector<DocumentObject*> getObjectsOfType(const Base::Type& typeId) 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, bool derived = true) const;
|
||||
@@ -267,18 +311,23 @@ public:
|
||||
void purgeTouched();
|
||||
/// check if there is any touched object in this document
|
||||
bool isTouched(void) const;
|
||||
/// check if there is any object must execute in this document
|
||||
bool mustExecute(void) const;
|
||||
/// returns all touched objects
|
||||
std::vector<App::DocumentObject *> getTouched(void) const;
|
||||
/// set the document to be closable, this is on by default.
|
||||
void setClosable(bool);
|
||||
/// check whether the document can be closed
|
||||
bool isClosable() const;
|
||||
/// Recompute all touched features and return the number of recalculated features
|
||||
int recompute();
|
||||
/** Recompute touched features and return the number of recalculated features
|
||||
*
|
||||
* @param objs: specify a sub set of objects to recompute. If empty, then
|
||||
* all object in this document is checked for recompute
|
||||
*/
|
||||
int recompute(const std::vector<App::DocumentObject*> &objs={},
|
||||
bool force=false,bool *hasError=0, int options=0);
|
||||
/// Recompute only one feature
|
||||
void recomputeFeature(DocumentObject* Feat);
|
||||
/// get the error log from the recompute run
|
||||
const std::vector<App::DocumentObjectExecReturn*> &getRecomputeLog(void)const{return _RecomputeLog;}
|
||||
bool recomputeFeature(DocumentObject* Feat,bool recursive=false);
|
||||
/// get the text of the error of a specified object
|
||||
const char* getErrorDescription(const App::DocumentObject*) const;
|
||||
/// return the status bits
|
||||
@@ -288,7 +337,22 @@ public:
|
||||
//@}
|
||||
|
||||
|
||||
/** @name methods for the UNDO REDO and Transaction handling */
|
||||
/** @name methods for the UNDO REDO and Transaction handling
|
||||
*
|
||||
* Introduce a new concept of transaction ID. Each transaction must be
|
||||
* unique inside the document. Multiple transactions from different
|
||||
* documents can be grouped together with the same transaction ID.
|
||||
*
|
||||
* When undo, Gui component can query getAvailableUndo(id) to see if it is
|
||||
* possible to undo with a given ID. If there more than one undo
|
||||
* transactions, meaning that there are other transactions before the given
|
||||
* ID. The Gui component shall ask user if he wants to undo multiple steps.
|
||||
* And if the user agrees, call undo(id) to unroll all transaction before
|
||||
* and including the the one with the give ID. Same applies for redo.
|
||||
*
|
||||
* The new transaction ID describe here is fully backward compatible.
|
||||
* Calling the APIs with a default id=0 gives the original behavior.
|
||||
*/
|
||||
//@{
|
||||
/// switch the level of Undo/Redo
|
||||
void setUndoMode(int iMode);
|
||||
@@ -296,14 +360,25 @@ public:
|
||||
int getUndoMode(void) const;
|
||||
/// switch the transaction mode
|
||||
void setTransactionMode(int iMode);
|
||||
/// Open a new command Undo/Redo, an UTF-8 name can be specified
|
||||
/** Open a new command Undo/Redo, an UTF-8 name can be specified
|
||||
*
|
||||
* @param name: transaction name
|
||||
*
|
||||
* This function calls App::Application::setActiveTransaction(name) instead
|
||||
* to setup a potential transaction which will only be created if there is
|
||||
* actual changes.
|
||||
*/
|
||||
void openTransaction(const char* name=0);
|
||||
// Commit the Command transaction. Do nothing If there is no Command transaction open.
|
||||
/// Rename the current transaction if the id matches
|
||||
void renameTransaction(const char *name, int id);
|
||||
/// Commit the Command transaction. Do nothing If there is no Command transaction open.
|
||||
void commitTransaction();
|
||||
/// Abort the actually running transaction.
|
||||
void abortTransaction();
|
||||
/// Check if a transaction is open
|
||||
bool hasPendingTransaction() const;
|
||||
/// Return the undo/redo transaction ID starting from the back
|
||||
int getTransactionID(bool undo, unsigned pos=0) const;
|
||||
/// Check if a transaction is open and its list is empty.
|
||||
/// If no transaction is open true is returned.
|
||||
bool isTransactionEmpty() const;
|
||||
@@ -318,21 +393,21 @@ public:
|
||||
/// Remove all stored Undos and Redos
|
||||
void clearUndos();
|
||||
/// Returns the number of stored Undos. If greater than 0 Undo will be effective.
|
||||
int getAvailableUndos() const;
|
||||
int getAvailableUndos(int id=0) const;
|
||||
/// Returns a list of the Undo names
|
||||
std::vector<std::string> getAvailableUndoNames() const;
|
||||
/// Will UNDO one step, returns False if no undo was done (Undos == 0).
|
||||
bool undo();
|
||||
bool undo(int id=0);
|
||||
/// Returns the number of stored Redos. If greater than 0 Redo will be effective.
|
||||
int getAvailableRedos() const;
|
||||
int getAvailableRedos(int id=0) const;
|
||||
/// Returns a list of the Redo names.
|
||||
std::vector<std::string> getAvailableRedoNames() const;
|
||||
/// Will REDO one step, returns False if no redo was done (Redos == 0).
|
||||
bool redo() ;
|
||||
bool redo(int id=0) ;
|
||||
/// returns true if the document is in an Transaction phase, e.g. currently performing a redo/undo or rollback
|
||||
bool isPerformingTransaction() const;
|
||||
/// \internal remove property from a transactional object with name \a name
|
||||
void removePropertyOfObject(TransactionalObject*, const char*);
|
||||
/// \internal add or remove property from a transactional object
|
||||
void addOrRemovePropertyOfObject(TransactionalObject*, Property *prop, bool add);
|
||||
//@}
|
||||
|
||||
/** @name dependency stuff */
|
||||
@@ -343,11 +418,31 @@ public:
|
||||
bool checkOnCycle(void);
|
||||
/// get a list of all objects linking to the given object
|
||||
std::vector<App::DocumentObject*> getInList(const DocumentObject* me) const;
|
||||
/// Get a complete list of all objects the given objects depend on. The list
|
||||
/// also contains the given objects!
|
||||
/// deprecated! Use In- and OutList mimic in the DocumentObject instead!
|
||||
std::vector<App::DocumentObject*> getDependencyList
|
||||
(const std::vector<App::DocumentObject*>&) const;
|
||||
|
||||
/// Option bit flags used by getDepenencyList()
|
||||
enum DependencyOption {
|
||||
/// Return topological sorted list
|
||||
DepSort = 1,
|
||||
/// Do no include object linked by PropertyXLink, as it can handle external link
|
||||
DepNoXLinked = 2,
|
||||
/// Raise exception on cycles
|
||||
DepNoCycle = 4,
|
||||
};
|
||||
/** Get a complete list of all objects the given objects depend on.
|
||||
*
|
||||
* This function is defined as static because it accpets objects from
|
||||
* different documents, and the returned list will contain dependent
|
||||
* objects from all relevant documents
|
||||
*
|
||||
* @param objs: input objects to query for dependency.
|
||||
* @param options: See DependencyOption
|
||||
*/
|
||||
static std::vector<App::DocumentObject*> getDependencyList(
|
||||
const std::vector<App::DocumentObject*> &objs, int options=0);
|
||||
|
||||
std::vector<App::Document*> getDependentDocuments(bool sort=true);
|
||||
static std::vector<App::Document*> getDependentDocuments(std::vector<App::Document*> docs, bool sort);
|
||||
|
||||
// set Changed
|
||||
//void setChanged(DocumentObject* change);
|
||||
/// get a list of topological sorted objects (https://en.wikipedia.org/wiki/Topological_sorting)
|
||||
@@ -359,10 +454,36 @@ public:
|
||||
(const App::DocumentObject* from, const App::DocumentObject* to) const;
|
||||
//@}
|
||||
|
||||
/** Return the links to a given object
|
||||
*
|
||||
* @param links: holds the links found
|
||||
* @param obj: the linked object. If NULL, then all links are returned.
|
||||
* @param option: @sa App::GetLinkOptions
|
||||
* @param maxCount: limit the number of links returned, 0 means no limit
|
||||
* @param objs: optional objects to search for, if empty, then all objects
|
||||
* of this document are searched.
|
||||
*/
|
||||
void getLinksTo(std::set<DocumentObject*> &links,
|
||||
const DocumentObject *obj, int options, int maxCount=0,
|
||||
const std::vector<DocumentObject*> &objs = {}) const;
|
||||
|
||||
/// Check if there is any link to the given object
|
||||
bool hasLinksTo(const DocumentObject *obj) const;
|
||||
|
||||
/// Called by objects during restore to ask for recompute
|
||||
void addRecomputeObject(DocumentObject *obj);
|
||||
|
||||
const std::string &getOldLabel() const {return oldLabel;}
|
||||
|
||||
/// Function called to signal that an object identifier has been renamed
|
||||
void renameObjectIdentifiers(const std::map<App::ObjectIdentifier, App::ObjectIdentifier> & paths, const std::function<bool(const App::DocumentObject*)> &selector = [](const App::DocumentObject *) { return true; });
|
||||
|
||||
virtual PyObject *getPyObject(void);
|
||||
virtual PyObject *getPyObject(void) override;
|
||||
|
||||
virtual std::string getFullName() const override;
|
||||
|
||||
/// Indicate if there is any document restoring/importing
|
||||
static bool isAnyRestoring();
|
||||
|
||||
friend class Application;
|
||||
/// because of transaction handling
|
||||
@@ -376,43 +497,63 @@ public:
|
||||
|
||||
protected:
|
||||
/// Construction
|
||||
Document(void);
|
||||
Document(const char *name = "");
|
||||
|
||||
void _removeObject(DocumentObject* pcObject);
|
||||
void _addObject(DocumentObject* pcObject, const char* pObjectName);
|
||||
/// checks if a valid transaction is open
|
||||
void _checkTransaction(DocumentObject* pcObject);
|
||||
void _checkTransaction(DocumentObject* pcDelObj, const Property *What, int line);
|
||||
void breakDependency(DocumentObject* pcObject, bool clear);
|
||||
std::vector<App::DocumentObject*> readObjects(Base::XMLReader& reader);
|
||||
void writeObjects(const std::vector<App::DocumentObject*>&, Base::Writer &writer) const;
|
||||
bool saveToFile(const char* filename) const;
|
||||
|
||||
void onBeforeChange(const Property* prop);
|
||||
void onChanged(const Property* prop);
|
||||
void onBeforeChange(const Property* prop) override;
|
||||
void onChanged(const Property* prop) override;
|
||||
/// callback from the Document objects before property will be changed
|
||||
void onBeforeChangeProperty(const TransactionalObject *Who, const Property *What);
|
||||
/// callback from the Document objects after property was changed
|
||||
void onChangedProperty(const DocumentObject *Who, const Property *What);
|
||||
/// helper which Recompute only this feature
|
||||
/// @return True if the recompute process of the Document shall be stopped, False if it shall be continued.
|
||||
bool _recomputeFeature(DocumentObject* Feat);
|
||||
/// @return 0 if succeeded, 1 if failed, -1 if aborted by user.
|
||||
int _recomputeFeature(DocumentObject* Feat);
|
||||
void _clearRedos();
|
||||
|
||||
/// refresh the internal dependency graph
|
||||
void _rebuildDependencyList(void);
|
||||
void _rebuildDependencyList(
|
||||
const std::vector<App::DocumentObject*> &objs = std::vector<App::DocumentObject*>());
|
||||
|
||||
std::string getTransientDirectoryName(const std::string& uuid, const std::string& filename) const;
|
||||
|
||||
/** Open a new command Undo/Redo, an UTF-8 name can be specified
|
||||
*
|
||||
* @param name: transaction name
|
||||
* @param id: transaction ID, if 0 then the ID is auto generated.
|
||||
*
|
||||
* @return: Return the ID of the new transaction.
|
||||
*
|
||||
* This function creates an actual transaction regardless of Application
|
||||
* AutoTransaction setting.
|
||||
*/
|
||||
int _openTransaction(const char* name=0, int id=0);
|
||||
/// Internally called by App::Application to commit the Command transaction.
|
||||
void _commitTransaction(bool notify=false);
|
||||
/// Internally called by App::Application to abort the running transaction.
|
||||
void _abortTransaction();
|
||||
|
||||
private:
|
||||
// # Data Member of the document +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
std::list<Transaction*> mUndoTransactions;
|
||||
std::map<int,Transaction*> mUndoMap;
|
||||
std::list<Transaction*> mRedoTransactions;
|
||||
// recompute log
|
||||
std::vector<App::DocumentObjectExecReturn*> _RecomputeLog;
|
||||
std::map<int,Transaction*> mRedoMap;
|
||||
|
||||
// pointer to the python class
|
||||
Py::Object DocumentPythonObject;
|
||||
struct DocumentP* d;
|
||||
|
||||
std::string oldLabel;
|
||||
std::string myName;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
||||
+622
-135
@@ -29,19 +29,28 @@
|
||||
#include <Base/Writer.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Exception.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Document.h"
|
||||
#include "DocumentObject.h"
|
||||
#include "DocumentObjectGroup.h"
|
||||
#include "PropertyLinks.h"
|
||||
#include "PropertyGeo.h"
|
||||
#include "PropertyExpressionEngine.h"
|
||||
#include "DocumentObjectExtension.h"
|
||||
#include "GeoFeatureGroupExtension.h"
|
||||
#include <App/DocumentObjectPy.h>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
FC_LOG_LEVEL_INIT("App",true,true)
|
||||
|
||||
using namespace App;
|
||||
|
||||
/** \defgroup DocObject Document Object
|
||||
\ingroup APP
|
||||
\brief Base class of all objects handled in the Document
|
||||
*/
|
||||
|
||||
PROPERTY_SOURCE(App::DocumentObject, App::TransactionalObject)
|
||||
|
||||
@@ -52,11 +61,22 @@ DocumentObjectExecReturn *DocumentObject::StdReturn = 0;
|
||||
//===========================================================================
|
||||
|
||||
DocumentObject::DocumentObject(void)
|
||||
: ExpressionEngine(),_pDoc(0),pcNameInDocument(0)
|
||||
: ExpressionEngine(),_pDoc(0),pcNameInDocument(0),_Id(0)
|
||||
{
|
||||
// define Label of type 'Output' to avoid being marked as touched after relabeling
|
||||
ADD_PROPERTY_TYPE(Label,("Unnamed"),"Base",Prop_Output,"User name of the object (UTF8)");
|
||||
ADD_PROPERTY_TYPE(Label2,(""),"Base",Prop_Hidden,"User description of the object (UTF8)");
|
||||
Label2.setStatus(App::Property::Output,true);
|
||||
ADD_PROPERTY_TYPE(ExpressionEngine,(),"Base",Prop_Hidden,"Property expressions");
|
||||
|
||||
ADD_PROPERTY(Visibility, (true));
|
||||
|
||||
// default set Visibility status to hidden and output (no touch) for
|
||||
// compatibitily reason. We use setStatus instead of PropertyType to
|
||||
// allow user to change its status later
|
||||
Visibility.setStatus(Property::Output,true);
|
||||
Visibility.setStatus(Property::Hidden,true);
|
||||
Visibility.setStatus(Property::NoModify,true);
|
||||
}
|
||||
|
||||
DocumentObject::~DocumentObject(void)
|
||||
@@ -103,11 +123,11 @@ DocumentObjectExecReturn *DocumentObject::execute(void)
|
||||
return StdReturn;
|
||||
}
|
||||
|
||||
bool DocumentObject::recomputeFeature()
|
||||
bool DocumentObject::recomputeFeature(bool recursive)
|
||||
{
|
||||
Document* doc = this->getDocument();
|
||||
if (doc)
|
||||
doc->recomputeFeature(this);
|
||||
return doc->recomputeFeature(this,recursive);
|
||||
return isValid();
|
||||
}
|
||||
|
||||
@@ -118,9 +138,13 @@ bool DocumentObject::recomputeFeature()
|
||||
* If it should be forced to recompute a document object then use
|
||||
* \ref enforceRecompute() instead.
|
||||
*/
|
||||
void DocumentObject::touch(void)
|
||||
void DocumentObject::touch(bool noRecompute)
|
||||
{
|
||||
if(!noRecompute)
|
||||
StatusBits.set(ObjectStatus::Enforce);
|
||||
StatusBits.set(ObjectStatus::Touch);
|
||||
if (_pDoc)
|
||||
_pDoc->signalTouchedObject(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,8 +163,7 @@ bool DocumentObject::isTouched() const
|
||||
*/
|
||||
void DocumentObject::enforceRecompute(void)
|
||||
{
|
||||
StatusBits.set(ObjectStatus::Enforce);
|
||||
StatusBits.set(ObjectStatus::Touch);
|
||||
touch(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,7 +207,16 @@ const char* DocumentObject::getStatusString(void) const
|
||||
return "Valid";
|
||||
}
|
||||
|
||||
const char *DocumentObject::getNameInDocument(void) const
|
||||
std::string DocumentObject::getFullName() const {
|
||||
if(!getDocument() || !pcNameInDocument)
|
||||
return "?";
|
||||
std::string name(getDocument()->getName());
|
||||
name += '#';
|
||||
name += *pcNameInDocument;
|
||||
return name;
|
||||
}
|
||||
|
||||
const char *DocumentObject::getNameInDocument() const
|
||||
{
|
||||
// Note: It can happen that we query the internal name of an object even if it is not
|
||||
// part of a document (anymore). This is the case e.g. if we have a reference in Python
|
||||
@@ -195,6 +227,26 @@ const char *DocumentObject::getNameInDocument(void) const
|
||||
return pcNameInDocument->c_str();
|
||||
}
|
||||
|
||||
int DocumentObject::isExporting() const {
|
||||
if(!getDocument() || !getNameInDocument())
|
||||
return 0;
|
||||
return getDocument()->isExporting(this);
|
||||
}
|
||||
|
||||
std::string DocumentObject::getExportName(bool forced) const {
|
||||
if(!pcNameInDocument)
|
||||
return std::string();
|
||||
|
||||
if(!forced && !isExporting())
|
||||
return *pcNameInDocument;
|
||||
|
||||
// '@' is an invalid character for an internal name, which ensures the
|
||||
// following returned name will be unique in any document. Saving external
|
||||
// object like that shall only happens in Document::exportObjects(). We
|
||||
// shall strip out this '@' and the following document name during restoring.
|
||||
return *pcNameInDocument + '@' + getDocument()->getName();
|
||||
}
|
||||
|
||||
bool DocumentObject::isAttachedToDocument() const
|
||||
{
|
||||
return (pcNameInDocument != 0);
|
||||
@@ -207,40 +259,38 @@ const char* DocumentObject::detachFromDocument()
|
||||
return name ? name->c_str() : 0;
|
||||
}
|
||||
|
||||
std::vector<DocumentObject*> DocumentObject::getOutList(void) const
|
||||
{
|
||||
std::vector<Property*> List;
|
||||
std::vector<DocumentObject*> ret;
|
||||
getPropertyList(List);
|
||||
for (std::vector<Property*>::const_iterator It = List.begin();It != List.end(); ++It) {
|
||||
if ((*It)->isDerivedFrom(PropertyLinkList::getClassTypeId())) {
|
||||
const std::vector<DocumentObject*> &OutList = static_cast<PropertyLinkList*>(*It)->getValues();
|
||||
for (std::vector<DocumentObject*>::const_iterator It2 = OutList.begin();It2 != OutList.end(); ++It2) {
|
||||
if (*It2)
|
||||
ret.push_back(*It2);
|
||||
}
|
||||
}
|
||||
else if ((*It)->isDerivedFrom(PropertyLinkSubList::getClassTypeId())) {
|
||||
const std::vector<DocumentObject*> &OutList = static_cast<PropertyLinkSubList*>(*It)->getValues();
|
||||
for (std::vector<DocumentObject*>::const_iterator It2 = OutList.begin();It2 != OutList.end(); ++It2) {
|
||||
if (*It2)
|
||||
ret.push_back(*It2);
|
||||
}
|
||||
}
|
||||
else if ((*It)->isDerivedFrom(PropertyLink::getClassTypeId())) {
|
||||
if (static_cast<PropertyLink*>(*It)->getValue())
|
||||
ret.push_back(static_cast<PropertyLink*>(*It)->getValue());
|
||||
}
|
||||
else if ((*It)->isDerivedFrom(PropertyLinkSub::getClassTypeId())) {
|
||||
if (static_cast<PropertyLinkSub*>(*It)->getValue())
|
||||
ret.push_back(static_cast<PropertyLinkSub*>(*It)->getValue());
|
||||
}
|
||||
const std::vector<DocumentObject*> &DocumentObject::getOutList() const {
|
||||
if(!_outListCached) {
|
||||
_outList.clear();
|
||||
getOutList(0,_outList);
|
||||
_outListCached = true;
|
||||
}
|
||||
return _outList;
|
||||
}
|
||||
|
||||
// Get document objects that this document object relies on
|
||||
ExpressionEngine.getDocumentObjectDeps(ret);
|
||||
std::vector<DocumentObject*> DocumentObject::getOutList(int options) const
|
||||
{
|
||||
std::vector<DocumentObject*> res;
|
||||
getOutList(options,res);
|
||||
return res;
|
||||
}
|
||||
|
||||
return ret;
|
||||
void DocumentObject::getOutList(int options, std::vector<DocumentObject*> &res) const {
|
||||
if(_outListCached && !options) {
|
||||
res.insert(res.end(),_outList.begin(),_outList.end());
|
||||
return;
|
||||
}
|
||||
std::vector<Property*> props;
|
||||
getPropertyList(props);
|
||||
bool noHidden = !!(options & OutListNoHidden);
|
||||
bool noXLinked = !!(options & OutListNoXLinked);
|
||||
for(auto prop : props) {
|
||||
auto link = dynamic_cast<PropertyLinkBase*>(prop);
|
||||
if(link && (!noXLinked || !PropertyXLink::supportXLink(prop)))
|
||||
link->getLinks(res,noHidden);
|
||||
}
|
||||
if(!(options & OutListNoExpression))
|
||||
ExpressionEngine.getLinks(res);
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*> DocumentObject::getOutListOfProperty(App::Property* prop) const
|
||||
@@ -249,33 +299,9 @@ std::vector<App::DocumentObject*> DocumentObject::getOutListOfProperty(App::Prop
|
||||
if (!prop || prop->getContainer() != this)
|
||||
return ret;
|
||||
|
||||
if (prop->isDerivedFrom(PropertyLinkList::getClassTypeId())) {
|
||||
const std::vector<DocumentObject*> &OutList = static_cast<PropertyLinkList*>(prop)->getValues();
|
||||
for (std::vector<DocumentObject*>::const_iterator It2 = OutList.begin();It2 != OutList.end(); ++It2) {
|
||||
if (*It2)
|
||||
ret.push_back(*It2);
|
||||
}
|
||||
}
|
||||
else if (prop->isDerivedFrom(PropertyLinkSubList::getClassTypeId())) {
|
||||
const std::vector<DocumentObject*> &OutList = static_cast<PropertyLinkSubList*>(prop)->getValues();
|
||||
for (std::vector<DocumentObject*>::const_iterator It2 = OutList.begin();It2 != OutList.end(); ++It2) {
|
||||
if (*It2)
|
||||
ret.push_back(*It2);
|
||||
}
|
||||
}
|
||||
else if (prop->isDerivedFrom(PropertyLink::getClassTypeId())) {
|
||||
if (static_cast<PropertyLink*>(prop)->getValue())
|
||||
ret.push_back(static_cast<PropertyLink*>(prop)->getValue());
|
||||
}
|
||||
else if (prop->isDerivedFrom(PropertyLinkSub::getClassTypeId())) {
|
||||
if (static_cast<PropertyLinkSub*>(prop)->getValue())
|
||||
ret.push_back(static_cast<PropertyLinkSub*>(prop)->getValue());
|
||||
}
|
||||
else if (prop == &ExpressionEngine) {
|
||||
// Get document objects that this document object relies on
|
||||
ExpressionEngine.getDocumentObjectDeps(ret);
|
||||
}
|
||||
|
||||
auto link = dynamic_cast<PropertyLinkBase*>(prop);
|
||||
if(link)
|
||||
link->getLinks(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -290,7 +316,7 @@ std::vector<App::DocumentObject*> DocumentObject::getInList(void) const
|
||||
|
||||
#else // ifndef USE_OLD_DAG
|
||||
|
||||
std::vector<App::DocumentObject*> DocumentObject::getInList(void) const
|
||||
const std::vector<App::DocumentObject*> &DocumentObject::getInList(void) const
|
||||
{
|
||||
return _inList;
|
||||
}
|
||||
@@ -298,6 +324,8 @@ std::vector<App::DocumentObject*> DocumentObject::getInList(void) const
|
||||
#endif // if USE_OLD_DAG
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
void _getInListRecursive(std::set<DocumentObject*>& objSet,
|
||||
const DocumentObject* obj,
|
||||
const DocumentObject* checkObj, int depth)
|
||||
@@ -318,8 +346,10 @@ void _getInListRecursive(std::set<DocumentObject*>& objSet,
|
||||
std::vector<App::DocumentObject*> DocumentObject::getInListRecursive(void) const
|
||||
{
|
||||
// number of objects in document is a good estimate in result size
|
||||
int maxDepth = getDocument()->countObjects() + 2;
|
||||
std::set<App::DocumentObject*> result;
|
||||
// int maxDepth = getDocument()->countObjects() +2;
|
||||
int maxDepth = GetApplication().checkLinkDepth(0);
|
||||
std::vector<App::DocumentObject*> result;
|
||||
result.reserve(maxDepth);
|
||||
|
||||
// using a rcursie helper to collect all InLists
|
||||
_getInListRecursive(result, this, this, maxDepth);
|
||||
@@ -329,6 +359,95 @@ std::vector<App::DocumentObject*> DocumentObject::getInListRecursive(void) const
|
||||
return array;
|
||||
}
|
||||
|
||||
#else
|
||||
// The original algorithm is highly inefficient in some special case.
|
||||
// Considering an object is linked by every other objects. After exculding this
|
||||
// object, there is another object linked by every other of the remaining
|
||||
// objects, and so on. The vector 'result' above will be of magnitude n^2.
|
||||
// Even if we replace the vector with a set, we still need to visit that amount
|
||||
// of objects. And this may not be the worst case. getInListEx() has no such
|
||||
// problem.
|
||||
|
||||
std::vector<App::DocumentObject*> DocumentObject::getInListRecursive(void) const {
|
||||
std::set<App::DocumentObject*> inSet;
|
||||
std::vector<App::DocumentObject*> res;
|
||||
getInListEx(inSet,true,&res);
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// More efficient algorithm to find the recursive inList of an object,
|
||||
// including possible external parents. One shortcoming of this algorithm is
|
||||
// it does not detect cyclic reference, althgouth it won't crash either.
|
||||
void DocumentObject::getInListEx(std::set<App::DocumentObject*> &inSet,
|
||||
bool recursive, std::vector<App::DocumentObject*> *inList) const
|
||||
{
|
||||
#ifdef USE_OLD_DAG
|
||||
std::map<DocumentObject*,std::set<App::DocumentObject*> > outLists;
|
||||
|
||||
// Old DAG does not have pre-built InList, and must calculate The InList by
|
||||
// going through all objects' OutLists. So we collect all objects and their
|
||||
// outLists first here.
|
||||
for(auto doc : GetApplication().getDocuments()) {
|
||||
for(auto obj : doc->getObjects()) {
|
||||
if(!obj || !obj->getNameInDocument() || obj==this)
|
||||
continue;
|
||||
const auto &outList = obj->getOutList();
|
||||
outLists[obj].insert(outList.begin(),outList.end());
|
||||
}
|
||||
}
|
||||
|
||||
std::stack<DocumentObject*> pendings;
|
||||
pendings.push(const_cast<DocumentObject*>(this));
|
||||
while(pendings.size()) {
|
||||
auto obj = pendings.top();
|
||||
pendings.pop();
|
||||
for(auto &v : outLists) {
|
||||
if(v.first == obj) continue;
|
||||
auto &outList = v.second;
|
||||
// Check the outList to see if the object is there, and pend the
|
||||
// object for recursive check if it's not already in the inList
|
||||
if(outList.find(obj)!=outList.end() &&
|
||||
inSet.insert(v.first).second &&
|
||||
recursive)
|
||||
{
|
||||
pendings.push(v.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else // USE_OLD_DAG
|
||||
|
||||
if(!recursive) {
|
||||
inSet.insert(_inList.begin(),_inList.end());
|
||||
if(inList)
|
||||
*inList = _inList;
|
||||
return;
|
||||
}
|
||||
|
||||
std::stack<DocumentObject*> pendings;
|
||||
pendings.push(const_cast<DocumentObject*>(this));
|
||||
while(pendings.size()) {
|
||||
auto obj = pendings.top();
|
||||
pendings.pop();
|
||||
for(auto o : obj->getInList()) {
|
||||
if(o && o->getNameInDocument() && inSet.insert(o).second) {
|
||||
pendings.push(o);
|
||||
if(inList)
|
||||
inList->push_back(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
std::set<App::DocumentObject*> DocumentObject::getInListEx(bool recursive) const {
|
||||
std::set<App::DocumentObject*> ret;
|
||||
getInListEx(ret,recursive);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void _getOutListRecursive(std::set<DocumentObject*>& objSet,
|
||||
const DocumentObject* obj,
|
||||
const DocumentObject* checkObj, int depth)
|
||||
@@ -349,7 +468,7 @@ void _getOutListRecursive(std::set<DocumentObject*>& objSet,
|
||||
std::vector<App::DocumentObject*> DocumentObject::getOutListRecursive(void) const
|
||||
{
|
||||
// number of objects in document is a good estimate in result size
|
||||
int maxDepth = getDocument()->countObjects() + 2;
|
||||
int maxDepth = GetApplication().checkLinkDepth(0);
|
||||
std::set<App::DocumentObject*> result;
|
||||
|
||||
// using a recursive helper to collect all OutLists
|
||||
@@ -387,8 +506,12 @@ bool _isInInListRecursive(const DocumentObject* act,
|
||||
|
||||
bool DocumentObject::isInInListRecursive(DocumentObject *linkTo) const
|
||||
{
|
||||
#if 0
|
||||
int maxDepth = getDocument()->countObjects() + 2;
|
||||
return _isInInListRecursive(this, linkTo, maxDepth);
|
||||
#else
|
||||
return this==linkTo || getInListEx(true).count(linkTo);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DocumentObject::isInInList(DocumentObject *linkTo) const
|
||||
@@ -455,6 +578,7 @@ bool DocumentObject::testIfLinkDAGCompatible(DocumentObject *linkTo) const
|
||||
|
||||
bool DocumentObject::testIfLinkDAGCompatible(const std::vector<DocumentObject *> &linksTo) const
|
||||
{
|
||||
#if 0
|
||||
Document* doc = this->getDocument();
|
||||
if (!doc)
|
||||
throw Base::RuntimeError("DocumentObject::testIfLinkIsDAG: object is not in any document.");
|
||||
@@ -464,6 +588,14 @@ bool DocumentObject::testIfLinkDAGCompatible(const std::vector<DocumentObject *>
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
#else
|
||||
auto inLists = getInListEx(true);
|
||||
inLists.emplace(const_cast<DocumentObject*>(this));
|
||||
for(auto obj : linksTo)
|
||||
if(inLists.count(obj))
|
||||
return false;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DocumentObject::testIfLinkDAGCompatible(PropertyLinkSubList &linksTo) const
|
||||
@@ -496,27 +628,44 @@ void DocumentObject::setDocument(App::Document* doc)
|
||||
onSettingDocument();
|
||||
}
|
||||
|
||||
void DocumentObject::onAboutToRemoveProperty(const char* name)
|
||||
bool DocumentObject::removeDynamicProperty(const char* name)
|
||||
{
|
||||
if (_pDoc) {
|
||||
_pDoc->removePropertyOfObject(this, name);
|
||||
if (!_pDoc)
|
||||
return false;
|
||||
|
||||
Property* prop = getDynamicPropertyByName(name);
|
||||
if (prop) {
|
||||
auto expressions = ExpressionEngine.getExpressions();
|
||||
std::vector<App::ObjectIdentifier> removeExpr;
|
||||
Property* prop = getDynamicPropertyByName(name);
|
||||
if(!prop || prop->testStatus(App::Property::LockDynamic))
|
||||
return false;
|
||||
|
||||
for (auto it : expressions) {
|
||||
if (it.first.getProperty() == prop) {
|
||||
removeExpr.push_back(it.first);
|
||||
}
|
||||
}
|
||||
if(prop->isDerivedFrom(PropertyLinkBase::getClassTypeId()))
|
||||
clearOutListCache();
|
||||
|
||||
for (auto it : removeExpr) {
|
||||
ExpressionEngine.setValue(it, boost::shared_ptr<Expression>());
|
||||
}
|
||||
_pDoc->addOrRemovePropertyOfObject(this, prop, false);
|
||||
|
||||
auto expressions = ExpressionEngine.getExpressions();
|
||||
std::vector<App::ObjectIdentifier> removeExpr;
|
||||
|
||||
for (auto it : expressions) {
|
||||
if (it.first.getProperty() == prop) {
|
||||
removeExpr.push_back(it.first);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it : removeExpr) {
|
||||
ExpressionEngine.setValue(it, boost::shared_ptr<Expression>());
|
||||
}
|
||||
|
||||
return TransactionalObject::removeDynamicProperty(name);
|
||||
}
|
||||
|
||||
App::Property* DocumentObject::addDynamicProperty(
|
||||
const char* type, const char* name, const char* group, const char* doc,
|
||||
short attr, bool ro, bool hidden)
|
||||
{
|
||||
auto prop = TransactionalObject::addDynamicProperty(type,name,group,doc,attr,ro,hidden);
|
||||
if(prop && _pDoc)
|
||||
_pDoc->addOrRemovePropertyOfObject(this, prop, true);
|
||||
return prop;
|
||||
}
|
||||
|
||||
void DocumentObject::onBeforeChange(const Property* prop)
|
||||
@@ -528,20 +677,46 @@ void DocumentObject::onBeforeChange(const Property* prop)
|
||||
|
||||
if (_pDoc)
|
||||
onBeforeChangeProperty(_pDoc, prop);
|
||||
|
||||
signalBeforeChange(*this,*prop);
|
||||
}
|
||||
|
||||
/// get called by the container when a Property was changed
|
||||
void DocumentObject::onChanged(const Property* prop)
|
||||
{
|
||||
if (_pDoc)
|
||||
_pDoc->onChangedProperty(this,prop);
|
||||
if(GetApplication().isClosingAll())
|
||||
return;
|
||||
|
||||
if(!GetApplication().isRestoring() &&
|
||||
prop && !prop->testStatus(Property::PartialTrigger) &&
|
||||
getDocument() &&
|
||||
getDocument()->testStatus(Document::PartialDoc))
|
||||
{
|
||||
static App::Document *warnedDoc;
|
||||
if(warnedDoc != getDocument()) {
|
||||
warnedDoc = getDocument();
|
||||
FC_WARN("Changes to partial loaded document will not be saved: "
|
||||
<< getFullName() << '.' << prop->getName());
|
||||
}
|
||||
}
|
||||
|
||||
// Delay signaling view provider until the document object has handled the
|
||||
// change
|
||||
// if (_pDoc)
|
||||
// _pDoc->onChangedProperty(this,prop);
|
||||
|
||||
if (prop == &Label && _pDoc && oldLabel != Label.getStrValue())
|
||||
_pDoc->signalRelabelObject(*this);
|
||||
|
||||
// set object touched if it is an input property
|
||||
if (!(prop->getType() & Prop_Output)) {
|
||||
StatusBits.set(ObjectStatus::Touch);
|
||||
if (!testStatus(ObjectStatus::NoTouch)
|
||||
&& !(prop->getType() & Prop_Output)
|
||||
&& !prop->testStatus(Property::Output))
|
||||
{
|
||||
if(!StatusBits.test(ObjectStatus::Touch)) {
|
||||
FC_TRACE("touch '" << getFullName() << "' on change of '" << prop->getName() << "'");
|
||||
StatusBits.set(ObjectStatus::Touch);
|
||||
}
|
||||
// must execute on document recompute
|
||||
if (!(prop->getType() & Prop_NoRecompute))
|
||||
StatusBits.set(ObjectStatus::Enforce);
|
||||
@@ -549,6 +724,18 @@ void DocumentObject::onChanged(const Property* prop)
|
||||
|
||||
//call the parent for appropriate handling
|
||||
TransactionalObject::onChanged(prop);
|
||||
|
||||
// Now signal the view provider
|
||||
if (_pDoc)
|
||||
_pDoc->onChangedProperty(this,prop);
|
||||
|
||||
signalChanged(*this,*prop);
|
||||
}
|
||||
|
||||
void DocumentObject::clearOutListCache() const {
|
||||
_outList.clear();
|
||||
_outListMap.clear();
|
||||
_outListCached = false;
|
||||
}
|
||||
|
||||
PyObject *DocumentObject::getPyObject(void)
|
||||
@@ -560,10 +747,126 @@ PyObject *DocumentObject::getPyObject(void)
|
||||
return Py::new_reference_to(PythonObject);
|
||||
}
|
||||
|
||||
std::vector<PyObject *> DocumentObject::getPySubObjects(const std::vector<std::string>&) const
|
||||
DocumentObject *DocumentObject::getSubObject(const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
|
||||
{
|
||||
// default implementation returns nothing
|
||||
return std::vector<PyObject *>();
|
||||
DocumentObject *ret = 0;
|
||||
auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
||||
for(auto ext : exts) {
|
||||
if(ext->extensionGetSubObject(ret,subname,pyObj,mat,transform, depth))
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
const char *dot=0;
|
||||
if(!subname || !(dot=strchr(subname,'.'))) {
|
||||
ret = const_cast<DocumentObject*>(this);
|
||||
}else if(subname[0]=='$') {
|
||||
name = std::string(subname+1,dot);
|
||||
for(auto obj : getOutList()) {
|
||||
if(name == obj->Label.getValue()) {
|
||||
ret = obj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
name = std::string(subname,dot);
|
||||
const auto &outList = getOutList();
|
||||
if(outList.size()!=_outListMap.size()) {
|
||||
_outListMap.clear();
|
||||
for(auto obj : outList)
|
||||
_outListMap[obj->getNameInDocument()] = obj;
|
||||
}
|
||||
auto it = _outListMap.find(name.c_str());
|
||||
if(it != _outListMap.end())
|
||||
ret = it->second;
|
||||
}
|
||||
|
||||
// TODO: By right, normal object's placement does not transform its sub
|
||||
// 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"));
|
||||
if(pla)
|
||||
*mat *= pla->getValue().toMatrix();
|
||||
}
|
||||
|
||||
if(ret && dot)
|
||||
return ret->getSubObject(dot+1,pyObj,mat,true,depth+1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<DocumentObject*> DocumentObject::getSubObjectList(const char *subname) const {
|
||||
std::vector<DocumentObject*> res;
|
||||
res.push_back(const_cast<DocumentObject*>(this));
|
||||
if(!subname || !subname[0])
|
||||
return res;
|
||||
std::string sub(subname);
|
||||
for(auto pos=sub.find('.');pos!=std::string::npos;pos=sub.find('.',pos+1)) {
|
||||
char c = sub[pos+1];
|
||||
sub[pos+1] = 0;
|
||||
auto sobj = getSubObject(sub.c_str());
|
||||
if(!sobj || !sobj->getNameInDocument())
|
||||
break;
|
||||
res.push_back(sobj);
|
||||
sub[pos+1] = c;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::string> DocumentObject::getSubObjects(int reason) const {
|
||||
std::vector<std::string> ret;
|
||||
auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
||||
for(auto ext : exts) {
|
||||
if(ext->extensionGetSubObjects(ret,reason))
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::pair<App::DocumentObject *,std::string> > DocumentObject::getParents(int depth) const {
|
||||
std::vector<std::pair<App::DocumentObject *,std::string> > ret;
|
||||
if(!getNameInDocument() || !GetApplication().checkLinkDepth(depth))
|
||||
return ret;
|
||||
std::string name(getNameInDocument());
|
||||
name += ".";
|
||||
for(auto parent : getInList()) {
|
||||
if(!parent || !parent->getNameInDocument())
|
||||
continue;
|
||||
if(!parent->hasChildElement() &&
|
||||
!parent->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId()))
|
||||
continue;
|
||||
if(!parent->getSubObject(name.c_str()))
|
||||
continue;
|
||||
|
||||
auto links = GetApplication().getLinksTo(parent,App::GetLinkRecursive);
|
||||
links.insert(parent);
|
||||
for(auto parent : links) {
|
||||
auto parents = parent->getParents(depth+1);
|
||||
if(parents.empty())
|
||||
parents.emplace_back(parent,std::string());
|
||||
for(auto &v : parents)
|
||||
ret.emplace_back(v.first,v.second+name);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
DocumentObject *DocumentObject::getLinkedObject(
|
||||
bool recursive, Base::Matrix4D *mat, bool transform, int depth) const
|
||||
{
|
||||
DocumentObject *ret = 0;
|
||||
auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
||||
for(auto ext : exts) {
|
||||
if(ext->extensionGetLinkedObject(ret,recursive,mat,transform,depth))
|
||||
return ret;
|
||||
}
|
||||
if(transform && mat) {
|
||||
auto pla = dynamic_cast<PropertyPlacement*>(getPropertyByName("Placement"));
|
||||
if(pla)
|
||||
*mat *= pla->getValue().toMatrix();
|
||||
}
|
||||
return const_cast<DocumentObject*>(this);
|
||||
}
|
||||
|
||||
void DocumentObject::Save (Base::Writer &writer) const
|
||||
@@ -577,13 +880,11 @@ void DocumentObject::Save (Base::Writer &writer) const
|
||||
* @brief Associate the expression \expr with the object identifier \a path in this document object.
|
||||
* @param path Target object identifier for the result of the expression
|
||||
* @param expr Expression tree
|
||||
* @param comment Optional comment describing the expression
|
||||
*/
|
||||
|
||||
void DocumentObject::setExpression(const ObjectIdentifier &path, boost::shared_ptr<Expression> expr, const char * comment)
|
||||
void DocumentObject::setExpression(const ObjectIdentifier &path, boost::shared_ptr<Expression> expr)
|
||||
{
|
||||
ExpressionEngine.setValue(path, expr, comment);
|
||||
connectRelabelSignals();
|
||||
ExpressionEngine.setValue(path, expr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -614,52 +915,14 @@ void DocumentObject::renameObjectIdentifiers(const std::map<ObjectIdentifier, Ob
|
||||
ExpressionEngine.renameObjectIdentifiers(paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function that sets up a signal to track document object renames.
|
||||
*/
|
||||
|
||||
void DocumentObject::connectRelabelSignals()
|
||||
{
|
||||
// Only keep signal if the ExpressionEngine has at least one expression
|
||||
if (ExpressionEngine.numExpressions() > 0) {
|
||||
|
||||
// Not already connected?
|
||||
if (!onRelabledObjectConnection.connected()) {
|
||||
onRelabledObjectConnection = getDocument()->signalRelabelObject
|
||||
.connect(boost::bind(&PropertyExpressionEngine::slotObjectRenamed,
|
||||
&ExpressionEngine, _1));
|
||||
}
|
||||
|
||||
// Connect to signalDeletedObject, to properly track deletion of other objects
|
||||
// that might be referenced in an expression
|
||||
if (!onDeletedObjectConnection.connected()) {
|
||||
onDeletedObjectConnection = getDocument()->signalDeletedObject
|
||||
.connect(boost::bind(&PropertyExpressionEngine::slotObjectDeleted,
|
||||
&ExpressionEngine, _1));
|
||||
}
|
||||
|
||||
try {
|
||||
// Crude method to resolve all expression dependencies
|
||||
ExpressionEngine.execute();
|
||||
}
|
||||
catch (...) {
|
||||
// Ignore any error
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Disconnect signals; nothing to track now
|
||||
onRelabledObjectConnection.disconnect();
|
||||
onRelabledDocumentConnection.disconnect();
|
||||
onDeletedObjectConnection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void DocumentObject::onDocumentRestored()
|
||||
{
|
||||
//call all extensions
|
||||
auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
||||
for(auto ext : vector)
|
||||
ext->onExtendedDocumentRestored();
|
||||
if(Visibility.testStatus(Property::Output))
|
||||
Visibility.setStatus(Property::NoModify,true);
|
||||
}
|
||||
|
||||
void DocumentObject::onSettingDocument()
|
||||
@@ -711,3 +974,227 @@ void App::DocumentObject::_addBackLink(DocumentObject* newObj)
|
||||
(void)newObj;
|
||||
#endif //USE_OLD_DAG
|
||||
}
|
||||
|
||||
int DocumentObject::setElementVisible(const char *element, bool visible) {
|
||||
for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
|
||||
int ret = ext->extensionSetElementVisible(element,visible);
|
||||
if(ret>=0) return ret;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int DocumentObject::isElementVisible(const char *element) const {
|
||||
for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
|
||||
int ret = ext->extensionIsElementVisible(element);
|
||||
if(ret>=0) return ret;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool DocumentObject::hasChildElement() const {
|
||||
for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
|
||||
if(ext->extensionHasChildElement())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
DocumentObject *DocumentObject::resolve(const char *subname,
|
||||
App::DocumentObject **parent, std::string *childName, const char **subElement,
|
||||
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const
|
||||
{
|
||||
auto self = const_cast<DocumentObject*>(this);
|
||||
if(parent) *parent = 0;
|
||||
if(subElement) *subElement = 0;
|
||||
|
||||
auto obj = getSubObject(subname,pyObj,pmat,transform,depth);
|
||||
if(!obj || !subname || *subname==0)
|
||||
return self;
|
||||
|
||||
if(!parent && !subElement)
|
||||
return obj;
|
||||
|
||||
// NOTE, the convention of '.' separated SubName demands a mandatory ending
|
||||
// '.' for each object name in SubName, even if there is no subelement
|
||||
// following it. So finding the last dot will give us the end of the last
|
||||
// object name.
|
||||
const char *dot=0;
|
||||
if(Data::ComplexGeoData::isMappedElement(subname) ||
|
||||
!(dot=strrchr(subname,'.')) ||
|
||||
dot == subname)
|
||||
{
|
||||
if(subElement)
|
||||
*subElement = dot?dot+1:subname;
|
||||
return obj; // this means no parent object reference in SubName
|
||||
}
|
||||
|
||||
if(parent)
|
||||
*parent = self;
|
||||
|
||||
bool elementMapChecked = false;
|
||||
const char *lastDot = dot;
|
||||
for(--dot;;--dot) {
|
||||
// check for the second last dot, which is the end of the last parent object
|
||||
if(*dot == '.' || dot == subname) {
|
||||
// We can't get parent object by its name, because the object may be
|
||||
// externally linked (i.e. in a different document). So go through
|
||||
// getSubObject again.
|
||||
if(!elementMapChecked) {
|
||||
elementMapChecked = true;
|
||||
const char *sub = dot==subname?dot:dot+1;
|
||||
if(Data::ComplexGeoData::isMappedElement(sub)) {
|
||||
lastDot = dot;
|
||||
if(dot==subname)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(dot==subname)
|
||||
break;
|
||||
auto sobj = getSubObject(std::string(subname,dot-subname+1).c_str());
|
||||
if(sobj!=obj) {
|
||||
if(parent) {
|
||||
// Link/LinkGroup has special visiblility handling of plain
|
||||
// group, so keep ascending
|
||||
if(!sobj->hasExtension(GroupExtension::getExtensionClassTypeId(),false)) {
|
||||
*parent = sobj;
|
||||
break;
|
||||
}
|
||||
for(auto ddot=dot-1;ddot!=subname;--ddot) {
|
||||
if(*ddot != '.') continue;
|
||||
auto sobj = getSubObject(std::string(subname,ddot-subname+1).c_str());
|
||||
if(!sobj->hasExtension(GroupExtension::getExtensionClassTypeId(),false)) {
|
||||
*parent = sobj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(childName && lastDot!=dot) {
|
||||
if(*dot == '.')
|
||||
++dot;
|
||||
const char *nextDot = strchr(dot,'.');
|
||||
assert(nextDot);
|
||||
*childName = std::string(dot,nextDot-dot);
|
||||
}
|
||||
if(subElement)
|
||||
*subElement = *lastDot=='.'?lastDot+1:lastDot;
|
||||
return obj;
|
||||
}
|
||||
|
||||
DocumentObject *DocumentObject::resolveRelativeLink(std::string &subname,
|
||||
DocumentObject *&link, std::string &linkSub) const
|
||||
{
|
||||
if(!link || !link->getNameInDocument() || !getNameInDocument())
|
||||
return 0;
|
||||
auto ret = const_cast<DocumentObject*>(this);
|
||||
if(link != ret) {
|
||||
auto sub = subname.c_str();
|
||||
auto nextsub = sub;
|
||||
for(auto dot=strchr(nextsub,'.');dot;nextsub=dot+1,dot=strchr(nextsub,'.')) {
|
||||
std::string subcheck(sub,nextsub-sub);
|
||||
subcheck += link->getNameInDocument();
|
||||
subcheck += '.';
|
||||
if(getSubObject(subcheck.c_str())==link) {
|
||||
ret = getSubObject(std::string(sub,dot+1-sub).c_str());
|
||||
if(!ret)
|
||||
return 0;
|
||||
subname = std::string(dot+1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t pos=0,linkPos=0;
|
||||
std::string linkssub,ssub;
|
||||
do {
|
||||
linkPos = linkSub.find('.',linkPos);
|
||||
if(linkPos == std::string::npos) {
|
||||
link = 0;
|
||||
return 0;
|
||||
}
|
||||
++linkPos;
|
||||
pos = subname.find('.',pos);
|
||||
if(pos == std::string::npos) {
|
||||
subname.clear();
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
++pos;
|
||||
}while(subname.compare(0,pos,linkSub,0,linkPos)==0);
|
||||
|
||||
if(pos != std::string::npos) {
|
||||
ret = getSubObject(subname.substr(0,pos).c_str());
|
||||
if(!ret) {
|
||||
link = 0;
|
||||
return 0;
|
||||
}
|
||||
subname = subname.substr(pos);
|
||||
}
|
||||
if(linkPos) {
|
||||
link = link->getSubObject(linkSub.substr(0,linkPos).c_str());
|
||||
if(!link)
|
||||
return 0;
|
||||
linkSub = linkSub.substr(linkPos);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool DocumentObject::adjustRelativeLinks(
|
||||
const std::set<App::DocumentObject *> &inList,
|
||||
std::set<App::DocumentObject *> *visited)
|
||||
{
|
||||
if(visited)
|
||||
visited->insert(this);
|
||||
|
||||
bool touched = false;
|
||||
std::vector<Property*> props;
|
||||
getPropertyList(props);
|
||||
for(auto prop : props) {
|
||||
auto linkProp = Base::freecad_dynamic_cast<PropertyLinkBase>(prop);
|
||||
if(linkProp && linkProp->adjustLink(inList))
|
||||
touched = true;
|
||||
}
|
||||
if(visited) {
|
||||
for(auto obj : getOutList()) {
|
||||
if(!visited->count(obj)) {
|
||||
if(obj->adjustRelativeLinks(inList,visited))
|
||||
touched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return touched;
|
||||
}
|
||||
|
||||
const std::string &DocumentObject::hiddenMarker() {
|
||||
static std::string marker("!hide");
|
||||
return marker;
|
||||
}
|
||||
|
||||
const char *DocumentObject::hasHiddenMarker(const char *subname) {
|
||||
if(!subname) return 0;
|
||||
const char *marker = strrchr(subname,'.');
|
||||
if(!marker)
|
||||
marker = subname;
|
||||
else
|
||||
++marker;
|
||||
return hiddenMarker()==marker?marker:0;
|
||||
}
|
||||
|
||||
bool DocumentObject::redirectSubName(std::ostringstream &, DocumentObject *, DocumentObject *) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DocumentObject::onPropertyStatusChanged(const Property &prop, unsigned long oldStatus) {
|
||||
(void)oldStatus;
|
||||
if(!Document::isAnyRestoring() && getNameInDocument() && getDocument())
|
||||
getDocument()->signalChangePropertyEditor(*getDocument(),prop);
|
||||
}
|
||||
|
||||
|
||||
+338
-26
@@ -30,8 +30,10 @@
|
||||
#include <App/PropertyExpressionEngine.h>
|
||||
|
||||
#include <Base/TimeInfo.h>
|
||||
#include <Base/Matrix.h>
|
||||
#include <CXX/Objects.hxx>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <bitset>
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
@@ -46,13 +48,20 @@ enum ObjectStatus {
|
||||
Touch = 0,
|
||||
Error = 1,
|
||||
New = 2,
|
||||
Recompute = 3,
|
||||
Recompute = 3, // set when the object is currently being recomputed
|
||||
Restore = 4,
|
||||
Remove = 5,
|
||||
PythonCall = 6,
|
||||
Destroy = 7,
|
||||
Enforce = 8,
|
||||
Expand = 16
|
||||
Recompute2 = 9, // set when the object is being recomputed in the second pass
|
||||
PartialObject = 10,
|
||||
PendingRecompute = 11, // set by Document, indicating the object is in recomputation queue
|
||||
PendingRemove = 12, // set by Document, indicating the object is in pending for remove after recompute
|
||||
ObjImporting = 13, // Mark the object as importing
|
||||
NoTouch = 14, // no touch on any property change
|
||||
GeoExcluded = 15, // mark as a member but not claimed by GeoFeatureGroup
|
||||
Expand = 16,
|
||||
};
|
||||
|
||||
/** Return object for feature execution
|
||||
@@ -81,25 +90,44 @@ public:
|
||||
*/
|
||||
class AppExport DocumentObject: public App::TransactionalObject
|
||||
{
|
||||
PROPERTY_HEADER(App::DocumentObject);
|
||||
PROPERTY_HEADER_WITH_OVERRIDE(App::DocumentObject);
|
||||
|
||||
public:
|
||||
|
||||
PropertyString Label;
|
||||
PropertyString Label2;
|
||||
PropertyExpressionEngine ExpressionEngine;
|
||||
|
||||
/// Allow control visibility status in App name space
|
||||
PropertyBool Visibility;
|
||||
|
||||
/// signal before changing a property of this object
|
||||
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalBeforeChange;
|
||||
/// signal on changed property of this object
|
||||
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalChanged;
|
||||
|
||||
/// returns the type name of the ViewProvider
|
||||
virtual const char* getViewProviderName(void) const {
|
||||
return "";
|
||||
}
|
||||
/// This function is introduced to allow Python feature override its view provider
|
||||
virtual const char *getViewProviderNameOverride() const {
|
||||
return getViewProviderName();
|
||||
}
|
||||
/// Constructor
|
||||
DocumentObject(void);
|
||||
virtual ~DocumentObject();
|
||||
|
||||
/// returns the name which is set in the document for this object (not the name property!)
|
||||
const char *getNameInDocument(void) const;
|
||||
virtual bool isAttachedToDocument() const;
|
||||
virtual const char* detachFromDocument();
|
||||
/// Return the object ID that is unique within its owner document
|
||||
long getID() const {return _Id;}
|
||||
/// returns the name that is safe to be exported to other document
|
||||
std::string getExportName(bool forced=false) const;
|
||||
/// Return the object full name of the form DocName#ObjName
|
||||
virtual std::string getFullName() const override;
|
||||
virtual bool isAttachedToDocument() const override;
|
||||
virtual const char* detachFromDocument() override;
|
||||
/// gets the document in which this Object is handled
|
||||
App::Document *getDocument(void) const;
|
||||
|
||||
@@ -107,7 +135,7 @@ public:
|
||||
*/
|
||||
//@{
|
||||
/// set this document object touched (cause recomputation on dependent features)
|
||||
void touch(void);
|
||||
void touch(bool noRecompute=false);
|
||||
/// test if this document object is touched
|
||||
bool isTouched(void) const;
|
||||
/// Enforce this document object to be recomputed
|
||||
@@ -137,23 +165,82 @@ public:
|
||||
void setStatus(ObjectStatus pos, bool on) {StatusBits.set((size_t)pos, on);}
|
||||
//@}
|
||||
|
||||
int isExporting() const;
|
||||
|
||||
/** Child element handling
|
||||
*/
|
||||
//@{
|
||||
/** Set sub-element visibility
|
||||
*
|
||||
* For performance reason, \c element must not contain any further
|
||||
* sub-elements, i.e. there should be no '.' inside \c element.
|
||||
*
|
||||
* @return -1 if element visibility is not supported, 0 if element is not
|
||||
* found, 1 if success
|
||||
*/
|
||||
virtual int setElementVisible(const char *element, bool visible);
|
||||
|
||||
/** Get sub-element visibility
|
||||
*
|
||||
* @return -1 if element visibility is not supported or element not found, 0
|
||||
* if element is invisible, or else 1
|
||||
*/
|
||||
virtual int isElementVisible(const char *element) const;
|
||||
|
||||
/// return true to activate tree view group object handling and element visibility
|
||||
virtual bool hasChildElement() const;
|
||||
//@}
|
||||
|
||||
|
||||
/** DAG handling
|
||||
This part of the interface deals with viewing the document as
|
||||
a DAG (directed acyclic graph).
|
||||
*/
|
||||
//@{
|
||||
/// OutList options
|
||||
enum OutListOption {
|
||||
/// Do not include link from expression engine
|
||||
OutListNoExpression = 1,
|
||||
/// Do not hide any link (i.e. include links with LinkScopeHidden)
|
||||
OutListNoHidden = 2,
|
||||
/// Do not include link from PropertyXLink
|
||||
OutListNoXLinked = 4,
|
||||
};
|
||||
/// returns a list of objects this object is pointing to by Links
|
||||
std::vector<App::DocumentObject*> getOutList(void) const;
|
||||
const std::vector<App::DocumentObject*> &getOutList() const;
|
||||
std::vector<App::DocumentObject*> getOutList(int option) const;
|
||||
void getOutList(int option, std::vector<App::DocumentObject*> &res) const;
|
||||
|
||||
/// returns a list of objects linked by the property
|
||||
std::vector<App::DocumentObject*> getOutListOfProperty(App::Property*) const;
|
||||
/// returns a list of objects this object is pointing to by Links and all further descended
|
||||
std::vector<App::DocumentObject*> getOutListRecursive(void) const;
|
||||
/// clear internal out list cache
|
||||
void clearOutListCache() const;
|
||||
/// get all possible paths from this to another object following the OutList
|
||||
std::vector<std::list<App::DocumentObject*> > getPathsByOutList(App::DocumentObject* to) const;
|
||||
#ifdef USE_OLD_DAG
|
||||
/// get all objects link to this object
|
||||
std::vector<App::DocumentObject*> getInList(void) const;
|
||||
std::vector<App::DocumentObject*> getInList(void) const
|
||||
#else
|
||||
const std::vector<App::DocumentObject*> &getInList(void) const;
|
||||
#endif
|
||||
/// get all objects link directly or indirectly to this object
|
||||
std::vector<App::DocumentObject*> getInListRecursive(void) const;
|
||||
/** Get a set of all objects linking to this object, including possible external parent objects
|
||||
*
|
||||
* @param inSet [out]: a set containing all objects linking to this object.
|
||||
* @param recursive [in]: whether to obtain recursive in list
|
||||
* @param inList [in, out]: optional pointer to a vector holding the output
|
||||
* objects, with the furthest linking object ordered last.
|
||||
*/
|
||||
void getInListEx(std::set<App::DocumentObject*> &inSet,
|
||||
bool recursive, std::vector<App::DocumentObject*> *inList=0) const;
|
||||
/** Return a set of all objects linking to this object, including possible external parent objects
|
||||
* @param recursive [in]: whether to obtain recursive in list
|
||||
*/
|
||||
std::set<App::DocumentObject*> getInListEx(bool recursive) const;
|
||||
|
||||
/// get group if object is part of a group, otherwise 0 is returned
|
||||
DocumentObjectGroup* getGroup() const;
|
||||
|
||||
@@ -200,8 +287,11 @@ public:
|
||||
*/
|
||||
virtual short mustExecute(void) const;
|
||||
|
||||
/// Recompute only this feature
|
||||
bool recomputeFeature();
|
||||
/** Recompute only this feature
|
||||
*
|
||||
* @param recursive: set to true to recompute any dependent objects as well
|
||||
*/
|
||||
bool recomputeFeature(bool recursive=false);
|
||||
|
||||
/// get the status Message
|
||||
const char *getStatusString(void) const;
|
||||
@@ -213,9 +303,101 @@ public:
|
||||
* additional or different behavior.
|
||||
*/
|
||||
virtual void onLostLinkToObject(DocumentObject*);
|
||||
virtual PyObject *getPyObject(void);
|
||||
/// its used to get the python sub objects by name (e.g. by the selection)
|
||||
virtual std::vector<PyObject *> getPySubObjects(const std::vector<std::string>&) const;
|
||||
virtual PyObject *getPyObject(void) override;
|
||||
|
||||
/** Get the sub element/object by name
|
||||
*
|
||||
* @param subname: a string which is dot separated name to refer to a sub
|
||||
* element or object. An empty string can be used to refer to the object
|
||||
* itself
|
||||
*
|
||||
* @param pyObj: if non zero, returns the python object corresponding to
|
||||
* this sub object. The actual type of this python object is implementation
|
||||
* dependent. For example, The current implementation of Part::Feature will
|
||||
* return the TopoShapePy, event if there is no sub-element reference, in
|
||||
* which case it returns the whole shape.
|
||||
*
|
||||
* @param mat: If non zero, it is used as the current transformation matrix
|
||||
* on input. And output as the accumulated transformation up until and
|
||||
* include the transformation applied by the final object reference in \c
|
||||
* subname. For Part::Feature, the transformation is applied to the
|
||||
* TopoShape inside \c pyObj before returning.
|
||||
*
|
||||
* @param transform: if false, then it will not apply the object's own
|
||||
* transformation to \c mat, which lets you override the object's placement
|
||||
* (and possibly scale).
|
||||
*
|
||||
* @param depth: depth limitation as hint for cyclic link detection
|
||||
*
|
||||
* @return The last document object referred in subname. If subname is empty,
|
||||
* then it shall return itself. If subname is invalid, then it shall return
|
||||
* zero.
|
||||
*/
|
||||
virtual DocumentObject *getSubObject(const char *subname, PyObject **pyObj=0,
|
||||
Base::Matrix4D *mat=0, bool transform=true, int depth=0) const;
|
||||
|
||||
/// Return a list of objects referenced by a given subname including this object
|
||||
std::vector<DocumentObject*> getSubObjectList(const char *subname) const;
|
||||
|
||||
/// reason of calling getSubObjects()
|
||||
enum GSReason {
|
||||
/// default, mostly for exporting shape objects
|
||||
GS_DEFAULT,
|
||||
/// for element selection
|
||||
GS_SELECT,
|
||||
};
|
||||
|
||||
/** Return name reference of all sub-objects
|
||||
*
|
||||
* @param reason: indicate reason of obtaining the sub objects
|
||||
*
|
||||
* The default implementation returns all object references in
|
||||
* PropertyLink, and PropertyLinkList, if any
|
||||
*
|
||||
* @return Return a vector of subname references for all sub-objects. In
|
||||
* most cases, the name returned will be the object name plus an ending
|
||||
* '.', which can be passed directly to getSubObject() to retrieve the
|
||||
* name. The reason to return the name reference instead of the sub object
|
||||
* itself is because there may be no real sub object, or the sub object
|
||||
* need special transformation. For example, sub objects of an array type
|
||||
* of object.
|
||||
*/
|
||||
virtual std::vector<std::string> getSubObjects(int reason=0) const;
|
||||
|
||||
///Obtain top parents and subnames of this object using its InList
|
||||
std::vector<std::pair<App::DocumentObject*,std::string> > getParents(int depth=0) const;
|
||||
|
||||
/** Return the linked object with optional transformation
|
||||
*
|
||||
* @param recurse: If false, return the immediate linked object, or else
|
||||
* recursively call this function to return the final linked object.
|
||||
*
|
||||
* @param mat: If non zero, it is used as the current transformation matrix
|
||||
* on input. And output as the accumulated transformation till the final
|
||||
* linked object.
|
||||
*
|
||||
* @param transform: if false, then it will not accumulate the object's own
|
||||
* placement into \c mat, which lets you override the object's placement.
|
||||
*
|
||||
* @return Return the linked object. This function must return itself if the
|
||||
* it is not a link or the link is invalid.
|
||||
*/
|
||||
virtual DocumentObject *getLinkedObject(bool recurse=true,
|
||||
Base::Matrix4D *mat=0, bool transform=false, int depth=0) const;
|
||||
|
||||
/* Return true to cause PropertyView to show linked object's property */
|
||||
virtual bool canLinkProperties() const {return true;}
|
||||
|
||||
/* Return true to bypass duplicate label checking */
|
||||
virtual bool allowDuplicateLabel() const {return false;}
|
||||
|
||||
/*** Called to let object itself control relabeling
|
||||
*
|
||||
* @param newLabel: input as the new label, which can be modified by object itself
|
||||
*
|
||||
* This function is is called before onBeforeChange()
|
||||
*/
|
||||
virtual void onBeforeChangeLabel(std::string &newLabel) {(void)newLabel;}
|
||||
|
||||
friend class Document;
|
||||
friend class Transaction;
|
||||
@@ -223,20 +405,144 @@ public:
|
||||
|
||||
static DocumentObjectExecReturn *StdReturn;
|
||||
|
||||
virtual void Save (Base::Writer &writer) const;
|
||||
virtual void Save (Base::Writer &writer) const override;
|
||||
|
||||
/* Expression support */
|
||||
|
||||
virtual void setExpression(const ObjectIdentifier & path, boost::shared_ptr<App::Expression> expr, const char *comment = 0);
|
||||
virtual void setExpression(const ObjectIdentifier & path, boost::shared_ptr<App::Expression> expr);
|
||||
|
||||
virtual const PropertyExpressionEngine::ExpressionInfo getExpression(const ObjectIdentifier &path) const;
|
||||
|
||||
virtual void renameObjectIdentifiers(const std::map<App::ObjectIdentifier, App::ObjectIdentifier> & paths);
|
||||
|
||||
virtual void connectRelabelSignals();
|
||||
|
||||
const std::string & getOldLabel() const { return oldLabel; }
|
||||
|
||||
const char *getViewProviderNameStored() const {
|
||||
return _pcViewProviderName.c_str();
|
||||
}
|
||||
|
||||
virtual bool removeDynamicProperty(const char* prop) override;
|
||||
|
||||
virtual App::Property* addDynamicProperty(
|
||||
const char* type, const char* name=0,
|
||||
const char* group=0, const char* doc=0,
|
||||
short attr=0, bool ro=false, bool hidden=false) override;
|
||||
|
||||
/** Resolve the last document object referenced in the subname
|
||||
*
|
||||
* @param subname: dot separated subname
|
||||
* @param parent: return the direct parent of the object
|
||||
* @param childName: return child name to be passed to is/setElementVisible()
|
||||
* @param subElement: return non-object sub-element name if found. The
|
||||
* pointer is guaranteed to be within the buffer pointed to by 'subname'
|
||||
*
|
||||
* @sa getSubObject()
|
||||
* @return Returns the last referenced document object in the subname. If no
|
||||
* such object in subname, return pObject.
|
||||
*/
|
||||
App::DocumentObject *resolve(const char *subname, App::DocumentObject **parent=0,
|
||||
std::string *childName=0, const char **subElement=0,
|
||||
PyObject **pyObj=0, Base::Matrix4D *mat=0, bool transform=true, int depth=0) const;
|
||||
|
||||
/** Resolve a link reference that is relative to this object reference
|
||||
*
|
||||
* @param subname: on input, this is the subname reference to the object
|
||||
* that is to be assigned a link. On output, the reference may be offsetted
|
||||
* to be rid off any common parent.
|
||||
* @param link: on input, this is the top parent of the link reference. On
|
||||
* output, it may be altered to one of its child to be rid off any common
|
||||
* parent.
|
||||
* @param linkSub: on input, this the subname of the link reference. On
|
||||
* output, it may be offsetted to be rid off any common parent.
|
||||
*
|
||||
* @return The corrected top parent of the object that is to be assigned the
|
||||
* link. If the output 'subname' is empty, then return the object itself.
|
||||
*
|
||||
* To avoid any cyclic reference, an object must not be assign a link to any
|
||||
* of the object in its parent. This function can be used to resolve any
|
||||
* common parents of an object and its link target.
|
||||
*
|
||||
* For example, with the following object hierarchy
|
||||
*
|
||||
* Group
|
||||
* |--Group001
|
||||
* | |--Box
|
||||
* | |--Cylinder
|
||||
* |--Group002
|
||||
* |--Box001
|
||||
* |--Cylinder001
|
||||
*
|
||||
* If you want add a link of Group.Group002.Box001 to Group.Group001, you
|
||||
* can call with the following parameter (which are usually obtained from
|
||||
* Selection.getSelectionEx(), check usage in TreeWidget::onDropEvent()):
|
||||
* std::string subname("Group002.");
|
||||
* auto link = Group;
|
||||
* std::string linkSub("Group001.Box001.");
|
||||
* parent = Group.resolveRelativeLink(subname,link,linkSub);
|
||||
*
|
||||
* The resolving result is as follow:
|
||||
* return -> Group001
|
||||
* subname -> ""
|
||||
* link -> Group002
|
||||
* linkSub -> "Box001."
|
||||
*
|
||||
* The common parent 'Group' is removed.
|
||||
*/
|
||||
App::DocumentObject *resolveRelativeLink(std::string &subname,
|
||||
App::DocumentObject *&link, std::string &linkSub) const;
|
||||
|
||||
/** Called to adjust link properties to avoid cyclic links
|
||||
*
|
||||
* @param inList: the recursive in-list of the future parent object,
|
||||
* including the parent itself.
|
||||
* @param visited: optional set holding the visited objects. If null then
|
||||
* only this object is adjusted, or else all object inside the out-list of
|
||||
* this object will be checked.
|
||||
*
|
||||
* @return Return whether the object has been modified
|
||||
*
|
||||
* This function tries to adjust any relative link properties (i.e. link
|
||||
* properties that can hold subnames) to avoid cyclic when added to the
|
||||
* future parent.
|
||||
*/
|
||||
virtual bool adjustRelativeLinks(const std::set<App::DocumentObject*> &inList,
|
||||
std::set<App::DocumentObject*> *visited=0);
|
||||
|
||||
/** allow partial loading of dependent objects
|
||||
*
|
||||
* @return Returns 0 means do not support partial loading. 1 means allow
|
||||
* dependent objects to be partially loaded, i.e. only create, but not
|
||||
* restored. 2 means this object itself can be partially loaded.
|
||||
*/
|
||||
virtual int canLoadPartial() const {return 0;}
|
||||
|
||||
virtual void onUpdateElementReference(const Property *) {}
|
||||
|
||||
/** Allow object to redirect a subname path
|
||||
*
|
||||
* @param ss: input as the current subname path from \a topParent leading
|
||||
* just before this object, i.e. ends at the parent of this object. This
|
||||
* function should append its own name to this path, or redirect the
|
||||
* subname to other place.
|
||||
* @param topParent: top parent of this subname path
|
||||
* @param child: the immediate child object in the path
|
||||
*
|
||||
* This function is called by tree view to generate a subname path when an
|
||||
* item is selected in the tree. Document object can use this function to
|
||||
* redirect the selection to some other objects.
|
||||
*/
|
||||
virtual bool redirectSubName(std::ostringstream &ss,
|
||||
DocumentObject *topParent, DocumentObject *child) const;
|
||||
|
||||
/** Special marker to mark the object as hidden
|
||||
*
|
||||
* It is used by Gui::ViewProvider::getElementColors(), but exposed here
|
||||
* for convenience
|
||||
*/
|
||||
static const std::string &hiddenMarker();
|
||||
/// Check if the subname reference ends with hidden marker.
|
||||
static const char *hasHiddenMarker(const char *subname);
|
||||
|
||||
protected:
|
||||
/// recompute only this object
|
||||
virtual App::DocumentObjectExecReturn *recompute(void);
|
||||
@@ -269,12 +575,10 @@ protected:
|
||||
void resetError(void){StatusBits.reset(ObjectStatus::Error);}
|
||||
void setDocument(App::Document* doc);
|
||||
|
||||
/// \internal get called when removing a property of name \a prop
|
||||
void onAboutToRemoveProperty(const char* prop);
|
||||
/// get called before the value is changed
|
||||
virtual void onBeforeChange(const Property* prop);
|
||||
virtual void onBeforeChange(const Property* prop) override;
|
||||
/// get called by the container when a property was changed
|
||||
virtual void onChanged(const Property* prop);
|
||||
virtual void onChanged(const Property* prop) override;
|
||||
/// get called after a document has been fully restored
|
||||
virtual void onDocumentRestored();
|
||||
/// get called after setting the document
|
||||
@@ -284,27 +588,35 @@ protected:
|
||||
/// get called when object is going to be removed from the document
|
||||
virtual void unsetupObject();
|
||||
|
||||
/// get called when a property status has changed
|
||||
virtual void onPropertyStatusChanged(const Property &prop, unsigned long oldStatus) override;
|
||||
|
||||
/// python object of this class and all descendent
|
||||
protected: // attributes
|
||||
Py::Object PythonObject;
|
||||
/// pointer to the document this object belongs to
|
||||
App::Document* _pDoc;
|
||||
|
||||
// Connections to track relabeling of document and document objects
|
||||
boost::signals2::scoped_connection onRelabledDocumentConnection;
|
||||
boost::signals2::scoped_connection onRelabledObjectConnection;
|
||||
boost::signals2::scoped_connection onDeletedObjectConnection;
|
||||
|
||||
/// Old label; used for renaming expressions
|
||||
std::string oldLabel;
|
||||
|
||||
// pointer to the document name string (for performance)
|
||||
const std::string *pcNameInDocument;
|
||||
|
||||
private:
|
||||
// accessed by App::Document to record and restore the correct view provider type
|
||||
std::string _pcViewProviderName;
|
||||
|
||||
// unique identifier (ammong a document) of this object.
|
||||
long _Id;
|
||||
|
||||
private:
|
||||
// Back pointer to all the fathers in a DAG of the document
|
||||
// this is used by the document (via friend) to have a effective DAG handling
|
||||
std::vector<App::DocumentObject*> _inList;
|
||||
mutable std::vector<App::DocumentObject *> _outList;
|
||||
mutable std::unordered_map<const char *, App::DocumentObject*, CStringHasher, CStringHasher> _outListMap;
|
||||
mutable bool _outListCached = false;
|
||||
};
|
||||
|
||||
} //namespace App
|
||||
|
||||
@@ -91,3 +91,20 @@ DocumentObject* DocumentObjectExtension::getExtendedObject() {
|
||||
assert(getExtendedContainer()->isDerivedFrom(DocumentObject::getClassTypeId()));
|
||||
return static_cast<DocumentObject*>(getExtendedContainer());
|
||||
}
|
||||
|
||||
bool DocumentObjectExtension::extensionGetSubObject(DocumentObject *&,
|
||||
const char *, PyObject **, Base::Matrix4D *, bool, int) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DocumentObjectExtension::extensionGetSubObjects(std::vector<std::string>&, int) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DocumentObjectExtension::extensionGetLinkedObject(
|
||||
DocumentObject *&, bool, Base::Matrix4D *, bool, int) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -61,12 +61,39 @@ public:
|
||||
virtual void onExtendedSetupObject();
|
||||
/// get called when object is going to be removed from the document
|
||||
virtual void onExtendedUnsetupObject();
|
||||
|
||||
|
||||
virtual PyObject* getExtensionPyObject(void) override;
|
||||
|
||||
/// returns the type name of the ViewProviderExtension which is automatically attached
|
||||
/// to the viewprovider object when it is initiated
|
||||
virtual const char* getViewProviderExtensionName(void) const {return "";}
|
||||
|
||||
/** Get the sub object by name
|
||||
* @sa DocumentObject::getSubObject()
|
||||
*
|
||||
* @return Return turn if handled, the sub object is returned in \c ret
|
||||
*/
|
||||
virtual bool extensionGetSubObject( DocumentObject *&ret, const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const;
|
||||
|
||||
/** Get name references of all sub objects
|
||||
* @sa DocumentObject::getSubObjects()
|
||||
*
|
||||
* @return Return turn if handled, the sub object is returned in \c ret
|
||||
*/
|
||||
virtual bool extensionGetSubObjects(std::vector<std::string> &ret, int reason) const;
|
||||
|
||||
/** Get the linked object
|
||||
* @sa DocumentObject::getLinkedObject()
|
||||
*
|
||||
* @return Return turn if handled, the linked object is returned in \c ret
|
||||
*/
|
||||
virtual bool extensionGetLinkedObject(DocumentObject *&ret, bool recursive,
|
||||
Base::Matrix4D *mat, bool transform, int depth) const;
|
||||
|
||||
virtual int extensionSetElementVisible(const char *, bool) {return -1;}
|
||||
virtual int extensionIsElementVisible(const char *) {return -1;}
|
||||
virtual bool extensionHasChildElement() const {return false;}
|
||||
};
|
||||
|
||||
} //App
|
||||
|
||||
@@ -44,11 +44,11 @@ public:
|
||||
virtual ~DocumentObjectGroup();
|
||||
|
||||
/// returns the type name of the ViewProvider
|
||||
virtual const char* getViewProviderName(void) const {
|
||||
virtual const char* getViewProviderName(void) const override{
|
||||
return "Gui::ViewProviderDocumentObjectGroup";
|
||||
}
|
||||
|
||||
virtual PyObject *getPyObject(void);
|
||||
virtual PyObject *getPyObject(void) override;
|
||||
};
|
||||
|
||||
typedef App::FeaturePythonT<DocumentObjectGroup> DocumentObjectGroupPython;
|
||||
|
||||
@@ -57,9 +57,93 @@
|
||||
</Methode>
|
||||
<Methode Name="recompute">
|
||||
<Documentation>
|
||||
<UserDocu>Recomputes this object</UserDocu>
|
||||
<UserDocu>recompute(recursive=False): Recomputes this object</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getSubObject" Keyword="true">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
getSubObject(subname, retType=0, matrix=None, transform=True, depth=0)
|
||||
|
||||
* subname(string|list|tuple): dot separated string or sequence of strings
|
||||
referencing subobject.
|
||||
|
||||
* retType: return type, 0=PyObject, 1=DocObject, 2=DocAndPyObject, 3=Placement
|
||||
|
||||
PyObject: return a python binding object for the (sub)object referenced in
|
||||
each 'subname' The actual type of 'PyObject' is implementation dependent.
|
||||
For Part::Feature compatible objects, this will be of type TopoShapePy and
|
||||
pre-transformed by accumulated transformation matrix along the object path.
|
||||
|
||||
DocObject: return the document object referenced in subname, if 'matrix' is
|
||||
None. Or, return a tuple (object, matrix) for each 'subname' and 'matrix' is
|
||||
the accumulated transformation matrix for the sub object.
|
||||
|
||||
DocAndPyObject: return a tuple (object, matrix, pyobj) for each subname
|
||||
|
||||
Placement: return a trasformed placement of the sub-object
|
||||
|
||||
* matrix: the initial transformation to be applied to the sub object.
|
||||
|
||||
* transform: whether to transform the sub object using this object's placement
|
||||
|
||||
* depth: current recursive depth
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getSubObjectList">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
getSubObjectList(subname)
|
||||
|
||||
Return a list of objects referenced by a given subname including this object
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getSubObjects">
|
||||
<Documentation>
|
||||
<UserDocu>getSubObjects(reason=0): Return subname reference of all sub-objects</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getLinkedObject" Keyword="true">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
getLinkedObject(recursive=True, matrix=None, transform=True, depth=0)
|
||||
Returns the linked object if there is one, or else return itself
|
||||
|
||||
* recursive: whether to recursively resolve the links
|
||||
|
||||
* transform: whether to transform the sub object using this object's placement
|
||||
|
||||
* matrix: If not none, this specifies the initial transformation to be applied
|
||||
to the sub object. And cause the method to return a tuple (object, matrix)
|
||||
containing the accumulated transformation matrix
|
||||
|
||||
* depth: current recursive depth
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="setElementVisible">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
setElementVisible(element,visible): Set the visibility of a child element
|
||||
Return -1 if element visibility is not supported, 0 if element not found, 1 if success
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="isElementVisible">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
isElementVisible(element): Check if a child element is visible
|
||||
Return -1 if element visibility is not supported or element not found, 0 if invisible, or else 1
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="hasChildElement">
|
||||
<Documentation>
|
||||
<UserDocu>Return true to indicate the object having child elements</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getParentGroup">
|
||||
<Documentation>
|
||||
<UserDocu>Returns the group the object is in or None if it is not part of a group.
|
||||
@@ -79,6 +163,37 @@
|
||||
<UserDocu>Get all paths from this object to another object following the OutList.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="resolve" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
resolve(subname) -- resolve the sub object
|
||||
|
||||
Returns a tuple (subobj,parent,elementName,subElement), where 'subobj' is the
|
||||
last object referenced in 'subname', and 'parent' is the direct parent of
|
||||
'subobj', and 'elementName' is the name of the subobj, which can be used
|
||||
to call parent.isElementVisible/setElementVisible(). 'subElement' is the
|
||||
non-object sub-element name if any.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="resolveSubElement" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
resolveSubElement(subname,append,type) -- resolve both new and old style sub element
|
||||
|
||||
subname: subname reference containing object hierarchy
|
||||
append: Whether to append object hierarchy prefix inside subname to returned element name
|
||||
type: 0: normal, 1: for import, 2: for export
|
||||
|
||||
Return tuple(obj,newElementName,oldElementName)
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="adjustRelativeLinks">
|
||||
<Documentation>
|
||||
<UserDocu>adjustRelativeLinks(parent,recursive=True) -- auto correct potential cyclic dependencies</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Attribute Name="OutList" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>A list of all objects this object links to.</UserDocu>
|
||||
@@ -103,6 +218,12 @@
|
||||
</Documentation>
|
||||
<Parameter Name="InListRecursive" Type="List"/>
|
||||
</Attribute>
|
||||
<Attribute Name="FullName" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Return the document name and internal name of this object</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="FullName" Type="String"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Name" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Return the internal name of this object</UserDocu>
|
||||
@@ -129,5 +250,41 @@ or None if the GUI is not up</UserDocu>
|
||||
<Parameter Name="ViewObject" Type="Object"/>
|
||||
</Attribute>
|
||||
<CustomAttributes />
|
||||
<Attribute Name="MustExecute" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Check if the object must be recomputed</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="MustExecute" Type="Boolean"/>
|
||||
</Attribute>
|
||||
<Attribute Name="ID" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>The unique identifier (among its document) of this object</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="ID" Type="Int"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Removing" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Indicate if the object is being removed</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Removing" Type="Boolean"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Parents" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>A List of tuple(parent,subname) holding all parents to this object</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Parents" Type="List"/>
|
||||
</Attribute>
|
||||
<Attribute Name="OldLabel" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Contains the old label before change</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="OldLabel" Type="String"/>
|
||||
</Attribute>
|
||||
<Attribute Name="NoTouch">
|
||||
<Documentation>
|
||||
<UserDocu>Enable/disable no touch on any property change</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="NoTouch" Type="Boolean"/>
|
||||
</Attribute>
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
|
||||
+427
-15
@@ -22,9 +22,12 @@
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#include <Base/GeometryPyCXX.h>
|
||||
#include <Base/MatrixPy.h>
|
||||
#include "DocumentObject.h"
|
||||
#include "Document.h"
|
||||
#include "Expression.h"
|
||||
#include "GeoFeature.h"
|
||||
#include "GroupExtension.h"
|
||||
#include "GeoFeatureGroupExtension.h"
|
||||
|
||||
@@ -53,6 +56,11 @@ Py::String DocumentObjectPy::getName(void) const
|
||||
return Py::String(std::string(internal));
|
||||
}
|
||||
|
||||
Py::String DocumentObjectPy::getFullName(void) const
|
||||
{
|
||||
return Py::String(getDocumentObjectPtr()->getFullName());
|
||||
}
|
||||
|
||||
Py::Object DocumentObjectPy::getDocument(void) const
|
||||
{
|
||||
DocumentObject* object = this->getDocumentObjectPtr();
|
||||
@@ -132,8 +140,21 @@ PyObject* DocumentObjectPy::supportedProperties(PyObject *args)
|
||||
|
||||
PyObject* DocumentObjectPy::touch(PyObject * args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) // convert args: Python->C
|
||||
char *propName = 0;
|
||||
if (!PyArg_ParseTuple(args, "|s",&propName)) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
if(propName) {
|
||||
if(!propName[0]) {
|
||||
getDocumentObjectPtr()->touch(true);
|
||||
Py_Return;
|
||||
}
|
||||
auto prop = getDocumentObjectPtr()->getPropertyByName(propName);
|
||||
if(!prop)
|
||||
throw Py::RuntimeError("Property not found");
|
||||
prop->touch();
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
getDocumentObjectPtr()->touch();
|
||||
Py_Return;
|
||||
}
|
||||
@@ -171,6 +192,9 @@ Py::List DocumentObjectPy::getState(void) const
|
||||
uptodate = false;
|
||||
list.append(Py::String("Recompute"));
|
||||
}
|
||||
if (object->testStatus(App::Recompute2)) {
|
||||
list.append(Py::String("Recompute2"));
|
||||
}
|
||||
if (object->isRestoring()) {
|
||||
uptodate = false;
|
||||
list.append(Py::String("Restore"));
|
||||
@@ -178,6 +202,12 @@ Py::List DocumentObjectPy::getState(void) const
|
||||
if (object->testStatus(App::Expand)){
|
||||
list.append(Py::String("Expanded"));
|
||||
}
|
||||
if (object->testStatus(App::PartialObject)){
|
||||
list.append(Py::String("Partial"));
|
||||
}
|
||||
if (object->testStatus(App::ObjImporting)){
|
||||
list.append(Py::String("Importing"));
|
||||
}
|
||||
if (uptodate) {
|
||||
list.append(Py::String("Up-to-date"));
|
||||
}
|
||||
@@ -187,21 +217,36 @@ Py::List DocumentObjectPy::getState(void) const
|
||||
Py::Object DocumentObjectPy::getViewObject(void) const
|
||||
{
|
||||
try {
|
||||
PyObject *dict = PySys_GetObject("modules");
|
||||
if (!dict) {
|
||||
return Py::None();
|
||||
}
|
||||
|
||||
// check if the FreeCADGui module is already loaded, if not then don't try to load it
|
||||
Py::Dict sysmod(dict);
|
||||
if (!sysmod.hasKey("FreeCADGui")) {
|
||||
return Py::None();
|
||||
}
|
||||
|
||||
// double-check that the module doesn't have a null pointer
|
||||
Py::Module module(PyImport_ImportModule("FreeCADGui"),true);
|
||||
if (!module.hasAttr("getDocument")) {
|
||||
if (module.isNull() || !module.hasAttr("getDocument")) {
|
||||
// in v0.14+, the GUI module can be loaded in console mode (but doesn't have all its document methods)
|
||||
return Py::None();
|
||||
}
|
||||
Py::Callable method(module.getAttr("getDocument"));
|
||||
Py::Tuple arg(1);
|
||||
arg.setItem(0, Py::String(getDocumentObjectPtr()->getDocument()->getName()));
|
||||
Py::Object doc = method.apply(arg);
|
||||
method = doc.getAttr("getObject");
|
||||
if(!getDocumentObjectPtr()->getDocument()) {
|
||||
throw Py::RuntimeError("Object has no document");
|
||||
}
|
||||
const char* internalName = getDocumentObjectPtr()->getNameInDocument();
|
||||
if (!internalName) {
|
||||
throw Py::RuntimeError("Object has been removed from document");
|
||||
}
|
||||
|
||||
Py::Callable method(module.getAttr("getDocument"));
|
||||
Py::Tuple arg(1);
|
||||
arg.setItem(0, Py::String(getDocumentObjectPtr()->getDocument()->getName()));
|
||||
Py::Object doc = method.apply(arg);
|
||||
method = doc.getAttr("getObject");
|
||||
arg.setItem(0, Py::String(internalName));
|
||||
Py::Object obj = method.apply(arg);
|
||||
return obj;
|
||||
@@ -292,9 +337,11 @@ PyObject* DocumentObjectPy::setExpression(PyObject * args)
|
||||
else if (PyString_Check(expr)) {
|
||||
const char * exprStr = PyString_AsString(expr);
|
||||
#endif
|
||||
boost::shared_ptr<Expression> shared_expr(ExpressionParser::parse(getDocumentObjectPtr(), exprStr));
|
||||
boost::shared_ptr<Expression> shared_expr(Expression::parse(getDocumentObjectPtr(), exprStr));
|
||||
if(shared_expr && comment)
|
||||
shared_expr->comment = comment;
|
||||
|
||||
getDocumentObjectPtr()->setExpression(p, shared_expr, comment);
|
||||
getDocumentObjectPtr()->setExpression(p, shared_expr);
|
||||
}
|
||||
else if (PyUnicode_Check(expr)) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
@@ -306,7 +353,9 @@ PyObject* DocumentObjectPy::setExpression(PyObject * args)
|
||||
Py_DECREF(unicode);
|
||||
boost::shared_ptr<Expression> shared_expr(ExpressionParser::parse(getDocumentObjectPtr(), exprStr.c_str()));
|
||||
|
||||
getDocumentObjectPtr()->setExpression(p, shared_expr, comment);
|
||||
if(shared_expr && comment)
|
||||
shared_expr->comment = comment;
|
||||
getDocumentObjectPtr()->setExpression(p, shared_expr);
|
||||
}
|
||||
else {
|
||||
// utf-8 encoding failed
|
||||
@@ -321,11 +370,12 @@ PyObject* DocumentObjectPy::setExpression(PyObject * args)
|
||||
|
||||
PyObject* DocumentObjectPy::recompute(PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
PyObject *recursive=Py_False;
|
||||
if (!PyArg_ParseTuple(args, "|O",&recursive))
|
||||
return NULL;
|
||||
|
||||
try {
|
||||
bool ok = getDocumentObjectPtr()->recomputeFeature();
|
||||
bool ok = getDocumentObjectPtr()->recomputeFeature(PyObject_IsTrue(recursive));
|
||||
return Py_BuildValue("O", (ok ? Py_True : Py_False));
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
@@ -333,6 +383,247 @@ PyObject* DocumentObjectPy::recompute(PyObject *args)
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::getSubObject(PyObject *args, PyObject *keywds)
|
||||
{
|
||||
PyObject *obj;
|
||||
short retType = 0;
|
||||
PyObject *pyMat = Py_None;
|
||||
PyObject *doTransform = Py_True;
|
||||
short depth = 0;
|
||||
static char *kwlist[] = {"subname","retType","matrix","transform","depth", NULL};
|
||||
if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|hOOh", kwlist,
|
||||
&obj,&retType,&pyMat,&doTransform,&depth))
|
||||
return 0;
|
||||
|
||||
if(retType<0 || retType>6) {
|
||||
PyErr_SetString(PyExc_TypeError, "invalid retType, can only be integer 0~6");
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> subs;
|
||||
bool single=true;
|
||||
if (PyUnicode_Check(obj)) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
subs.push_back(PyUnicode_AsUTF8(obj));
|
||||
#else
|
||||
PyObject* unicode = PyUnicode_AsUTF8String(obj);
|
||||
subs.push_back(PyString_AsString(unicode));
|
||||
Py_DECREF(unicode);
|
||||
}
|
||||
else if (PyString_Check(obj)) {
|
||||
subs.push_back(PyString_AsString(obj));
|
||||
#endif
|
||||
} else if (PySequence_Check(obj)) {
|
||||
single=false;
|
||||
Py::Sequence shapeSeq(obj);
|
||||
for (Py::Sequence::iterator it = shapeSeq.begin(); it != shapeSeq.end(); ++it) {
|
||||
PyObject* item = (*it).ptr();
|
||||
if (PyUnicode_Check(item)) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
subs.push_back(PyUnicode_AsUTF8(item));
|
||||
#else
|
||||
PyObject* unicode = PyUnicode_AsUTF8String(item);
|
||||
subs.push_back(PyString_AsString(unicode));
|
||||
Py_DECREF(unicode);
|
||||
}
|
||||
else if (PyString_Check(item)) {
|
||||
subs.push_back(PyString_AsString(item));
|
||||
#endif
|
||||
}else{
|
||||
PyErr_SetString(PyExc_TypeError, "non-string object in sequence");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
PyErr_SetString(PyExc_TypeError, "subname must be either a string or sequence of string");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool transform = PyObject_IsTrue(doTransform);
|
||||
|
||||
struct SubInfo {
|
||||
App::DocumentObject *sobj;
|
||||
Py::Object obj;
|
||||
Py::Object pyObj;
|
||||
Base::Matrix4D mat;
|
||||
SubInfo(const Base::Matrix4D &mat):mat(mat){}
|
||||
};
|
||||
|
||||
Base::Matrix4D mat;
|
||||
if(pyMat!=Py_None) {
|
||||
if(!PyObject_TypeCheck(pyMat,&Base::MatrixPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError, "expect argument 'matrix' to be of type Base.Matrix");
|
||||
return 0;
|
||||
}
|
||||
mat = *static_cast<Base::MatrixPy*>(pyMat)->getMatrixPtr();
|
||||
}
|
||||
|
||||
PY_TRY {
|
||||
std::vector<SubInfo> ret;
|
||||
for(const auto &sub : subs) {
|
||||
ret.emplace_back(mat);
|
||||
auto &info = ret.back();
|
||||
PyObject *pyObj = 0;
|
||||
info.sobj = getDocumentObjectPtr()->getSubObject(
|
||||
sub.c_str(),retType!=0&&retType!=2?0:&pyObj,&info.mat,transform,depth);
|
||||
if(pyObj)
|
||||
info.pyObj = Py::Object(pyObj,true);
|
||||
if(info.sobj)
|
||||
info.obj = Py::Object(info.sobj->getPyObject(),true);
|
||||
}
|
||||
if(ret.empty())
|
||||
Py_Return;
|
||||
|
||||
if(single) {
|
||||
if(retType==0)
|
||||
return Py::new_reference_to(ret[0].pyObj);
|
||||
else if(retType==1 && pyMat==Py_None)
|
||||
return Py::new_reference_to(ret[0].obj);
|
||||
else if(!ret[0].sobj)
|
||||
Py_Return;
|
||||
else if(retType==3)
|
||||
return Py::new_reference_to(Py::Placement(Base::Placement(ret[0].mat)));
|
||||
else if(retType==4)
|
||||
return Py::new_reference_to(Py::Matrix(ret[0].mat));
|
||||
else if(retType==5 || retType==6) {
|
||||
ret[0].sobj->getLinkedObject(true,&ret[0].mat,false);
|
||||
if(retType==5)
|
||||
return Py::new_reference_to(Py::Placement(Base::Placement(ret[0].mat)));
|
||||
else
|
||||
return Py::new_reference_to(Py::Matrix(ret[0].mat));
|
||||
}
|
||||
Py::Tuple rret(retType==1?2:3);
|
||||
rret.setItem(0,ret[0].obj);
|
||||
rret.setItem(1,Py::Object(new Base::MatrixPy(ret[0].mat)));
|
||||
if(retType!=1)
|
||||
rret.setItem(2,ret[0].pyObj);
|
||||
return Py::new_reference_to(rret);
|
||||
}
|
||||
Py::Tuple tuple(ret.size());
|
||||
for(size_t i=0;i<ret.size();++i) {
|
||||
if(retType==0)
|
||||
tuple.setItem(i,ret[i].pyObj);
|
||||
else if(retType==1 && pyMat==Py_None)
|
||||
tuple.setItem(i,ret[i].obj);
|
||||
else if(!ret[i].sobj)
|
||||
tuple.setItem(i, Py::Object());
|
||||
else if(retType==3)
|
||||
tuple.setItem(i,Py::Placement(Base::Placement(ret[0].mat)));
|
||||
else if(retType==4)
|
||||
tuple.setItem(i,Py::Matrix(ret[0].mat));
|
||||
else if(retType==5 || retType==6) {
|
||||
ret[i].sobj->getLinkedObject(true,&ret[i].mat,false);
|
||||
if(retType==5)
|
||||
tuple.setItem(i,Py::Placement(Base::Placement(ret[i].mat)));
|
||||
else
|
||||
tuple.setItem(i,Py::Matrix(ret[i].mat));
|
||||
} else {
|
||||
Py::Tuple rret(retType==1?2:3);
|
||||
rret.setItem(0,ret[i].obj);
|
||||
rret.setItem(1,Py::Object(new Base::MatrixPy(ret[i].mat)));
|
||||
if(retType!=1)
|
||||
rret.setItem(2,ret[i].pyObj);
|
||||
tuple.setItem(i,rret);
|
||||
}
|
||||
}
|
||||
return Py::new_reference_to(tuple);
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::getSubObjectList(PyObject *args) {
|
||||
const char *subname;
|
||||
if (!PyArg_ParseTuple(args, "s", &subname))
|
||||
return NULL;
|
||||
Py::List res;
|
||||
PY_TRY {
|
||||
for(auto o : getDocumentObjectPtr()->getSubObjectList(subname))
|
||||
res.append(Py::asObject(o->getPyObject()));
|
||||
return Py::new_reference_to(res);
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::getSubObjects(PyObject *args) {
|
||||
int reason = 0;
|
||||
if (!PyArg_ParseTuple(args, "|i", &reason))
|
||||
return NULL;
|
||||
|
||||
PY_TRY {
|
||||
auto names = getDocumentObjectPtr()->getSubObjects(reason);
|
||||
Py::Tuple pyObjs(names.size());
|
||||
for(size_t i=0;i<names.size();++i)
|
||||
pyObjs.setItem(i,Py::String(names[i]));
|
||||
return Py::new_reference_to(pyObjs);
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::getLinkedObject(PyObject *args, PyObject *keywds)
|
||||
{
|
||||
PyObject *recursive = Py_True;
|
||||
PyObject *pyMat = Py_None;
|
||||
PyObject *transform = Py_True;
|
||||
short depth = 0;
|
||||
static char *kwlist[] = {"recursive","matrix","transform","depth", NULL};
|
||||
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|OOOh", kwlist,
|
||||
&recursive,&pyMat,&transform,&depth))
|
||||
return NULL;
|
||||
|
||||
Base::Matrix4D _mat;
|
||||
Base::Matrix4D *mat = 0;
|
||||
if(pyMat!=Py_None) {
|
||||
if(!PyObject_TypeCheck(pyMat,&Base::MatrixPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError, "expect argument 'matrix' to be of type Base.Matrix");
|
||||
return 0;
|
||||
}
|
||||
_mat = *static_cast<Base::MatrixPy*>(pyMat)->getMatrixPtr();
|
||||
mat = &_mat;
|
||||
}
|
||||
|
||||
PY_TRY {
|
||||
auto linked = getDocumentObjectPtr()->getLinkedObject(
|
||||
PyObject_IsTrue(recursive), mat, PyObject_IsTrue(transform),depth);
|
||||
if(!linked)
|
||||
linked = getDocumentObjectPtr();
|
||||
auto pyObj = Py::Object(linked->getPyObject(),true);
|
||||
if(mat) {
|
||||
Py::Tuple ret(2);
|
||||
ret.setItem(0,pyObj);
|
||||
ret.setItem(1,Py::Object(new Base::MatrixPy(*mat)));
|
||||
return Py::new_reference_to(ret);
|
||||
}
|
||||
return Py::new_reference_to(pyObj);
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::isElementVisible(PyObject *args)
|
||||
{
|
||||
char *element = 0;
|
||||
if (!PyArg_ParseTuple(args, "s", &element))
|
||||
return NULL;
|
||||
PY_TRY {
|
||||
return Py_BuildValue("h", getDocumentObjectPtr()->isElementVisible(element));
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::setElementVisible(PyObject *args)
|
||||
{
|
||||
char *element = 0;
|
||||
PyObject *visible = Py_True;
|
||||
if (!PyArg_ParseTuple(args, "s|O", &element,&visible))
|
||||
return NULL;
|
||||
PY_TRY {
|
||||
return Py_BuildValue("h", getDocumentObjectPtr()->setElementVisible(element,PyObject_IsTrue(visible)));
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::hasChildElement(PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
PY_TRY {
|
||||
return Py_BuildValue("O", getDocumentObjectPtr()->hasChildElement()?Py_True:Py_False);
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::getParentGroup(PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
@@ -369,6 +660,16 @@ PyObject* DocumentObjectPy::getParentGeoFeatureGroup(PyObject *args)
|
||||
}
|
||||
}
|
||||
|
||||
Py::Boolean DocumentObjectPy::getMustExecute() const
|
||||
{
|
||||
try {
|
||||
return Py::Boolean(getDocumentObjectPtr()->mustExecute()?true:false);
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
throw Py::RuntimeError(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::getPathsByOutList(PyObject *args)
|
||||
{
|
||||
PyObject* o;
|
||||
@@ -396,20 +697,39 @@ PyObject* DocumentObjectPy::getPathsByOutList(PyObject *args)
|
||||
|
||||
PyObject *DocumentObjectPy::getCustomAttributes(const char* attr) const
|
||||
{
|
||||
// Dynamic property is now directly supported in PropertyContainer. So we
|
||||
// can comment out here and let PropertyContainerPy handle it.
|
||||
#if 1
|
||||
(void)attr;
|
||||
#else
|
||||
// search for dynamic property
|
||||
Property* prop = getDocumentObjectPtr()->getDynamicPropertyByName(attr);
|
||||
if (prop)
|
||||
return prop->getPyObject();
|
||||
else
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DocumentObjectPy::setCustomAttributes(const char* attr, PyObject *obj)
|
||||
{
|
||||
// The following code is practically the same as in PropertyContainerPy,
|
||||
// especially since now dynamic property is directly supported in
|
||||
// PropertyContainer. So we can comment out here and let PropertyContainerPy
|
||||
// handle it.
|
||||
#if 1
|
||||
(void)attr;
|
||||
(void)obj;
|
||||
#else
|
||||
// explicitly search for dynamic property
|
||||
try {
|
||||
Property* prop = getDocumentObjectPtr()->getDynamicPropertyByName(attr);
|
||||
if (prop) {
|
||||
if(prop->testStatus(Property::Immutable)) {
|
||||
std::stringstream s;
|
||||
s << "'DocumentObject' attribute '" << attr << "' is read-only";
|
||||
throw Py::AttributeError(s.str());
|
||||
}
|
||||
prop->setPyObject(obj);
|
||||
return 1;
|
||||
}
|
||||
@@ -424,7 +744,9 @@ int DocumentObjectPy::setCustomAttributes(const char* attr, PyObject *obj)
|
||||
s << "Attribute (Name: " << attr << ") error: '" << exc.what() << "' ";
|
||||
throw Py::AttributeError(s.str());
|
||||
}
|
||||
catch (...) {
|
||||
catch (Py::AttributeError &) {
|
||||
throw;
|
||||
}catch (...) {
|
||||
std::stringstream s;
|
||||
s << "Unknown error in attribute " << attr;
|
||||
throw Py::AttributeError(s.str());
|
||||
@@ -434,8 +756,9 @@ int DocumentObjectPy::setCustomAttributes(const char* attr, PyObject *obj)
|
||||
Property *prop = getDocumentObjectPtr()->getPropertyByName(attr);
|
||||
if (prop) {
|
||||
// Read-only attributes must not be set over its Python interface
|
||||
short Type = getDocumentObjectPtr()->getPropertyType(prop);
|
||||
if (Type & Prop_ReadOnly) {
|
||||
if(prop->testStatus(Property::Immutable) ||
|
||||
(getDocumentObjectPtr()->getPropertyType(prop) & Prop_ReadOnly))
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "'DocumentObject' attribute '" << attr << "' is read-only";
|
||||
throw Py::AttributeError(s.str());
|
||||
@@ -451,6 +774,95 @@ int DocumentObjectPy::setCustomAttributes(const char* attr, PyObject *obj)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Py::Int DocumentObjectPy::getID() const {
|
||||
return Py::Int(getDocumentObjectPtr()->getID());
|
||||
}
|
||||
|
||||
Py::Boolean DocumentObjectPy::getRemoving() const {
|
||||
return Py::Boolean(getDocumentObjectPtr()->testStatus(ObjectStatus::Remove));
|
||||
}
|
||||
|
||||
PyObject *DocumentObjectPy::resolve(PyObject *args)
|
||||
{
|
||||
const char *subname;
|
||||
if (!PyArg_ParseTuple(args, "s",&subname))
|
||||
return NULL; // NULL triggers exception
|
||||
|
||||
PY_TRY {
|
||||
std::string elementName;
|
||||
const char *subElement = 0;
|
||||
App::DocumentObject *parent = 0;
|
||||
auto obj = getDocumentObjectPtr()->resolve(subname,&parent,&elementName,&subElement);
|
||||
|
||||
Py::Tuple ret(4);
|
||||
ret.setItem(0,obj?Py::Object(obj->getPyObject(),true):Py::None());
|
||||
ret.setItem(1,parent?Py::Object(parent->getPyObject(),true):Py::None());
|
||||
ret.setItem(2,Py::String(elementName.c_str()));
|
||||
ret.setItem(3,Py::String(subElement?subElement:""));
|
||||
return Py::new_reference_to(ret);
|
||||
} PY_CATCH;
|
||||
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject *DocumentObjectPy::resolveSubElement(PyObject *args)
|
||||
{
|
||||
const char *subname;
|
||||
PyObject *append = Py_False;
|
||||
int type = 0;
|
||||
if (!PyArg_ParseTuple(args, "s|Oi",&subname,&append,&type))
|
||||
return NULL; // NULL triggers exception
|
||||
|
||||
PY_TRY {
|
||||
std::pair<std::string,std::string> elementName;
|
||||
auto obj = GeoFeature::resolveElement(getDocumentObjectPtr(), subname,elementName,
|
||||
PyObject_IsTrue(append),(GeoFeature::ElementNameType)type);
|
||||
Py::Tuple ret(3);
|
||||
ret.setItem(0,obj?Py::Object(obj->getPyObject(),true):Py::None());
|
||||
ret.setItem(1,Py::String(elementName.first));
|
||||
ret.setItem(2,Py::String(elementName.second));
|
||||
return Py::new_reference_to(ret);
|
||||
} PY_CATCH;
|
||||
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
Py::List DocumentObjectPy::getParents() const {
|
||||
Py::List ret;
|
||||
for(auto &v : getDocumentObjectPtr()->getParents())
|
||||
ret.append(Py::TupleN(Py::Object(v.first->getPyObject(),true),Py::String(v.second)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyObject *DocumentObjectPy::adjustRelativeLinks(PyObject *args) {
|
||||
PyObject *pyobj;
|
||||
PyObject *recursive = Py_True;
|
||||
if (!PyArg_ParseTuple(args, "O!|O",&DocumentObjectPy::Type,&pyobj,&recursive))
|
||||
return NULL;
|
||||
PY_TRY {
|
||||
auto obj = static_cast<DocumentObjectPy*>(pyobj)->getDocumentObjectPtr();
|
||||
auto inList = obj->getInListEx(true);
|
||||
inList.insert(obj);
|
||||
std::set<App::DocumentObject *> visited;
|
||||
return Py::new_reference_to(Py::Boolean(
|
||||
getDocumentObjectPtr()->adjustRelativeLinks(inList,
|
||||
PyObject_IsTrue(recursive)?&visited:nullptr)));
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
Py::String DocumentObjectPy::getOldLabel() const {
|
||||
return Py::String(getDocumentObjectPtr()->getOldLabel());
|
||||
}
|
||||
|
||||
Py::Boolean DocumentObjectPy::getNoTouch() const {
|
||||
return Py::Boolean(getDocumentObjectPtr()->testStatus(ObjectStatus::NoTouch));
|
||||
}
|
||||
|
||||
void DocumentObjectPy::setNoTouch(Py::Boolean value) {
|
||||
getDocumentObjectPtr()->setStatus(ObjectStatus::NoTouch,value.isTrue());
|
||||
}
|
||||
|
||||
@@ -103,13 +103,18 @@ DocumentObjectT::DocumentObjectT()
|
||||
{
|
||||
}
|
||||
|
||||
DocumentObjectT::DocumentObjectT(DocumentObject* obj)
|
||||
DocumentObjectT::DocumentObjectT(const DocumentObject* obj)
|
||||
{
|
||||
object = obj->getNameInDocument();
|
||||
label = obj->Label.getValue();
|
||||
document = obj->getDocument()->getName();
|
||||
}
|
||||
|
||||
DocumentObjectT::DocumentObjectT(const Property* prop)
|
||||
{
|
||||
*this = prop;
|
||||
}
|
||||
|
||||
DocumentObjectT::~DocumentObjectT()
|
||||
{
|
||||
}
|
||||
@@ -121,6 +126,7 @@ void DocumentObjectT::operator=(const DocumentObjectT& obj)
|
||||
object = obj.object;
|
||||
label = obj.label;
|
||||
document = obj.document;
|
||||
property = obj.property;
|
||||
}
|
||||
|
||||
void DocumentObjectT::operator=(const DocumentObject* obj)
|
||||
@@ -128,6 +134,16 @@ void DocumentObjectT::operator=(const DocumentObject* obj)
|
||||
object = obj->getNameInDocument();
|
||||
label = obj->Label.getValue();
|
||||
document = obj->getDocument()->getName();
|
||||
property.clear();
|
||||
}
|
||||
|
||||
void DocumentObjectT::operator=(const Property *prop) {
|
||||
auto obj = dynamic_cast<const DocumentObject*>(prop->getContainer());
|
||||
assert(obj);
|
||||
object = obj->getNameInDocument();
|
||||
label = obj->Label.getValue();
|
||||
document = obj->getDocument()->getName();
|
||||
property = prop->getName();
|
||||
}
|
||||
|
||||
Document* DocumentObjectT::getDocument() const
|
||||
@@ -192,6 +208,27 @@ std::string DocumentObjectT::getObjectPython() const
|
||||
return str.str();
|
||||
}
|
||||
|
||||
std::string DocumentObjectT::getPropertyName() const {
|
||||
return property;
|
||||
}
|
||||
|
||||
std::string DocumentObjectT::getPropertyPython() const
|
||||
{
|
||||
std::stringstream str;
|
||||
str << "FreeCAD.getDocument('" << document
|
||||
<< "').getObject('" << object
|
||||
<< "')";
|
||||
if(property.size())
|
||||
str << '.' << property;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
Property *DocumentObjectT::getProperty() const {
|
||||
auto obj = getObject();
|
||||
if(obj)
|
||||
return obj->getPropertyByName(property.c_str());
|
||||
return 0;
|
||||
}
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
DocumentObserver::DocumentObserver() : _document(0)
|
||||
|
||||
@@ -83,13 +83,17 @@ public:
|
||||
/*! Constructor */
|
||||
DocumentObjectT();
|
||||
/*! Constructor */
|
||||
DocumentObjectT(DocumentObject*);
|
||||
DocumentObjectT(const DocumentObject*);
|
||||
/*! Constructor */
|
||||
DocumentObjectT(const Property*);
|
||||
/*! Destructor */
|
||||
~DocumentObjectT();
|
||||
/*! Assignment operator */
|
||||
void operator=(const DocumentObjectT&);
|
||||
/*! Assignment operator */
|
||||
void operator=(const DocumentObject*);
|
||||
/*! Assignment operator */
|
||||
void operator=(const Property*);
|
||||
|
||||
/*! Get a pointer to the document or 0 if it doesn't exist any more. */
|
||||
Document* getDocument() const;
|
||||
@@ -99,23 +103,35 @@ public:
|
||||
std::string getDocumentPython() const;
|
||||
/*! Get a pointer to the document object or 0 if it doesn't exist any more. */
|
||||
DocumentObject* getObject() const;
|
||||
/*! Get a pointer to the property or 0 if it doesn't exist any more. */
|
||||
Property* getProperty() const;
|
||||
/*! Get the name of the document object. */
|
||||
std::string getObjectName() const;
|
||||
/*! Get the label of the document object. */
|
||||
std::string getObjectLabel() const;
|
||||
/*! Get the name of the property. */
|
||||
std::string getPropertyName() const;
|
||||
/*! Get the document object as Python command. */
|
||||
std::string getObjectPython() const;
|
||||
/*! Get the property as Python command. */
|
||||
std::string getPropertyPython() const;
|
||||
/*! Get a pointer to the document or 0 if it doesn't exist any more or the type doesn't match. */
|
||||
template<typename T>
|
||||
inline T* getObjectAs() const
|
||||
{
|
||||
return Base::freecad_dynamic_cast<T>(getObject());
|
||||
}
|
||||
template<typename T>
|
||||
inline T* getPropertyAs() const
|
||||
{
|
||||
return Base::freecad_dynamic_cast<T>(getProperty());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string document;
|
||||
std::string object;
|
||||
std::string label;
|
||||
std::string property;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+192
-245
@@ -59,95 +59,38 @@ void DocumentObserverPython::removeObserver(const Py::Object& obj)
|
||||
|
||||
DocumentObserverPython::DocumentObserverPython(const Py::Object& obj) : inst(obj)
|
||||
{
|
||||
this->connectApplicationCreatedDocument = App::GetApplication().signalNewDocument.connect(boost::bind
|
||||
(&DocumentObserverPython::slotCreatedDocument, this, _1));
|
||||
this->connectApplicationDeletedDocument = App::GetApplication().signalDeleteDocument.connect(boost::bind
|
||||
(&DocumentObserverPython::slotDeletedDocument, this, _1));
|
||||
this->connectApplicationRelabelDocument = App::GetApplication().signalRelabelDocument.connect(boost::bind
|
||||
(&DocumentObserverPython::slotRelabelDocument, this, _1));
|
||||
this->connectApplicationActivateDocument = App::GetApplication().signalActiveDocument.connect(boost::bind
|
||||
(&DocumentObserverPython::slotActivateDocument, this, _1));
|
||||
this->connectApplicationUndoDocument = App::GetApplication().signalUndoDocument.connect(boost::bind
|
||||
(&DocumentObserverPython::slotUndoDocument, this, _1));
|
||||
this->connectApplicationRedoDocument = App::GetApplication().signalRedoDocument.connect(boost::bind
|
||||
(&DocumentObserverPython::slotRedoDocument, this, _1));
|
||||
#define signalCreatedDocument signalNewDocument
|
||||
#define signalCreatedObject signalNewObject
|
||||
#define signalRecomputedObject signalObjectRecomputed
|
||||
#define signalRecomputedDocument signalRecomputed
|
||||
#define signalActivateDocument signalActiveDocument
|
||||
#define signalDeletedDocument signalDeleteDocument
|
||||
|
||||
this->connectDocumentBeforeChange = App::GetApplication().signalBeforeChangeDocument.connect(boost::bind
|
||||
(&DocumentObserverPython::slotBeforeChangeDocument, this, _1, _2));
|
||||
this->connectDocumentChanged = App::GetApplication().signalChangedDocument.connect(boost::bind
|
||||
(&DocumentObserverPython::slotChangedDocument, this, _1, _2));
|
||||
this->connectDocumentCreatedObject = App::GetApplication().signalNewObject.connect(boost::bind
|
||||
(&DocumentObserverPython::slotCreatedObject, this, _1));
|
||||
this->connectDocumentDeletedObject = App::GetApplication().signalDeletedObject.connect(boost::bind
|
||||
(&DocumentObserverPython::slotDeletedObject, this, _1));
|
||||
this->connectDocumentBeforeChangeObject = App::GetApplication().signalBeforeChangeObject.connect(boost::bind
|
||||
(&DocumentObserverPython::slotBeforeChangeObject, this, _1, _2));
|
||||
this->connectDocumentChangedObject = App::GetApplication().signalChangedObject.connect(boost::bind
|
||||
(&DocumentObserverPython::slotChangedObject, this, _1, _2));
|
||||
#undef FC_PY_ELEMENT
|
||||
#define FC_PY_ELEMENT(_name,...) do{\
|
||||
FC_PY_GetCallable(obj.ptr(),"slot" #_name, py##_name);\
|
||||
if(!py##_name.isNone())\
|
||||
connect##_name = App::GetApplication().signal##_name.connect(\
|
||||
boost::bind(&DocumentObserverPython::slot##_name, this, ##__VA_ARGS__));\
|
||||
}while(0);
|
||||
|
||||
this->connectDocumentObjectRecomputed = App::GetApplication().signalObjectRecomputed.connect(boost::bind
|
||||
(&DocumentObserverPython::slotRecomputedObject, this, _1));
|
||||
this->connectDocumentRecomputed = App::GetApplication().signalRecomputed.connect(boost::bind
|
||||
(&DocumentObserverPython::slotRecomputedDocument, this, _1));
|
||||
|
||||
this->connectDocumentOpenTransaction = App::GetApplication().signalOpenTransaction.connect(boost::bind
|
||||
(&DocumentObserverPython::slotOpenTransaction, this, _1, _2));
|
||||
this->connectDocumentCommitTransaction = App::GetApplication().signalCommitTransaction.connect(boost::bind
|
||||
(&DocumentObserverPython::slotCommitTransaction, this, _1));
|
||||
this->connectDocumentAbortTransaction = App::GetApplication().signalAbortTransaction.connect(boost::bind
|
||||
(&DocumentObserverPython::slotAbortTransaction, this, _1));
|
||||
|
||||
this->connectDocumentStartSave = App::GetApplication().signalStartSaveDocument.connect(boost::bind
|
||||
(&DocumentObserverPython::slotStartSaveDocument, this, _1, _2));
|
||||
this->connectDocumentFinishSave = App::GetApplication().signalFinishSaveDocument.connect(boost::bind
|
||||
(&DocumentObserverPython::slotFinishSaveDocument, this, _1, _2));
|
||||
|
||||
this->connectObjectAppendDynamicProperty = App::GetApplication().signalAppendDynamicProperty.connect(boost::bind
|
||||
(&DocumentObserverPython::slotAppendDynamicProperty, this, _1));
|
||||
this->connectObjectRemoveDynamicProperty = App::GetApplication().signalRemoveDynamicProperty.connect(boost::bind
|
||||
(&DocumentObserverPython::slotRemoveDynamicProperty, this, _1));
|
||||
this->connectObjectChangePropertyEditor = App::GetApplication().signalChangePropertyEditor.connect(boost::bind
|
||||
(&DocumentObserverPython::slotChangePropertyEditor, this, _1));
|
||||
FC_PY_DOC_OBSERVER
|
||||
}
|
||||
|
||||
DocumentObserverPython::~DocumentObserverPython()
|
||||
{
|
||||
this->connectApplicationCreatedDocument.disconnect();
|
||||
this->connectApplicationDeletedDocument.disconnect();
|
||||
this->connectApplicationRelabelDocument.disconnect();
|
||||
this->connectApplicationActivateDocument.disconnect();
|
||||
this->connectApplicationUndoDocument.disconnect();
|
||||
this->connectApplicationRedoDocument.disconnect();
|
||||
|
||||
this->connectDocumentBeforeChange.disconnect();
|
||||
this->connectDocumentChanged.disconnect();
|
||||
this->connectDocumentCreatedObject.disconnect();
|
||||
this->connectDocumentDeletedObject.disconnect();
|
||||
this->connectDocumentBeforeChangeObject.disconnect();
|
||||
this->connectDocumentChangedObject.disconnect();
|
||||
this->connectDocumentObjectRecomputed.disconnect();
|
||||
this->connectDocumentRecomputed.disconnect();
|
||||
this->connectDocumentOpenTransaction.disconnect();
|
||||
this->connectDocumentCommitTransaction.disconnect();
|
||||
this->connectDocumentAbortTransaction.disconnect();
|
||||
this->connectDocumentStartSave.disconnect();
|
||||
this->connectDocumentFinishSave.disconnect();
|
||||
|
||||
this->connectObjectAppendDynamicProperty.disconnect();
|
||||
this->connectObjectRemoveDynamicProperty.disconnect();
|
||||
this->connectObjectChangePropertyEditor.disconnect();
|
||||
#undef FC_PY_ELEMENT
|
||||
#define FC_PY_ELEMENT(_name,...) connect##_name.disconnect();
|
||||
FC_PY_DOC_OBSERVER
|
||||
}
|
||||
|
||||
void DocumentObserverPython::slotCreatedDocument(const App::Document& Doc)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotCreatedDocument"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotCreatedDocument")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
Base::pyCall(pyCreatedDocument.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -159,12 +102,9 @@ void DocumentObserverPython::slotDeletedDocument(const App::Document& Doc)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotDeletedDocument"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotDeletedDocument")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
Base::pyCall(pyDeletedDocument.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -176,12 +116,9 @@ void DocumentObserverPython::slotRelabelDocument(const App::Document& Doc)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotRelabelDocument"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotRelabelDocument")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
Base::pyCall(pyRelabelDocument.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -193,12 +130,9 @@ void DocumentObserverPython::slotActivateDocument(const App::Document& Doc)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotActivateDocument"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotActivateDocument")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
Base::pyCall(pyActivateDocument.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -210,12 +144,9 @@ void DocumentObserverPython::slotUndoDocument(const App::Document& Doc)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotUndoDocument"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotUndoDocument")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
Base::pyCall(pyUndoDocument.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -223,16 +154,66 @@ void DocumentObserverPython::slotUndoDocument(const App::Document& Doc)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DocumentObserverPython::slotRedoDocument(const App::Document& Doc)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotRedoDocument"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotRedoDocument")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
Base::pyCall(pyRedoDocument.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
}
|
||||
|
||||
void DocumentObserverPython::slotUndo()
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Base::pyCall(pyUndo.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
}
|
||||
|
||||
void DocumentObserverPython::slotRedo()
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Base::pyCall(pyRedo.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
}
|
||||
|
||||
void DocumentObserverPython::slotBeforeCloseTransaction(bool abort)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Boolean(abort));
|
||||
Base::pyCall(pyBeforeCloseTransaction.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
}
|
||||
|
||||
void DocumentObserverPython::slotCloseTransaction(bool abort)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Boolean(abort));
|
||||
Base::pyCall(pyCloseTransaction.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -244,17 +225,14 @@ void DocumentObserverPython::slotBeforeChangeDocument(const App::Document& Doc,
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotBeforeChangeDocument"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotBeforeChangeDocument")));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
// If a property is touched but not part of a document object then its name is null.
|
||||
// In this case the slot function must not be called.
|
||||
const char* prop_name = Doc.getPropertyName(&Prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
// If a property is touched but not part of a document object then its name is null.
|
||||
// In this case the slot function must not be called.
|
||||
const char* prop_name = Doc.getPropertyName(&Prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
Base::pyCall(pyBeforeChangeDocument.ptr(),args.ptr());
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -267,17 +245,14 @@ void DocumentObserverPython::slotChangedDocument(const App::Document& Doc, const
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotChangedDocument"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotChangedDocument")));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
// If a property is touched but not part of a document object then its name is null.
|
||||
// In this case the slot function must not be called.
|
||||
const char* prop_name = Doc.getPropertyName(&Prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(Doc).getPyObject(), true));
|
||||
// If a property is touched but not part of a document object then its name is null.
|
||||
// In this case the slot function must not be called.
|
||||
const char* prop_name = Doc.getPropertyName(&Prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
Base::pyCall(pyChangedDocument.ptr(),args.ptr());
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -290,12 +265,9 @@ void DocumentObserverPython::slotCreatedObject(const App::DocumentObject& Obj)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotCreatedObject"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotCreatedObject")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::DocumentObject&>(Obj).getPyObject(), true));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::DocumentObject&>(Obj).getPyObject(), true));
|
||||
Base::pyCall(pyCreatedObject.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -307,12 +279,9 @@ void DocumentObserverPython::slotDeletedObject(const App::DocumentObject& Obj)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotDeletedObject"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotDeletedObject")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::DocumentObject&>(Obj).getPyObject(), true));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::DocumentObject&>(Obj).getPyObject(), true));
|
||||
Base::pyCall(pyDeletedObject.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -325,17 +294,14 @@ void DocumentObserverPython::slotBeforeChangeObject(const App::DocumentObject& O
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotBeforeChangeObject"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotBeforeChangeObject")));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(const_cast<App::DocumentObject&>(Obj).getPyObject(), true));
|
||||
// If a property is touched but not part of a document object then its name is null.
|
||||
// In this case the slot function must not be called.
|
||||
const char* prop_name = Obj.getPropertyName(&Prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(const_cast<App::DocumentObject&>(Obj).getPyObject(), true));
|
||||
// If a property is touched but not part of a document object then its name is null.
|
||||
// In this case the slot function must not be called.
|
||||
const char* prop_name = Obj.getPropertyName(&Prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
Base::pyCall(pyBeforeChangeObject.ptr(),args.ptr());
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -349,17 +315,14 @@ void DocumentObserverPython::slotChangedObject(const App::DocumentObject& Obj,
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotChangedObject"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotChangedObject")));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(const_cast<App::DocumentObject&>(Obj).getPyObject(), true));
|
||||
// If a property is touched but not part of a document object then its name is null.
|
||||
// In this case the slot function must not be called.
|
||||
const char* prop_name = Obj.getPropertyName(&Prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(const_cast<App::DocumentObject&>(Obj).getPyObject(), true));
|
||||
// If a property is touched but not part of a document object then its name is null.
|
||||
// In this case the slot function must not be called.
|
||||
const char* prop_name = Obj.getPropertyName(&Prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
Base::pyCall(pyChangedObject.ptr(),args.ptr());
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -372,12 +335,9 @@ void DocumentObserverPython::slotRecomputedObject(const App::DocumentObject& Obj
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotRecomputedObject"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotRecomputedObject")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::DocumentObject&>(Obj).getPyObject(), true));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::DocumentObject&>(Obj).getPyObject(), true));
|
||||
Base::pyCall(pyRecomputedObject.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -389,12 +349,23 @@ void DocumentObserverPython::slotRecomputedDocument(const App::Document& doc)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotRecomputedDocument"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotRecomputedDocument")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(doc).getPyObject(), true));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(doc).getPyObject(), true));
|
||||
Base::pyCall(pyRecomputedDocument.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
}
|
||||
|
||||
void DocumentObserverPython::slotBeforeRecomputeDocument(const App::Document& doc)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(doc).getPyObject(), true));
|
||||
Base::pyCall(pyBeforeRecomputeDocument.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -406,13 +377,10 @@ void DocumentObserverPython::slotOpenTransaction(const App::Document& doc, std::
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotOpenTransaction"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotOpenTransaction")));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(doc).getPyObject(), true));
|
||||
args.setItem(1, Py::String(str));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(doc).getPyObject(), true));
|
||||
args.setItem(1, Py::String(str));
|
||||
Base::pyCall(pyOpenTransaction.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -424,12 +392,9 @@ void DocumentObserverPython::slotCommitTransaction(const App::Document& doc)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotCommitTransaction"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotCommitTransaction")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(doc).getPyObject(), true));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(doc).getPyObject(), true));
|
||||
Base::pyCall(pyCommitTransaction.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -441,12 +406,9 @@ void DocumentObserverPython::slotAbortTransaction(const App::Document& doc)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotAbortTransaction"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotAbortTransaction")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(doc).getPyObject(), true));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(doc).getPyObject(), true));
|
||||
Base::pyCall(pyAbortTransaction.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -458,18 +420,15 @@ void DocumentObserverPython::slotAppendDynamicProperty(const App::Property& Prop
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotAppendDynamicProperty"))) {
|
||||
auto container = Prop.getContainer();
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotAppendDynamicProperty")));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(container->getPyObject(), true));
|
||||
// If a property is touched but not part of a document object then its name is null.
|
||||
// In this case the slot function must not be called.
|
||||
const char* prop_name = container->getPropertyName(&Prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
auto container = Prop.getContainer();
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(container->getPyObject(), true));
|
||||
// If a property is touched but not part of a document object then its name is null.
|
||||
// In this case the slot function must not be called.
|
||||
const char* prop_name = container->getPropertyName(&Prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
Base::pyCall(pyAppendDynamicProperty.ptr(),args.ptr());
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -482,18 +441,15 @@ void DocumentObserverPython::slotRemoveDynamicProperty(const App::Property& Prop
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotRemoveDynamicProperty"))) {
|
||||
auto container = Prop.getContainer();
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotRemoveDynamicProperty")));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(container->getPyObject(), true));
|
||||
// If a property is touched but not part of a document object then its name is null.
|
||||
// In this case the slot function must not be called.
|
||||
const char* prop_name = container->getPropertyName(&Prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
auto container = Prop.getContainer();
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(container->getPyObject(), true));
|
||||
// If a property is touched but not part of a document object then its name is null.
|
||||
// In this case the slot function must not be called.
|
||||
const char* prop_name = container->getPropertyName(&Prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
Base::pyCall(pyRemoveDynamicProperty.ptr(),args.ptr());
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -502,22 +458,19 @@ void DocumentObserverPython::slotRemoveDynamicProperty(const App::Property& Prop
|
||||
}
|
||||
}
|
||||
|
||||
void DocumentObserverPython::slotChangePropertyEditor(const App::Property& Prop)
|
||||
void DocumentObserverPython::slotChangePropertyEditor(const App::Document &, const App::Property& Prop)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotChangePropertyEditor"))) {
|
||||
auto container = Prop.getContainer();
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotChangePropertyEditor")));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(container->getPyObject(), true));
|
||||
// If a property is touched but not part of a document object then its name is null.
|
||||
// In this case the slot function must not be called.
|
||||
const char* prop_name = container->getPropertyName(&Prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
auto container = Prop.getContainer();
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(container->getPyObject(), true));
|
||||
// If a property is touched but not part of a document object then its name is null.
|
||||
// In this case the slot function must not be called.
|
||||
const char* prop_name = container->getPropertyName(&Prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
Base::pyCall(pyChangePropertyEditor.ptr(),args.ptr());
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -530,13 +483,10 @@ void DocumentObserverPython::slotStartSaveDocument(const App::Document& doc, con
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotStartSaveDocument"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotStartSaveDocument")));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(doc).getPyObject(), true));
|
||||
args.setItem(1, Py::String(file));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(doc).getPyObject(), true));
|
||||
args.setItem(1, Py::String(file));
|
||||
Base::pyCall(pyStartSaveDocument.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -548,13 +498,10 @@ void DocumentObserverPython::slotFinishSaveDocument(const App::Document& doc, co
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (this->inst.hasAttr(std::string("slotFinishSaveDocument"))) {
|
||||
Py::Callable method(this->inst.getAttr(std::string("slotFinishSaveDocument")));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(doc).getPyObject(), true));
|
||||
args.setItem(1, Py::String(file));
|
||||
method.apply(args);
|
||||
}
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(const_cast<App::Document&>(doc).getPyObject(), true));
|
||||
args.setItem(1, Py::String(file));
|
||||
Base::pyCall(pyFinishSaveDocument.ptr(),args.ptr());
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
|
||||
@@ -77,6 +77,8 @@ private:
|
||||
void slotRedoDocument(const App::Document& Doc);
|
||||
/** Called when a given object is recomputed */
|
||||
void slotRecomputedObject(const App::DocumentObject& Obj);
|
||||
/** Called before an observed document is recomputed */
|
||||
void slotBeforeRecomputeDocument(const App::Document& Doc);
|
||||
/** Called when an observed document is recomputed */
|
||||
void slotRecomputedDocument(const App::Document& Doc);
|
||||
/** Called when an observed document opens a transaction */
|
||||
@@ -85,12 +87,20 @@ private:
|
||||
void slotCommitTransaction(const App::Document& Doc);
|
||||
/** Called when an observed document aborts a transaction */
|
||||
void slotAbortTransaction(const App::Document& Doc);
|
||||
/** Called after application wide undo */
|
||||
void slotUndo();
|
||||
/** Called after application wide redo */
|
||||
void slotRedo();
|
||||
/** Called before closing/aborting application active transaction */
|
||||
void slotBeforeCloseTransaction(bool abort);
|
||||
/** Called after closing/aborting application active transaction */
|
||||
void slotCloseTransaction(bool abort);
|
||||
/** Called when an object gets a new dynamic property added*/
|
||||
void slotAppendDynamicProperty(const App::Property& Prop);
|
||||
/** Called when an object gets a dynamic property removed*/
|
||||
void slotRemoveDynamicProperty(const App::Property& Prop);
|
||||
/** Called when an object property gets a new editor relevant status like hidden or read only*/
|
||||
void slotChangePropertyEditor(const App::Property& Prop);
|
||||
void slotChangePropertyEditor(const App::Document &Doc, const App::Property& Prop);
|
||||
/** Called when a document is about to be saved*/
|
||||
void slotStartSaveDocument(const App::Document&, const std::string&);
|
||||
/** Called when an document has been saved*/
|
||||
@@ -101,28 +111,42 @@ private:
|
||||
static std::vector<DocumentObserverPython*> _instances;
|
||||
|
||||
typedef boost::signals2::connection Connection;
|
||||
Connection connectApplicationCreatedDocument;
|
||||
Connection connectApplicationDeletedDocument;
|
||||
Connection connectApplicationRelabelDocument;
|
||||
Connection connectApplicationActivateDocument;
|
||||
Connection connectApplicationUndoDocument;
|
||||
Connection connectApplicationRedoDocument;
|
||||
Connection connectDocumentBeforeChange;
|
||||
Connection connectDocumentChanged;
|
||||
Connection connectDocumentCreatedObject;
|
||||
Connection connectDocumentDeletedObject;
|
||||
Connection connectDocumentBeforeChangeObject;
|
||||
Connection connectDocumentChangedObject;
|
||||
Connection connectDocumentObjectRecomputed;
|
||||
Connection connectDocumentRecomputed;
|
||||
Connection connectDocumentOpenTransaction;
|
||||
Connection connectDocumentCommitTransaction;
|
||||
Connection connectDocumentAbortTransaction;
|
||||
Connection connectDocumentStartSave;
|
||||
Connection connectDocumentFinishSave;
|
||||
Connection connectObjectAppendDynamicProperty;
|
||||
Connection connectObjectRemoveDynamicProperty;
|
||||
Connection connectObjectChangePropertyEditor;
|
||||
|
||||
#define FC_PY_DOC_OBSERVER \
|
||||
FC_PY_ELEMENT(CreatedDocument,_1) \
|
||||
FC_PY_ELEMENT(DeletedDocument,_1) \
|
||||
FC_PY_ELEMENT(RelabelDocument,_1) \
|
||||
FC_PY_ELEMENT(ActivateDocument,_1) \
|
||||
FC_PY_ELEMENT(UndoDocument,_1) \
|
||||
FC_PY_ELEMENT(RedoDocument,_1) \
|
||||
FC_PY_ELEMENT(BeforeChangeDocument,_1,_2) \
|
||||
FC_PY_ELEMENT(ChangedDocument,_1,_2) \
|
||||
FC_PY_ELEMENT(CreatedObject,_1) \
|
||||
FC_PY_ELEMENT(DeletedObject,_1) \
|
||||
FC_PY_ELEMENT(BeforeChangeObject,_1,_2) \
|
||||
FC_PY_ELEMENT(ChangedObject,_1,_2) \
|
||||
FC_PY_ELEMENT(RecomputedObject,_1) \
|
||||
FC_PY_ELEMENT(BeforeRecomputeDocument,_1) \
|
||||
FC_PY_ELEMENT(RecomputedDocument,_1) \
|
||||
FC_PY_ELEMENT(OpenTransaction,_1,_2) \
|
||||
FC_PY_ELEMENT(CommitTransaction,_1) \
|
||||
FC_PY_ELEMENT(AbortTransaction,_1) \
|
||||
FC_PY_ELEMENT(Undo) \
|
||||
FC_PY_ELEMENT(Redo) \
|
||||
FC_PY_ELEMENT(BeforeCloseTransaction,_1) \
|
||||
FC_PY_ELEMENT(CloseTransaction,_1) \
|
||||
FC_PY_ELEMENT(StartSaveDocument,_1,_2) \
|
||||
FC_PY_ELEMENT(FinishSaveDocument,_1,_2) \
|
||||
FC_PY_ELEMENT(AppendDynamicProperty,_1) \
|
||||
FC_PY_ELEMENT(RemoveDynamicProperty,_1) \
|
||||
FC_PY_ELEMENT(ChangePropertyEditor,_1,_2)
|
||||
|
||||
#undef FC_PY_ELEMENT
|
||||
#define FC_PY_ELEMENT(_name,...) \
|
||||
Connection connect##_name;\
|
||||
Py::Object py##_name;
|
||||
|
||||
FC_PY_DOC_OBSERVER
|
||||
};
|
||||
|
||||
} //namespace App
|
||||
|
||||
+125
-8
@@ -50,7 +50,14 @@
|
||||
</Methode>
|
||||
<Methode Name="openTransaction">
|
||||
<Documentation>
|
||||
<UserDocu>Open a new Undo/Redo transaction.</UserDocu>
|
||||
<UserDocu>openTransaction(name) - Open a new Undo/Redo transaction.
|
||||
|
||||
This function no long creates a new transaction, but calls
|
||||
FreeCAD.setActiveTransaction(name) instead, which will auto creates a
|
||||
transaction with the given name when any change happed in any opened document.
|
||||
If more than one document is changed, all newly created transactions will have
|
||||
the same internal ID and will be undo/redo together.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="abortTransaction">
|
||||
@@ -63,9 +70,20 @@
|
||||
<UserDocu>Commit an Undo/Redo transaction</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="addObject">
|
||||
<Methode Name="addObject" Keyword="true">
|
||||
<Documentation>
|
||||
<UserDocu>Add an object with given type and name to the document</UserDocu>
|
||||
<UserDocu>addObject(type, name=None, objProxy=None, viewProxy=None, attach=False, viewType=None)
|
||||
|
||||
Add an object to document
|
||||
|
||||
type (String): the type of the document object to create.
|
||||
name (String): the optional name of the new object.
|
||||
objProxy (Object): the Python binding object to attach to the new document object.
|
||||
viewProxy (Object): the Python binding object to attach the view provider of this object.
|
||||
attach (Boolean): if True, then bind the document object first before adding to the document
|
||||
to allow Python code to override view provider type. Once bound, and before adding to
|
||||
the document, it will try to call Python binding object's attach(obj) method.
|
||||
viewType (String): override the view provider type directly, only effective when attach is False.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="removeObject">
|
||||
@@ -75,14 +93,38 @@
|
||||
</Methode>
|
||||
<Methode Name="copyObject">
|
||||
<Documentation>
|
||||
<UserDocu>copyObject(object, bool with_dependencies = False, bool ignored_argument = False)
|
||||
Copy an object from another document to this document. If with_dependencies is True, all objects this object depends on are copied too.</UserDocu>
|
||||
<UserDocu>
|
||||
copyObject(object, with_dependencies=False)
|
||||
Copy an object or objects from another document to this document.
|
||||
|
||||
object: can either a single object or sequence of objects
|
||||
with_dependencies: if True, all internal dependent objects are copied too.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="moveObject">
|
||||
<Documentation>
|
||||
<UserDocu>moveObject(object, bool with_dependencies = False)
|
||||
Transfers an object from another document to this document. If with_dependencies is True, all objects this object depends on are transferred too.</UserDocu>
|
||||
<UserDocu>
|
||||
moveObject(object, bool with_dependencies = False)
|
||||
Transfers an object from another document to this document.
|
||||
|
||||
object: can either a single object or sequence of objects
|
||||
with_dependencies: if True, all internal dependent objects are copied too.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="importLinks">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
importLinks(object|[object...])
|
||||
|
||||
Import any externally linked object given a list of objects in
|
||||
this document. Any link type properties of the input objects
|
||||
will be automatically reassigned to the imported object
|
||||
|
||||
If no object is given as input, it import all externally linked
|
||||
object of this document.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="undo">
|
||||
@@ -102,7 +144,7 @@
|
||||
</Methode>
|
||||
<Methode Name="recompute">
|
||||
<Documentation>
|
||||
<UserDocu>Recompute the document and returns the amount of recomputed features</UserDocu>
|
||||
<UserDocu>recompute(objs=None): Recompute the document and returns the amount of recomputed features</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getObject">
|
||||
@@ -123,6 +165,16 @@ Return a list of objects that match the specified type and name.
|
||||
Both parameters are optional.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getLinksTo">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
getLinksTo(obj, options=0, maxCount=0): return objects linked to 'obj'
|
||||
|
||||
options: 1: recursive, 2: check link array. Options can combine.
|
||||
maxCount: to limit the number of links returned
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="supportedTypes">
|
||||
<Documentation>
|
||||
<UserDocu>A list of supported types of objects</UserDocu>
|
||||
@@ -133,6 +185,17 @@ Both parameters are optional.</UserDocu>
|
||||
<UserDocu>Returns a file name with path in the temp directory of the document.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getDependentDocuments">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
getDependentDocuments(sort=True)
|
||||
|
||||
Returns a list of documents that this document directly or indirectly links to including itself.
|
||||
|
||||
sort: whether to topologically sort the return list
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Attribute Name="DependencyGraph" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>The dependency graph as GraphViz text</UserDocu>
|
||||
@@ -211,6 +274,60 @@ Both parameters are optional.</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="RecomputesFrozen" Type="Boolean"/>
|
||||
</Attribute>
|
||||
<Attribute Name="HasPendingTransaction" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Check if there is a pending transaction</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="HasPendingTransaction" Type="Boolean"/>
|
||||
</Attribute>
|
||||
<Attribute Name="InList" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>A list of all documents that link to this document.</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="InList" Type="List" />
|
||||
</Attribute>
|
||||
<Attribute Name="OutList" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>A list of all documents that this document links to.</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="OutList" Type="List" />
|
||||
</Attribute>
|
||||
<Attribute Name="Restoring" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Indicate if the document is restoring</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Restoring" Type="Boolean" />
|
||||
</Attribute>
|
||||
<Attribute Name="Partial" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Indicate if the document is partially loaded</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Partial" Type="Boolean" />
|
||||
</Attribute>
|
||||
<Attribute Name="Importing" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Indicate if the document is importing. Note the document will also report Restoring while importing</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Importing" Type="Boolean" />
|
||||
</Attribute>
|
||||
<Attribute Name="Recomputing" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Indicate if the document is recomputing</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Recomputing" Type="Boolean" />
|
||||
</Attribute>
|
||||
<Attribute Name="Transacting" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Indicate whether the document is undoing/redoing</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Transacting" Type="Boolean" />
|
||||
</Attribute>
|
||||
<Attribute Name="OldLabel" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Contains the old label before change</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="OldLabel" Type="String"/>
|
||||
</Attribute>
|
||||
<CustomAttributes />
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
|
||||
+244
-73
@@ -32,6 +32,7 @@
|
||||
#include "DocumentObject.h"
|
||||
#include "DocumentObjectPy.h"
|
||||
#include "MergeDocuments.h"
|
||||
#include "PropertyLinks.h"
|
||||
|
||||
// inclusion of the generated files (generated By DocumentPy.xml)
|
||||
#include "DocumentPy.h"
|
||||
@@ -55,20 +56,12 @@ PyObject* DocumentPy::save(PyObject * args)
|
||||
if (!PyArg_ParseTuple(args, "")) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
|
||||
try {
|
||||
PY_TRY {
|
||||
if (!getDocumentPtr()->save()) {
|
||||
PyErr_SetString(PyExc_ValueError, "Object attribute 'FileName' is not set");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
catch (const Base::FileException& e) {
|
||||
PyErr_SetString(PyExc_IOError, e.what());
|
||||
return 0;
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
PyErr_SetString(PyExc_RuntimeError, e.what());
|
||||
return 0;
|
||||
}
|
||||
} PY_CATCH;
|
||||
|
||||
const char* filename = getDocumentPtr()->FileName.getValue();
|
||||
Base::FileInfo fi(filename);
|
||||
@@ -89,28 +82,10 @@ PyObject* DocumentPy::saveAs(PyObject * args)
|
||||
std::string utf8Name = fn;
|
||||
PyMem_Free(fn);
|
||||
|
||||
try {
|
||||
if (!getDocumentPtr()->saveAs(utf8Name.c_str())) {
|
||||
PyErr_SetString(PyExc_ValueError, "Object attribute 'FileName' is not set");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
catch (const Base::FileException& e) {
|
||||
PyErr_SetString(PyExc_IOError, e.what());
|
||||
return 0;
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
PyErr_SetString(PyExc_RuntimeError, e.what());
|
||||
return 0;
|
||||
}
|
||||
|
||||
Base::FileInfo fi(utf8Name);
|
||||
if (!fi.isReadable()) {
|
||||
PyErr_Format(PyExc_IOError, "No such file or directory: '%s'", utf8Name.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_Return;
|
||||
PY_TRY {
|
||||
getDocumentPtr()->saveAs(utf8Name.c_str());
|
||||
Py_Return;
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::saveCopy(PyObject * args)
|
||||
@@ -118,18 +93,11 @@ PyObject* DocumentPy::saveCopy(PyObject * args)
|
||||
char* fn;
|
||||
if (!PyArg_ParseTuple(args, "s", &fn)) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
if (!getDocumentPtr()->saveCopy(fn)) {
|
||||
PyErr_Format(PyExc_ValueError, "Object attribute 'FileName' is not set");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Base::FileInfo fi(fn);
|
||||
if (!fi.isReadable()) {
|
||||
PyErr_Format(PyExc_IOError, "No such file or directory: '%s'", fn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_Return;
|
||||
PY_TRY {
|
||||
getDocumentPtr()->saveCopy(fn);
|
||||
Py_Return;
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::load(PyObject * args)
|
||||
@@ -219,17 +187,33 @@ PyObject* DocumentPy::exportGraphviz(PyObject * args)
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::addObject(PyObject *args)
|
||||
PyObject* DocumentPy::addObject(PyObject *args, PyObject *kwd)
|
||||
{
|
||||
char *sType,*sName=0;
|
||||
char *sType,*sName=0,*sViewType=0;
|
||||
PyObject* obj=0;
|
||||
PyObject* view=0;
|
||||
if (!PyArg_ParseTuple(args, "s|sOO", &sType,&sName,&obj,&view)) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
PyObject *attach=Py_False;
|
||||
static char *kwlist[] = {"type","name","objProxy","viewProxy","attach","viewType",NULL};
|
||||
if (!PyArg_ParseTupleAndKeywords(args,kwd,"s|sOOOs",
|
||||
kwlist, &sType,&sName,&obj,&view,&attach,&sViewType))
|
||||
return NULL;
|
||||
|
||||
DocumentObject *pcFtr;
|
||||
DocumentObject *pcFtr = 0;
|
||||
|
||||
pcFtr = getDocumentPtr()->addObject(sType,sName);
|
||||
if(!obj || !PyObject_IsTrue(attach)) {
|
||||
pcFtr = getDocumentPtr()->addObject(sType,sName,true,sViewType);
|
||||
}else{
|
||||
Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(sType,true));
|
||||
if(base) {
|
||||
if (!base->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId())) {
|
||||
delete base;
|
||||
std::stringstream str;
|
||||
str << "'" << sType << "' is not a document object type";
|
||||
throw Base::TypeError(str.str());
|
||||
}
|
||||
pcFtr = static_cast<DocumentObject*>(base);
|
||||
}
|
||||
}
|
||||
if (pcFtr) {
|
||||
// Allows to hide the handling with Proxy in client python code
|
||||
if (obj) {
|
||||
@@ -243,6 +227,21 @@ PyObject* DocumentPy::addObject(PyObject *args)
|
||||
}
|
||||
pyftr.setAttr("Proxy", pyobj);
|
||||
|
||||
if(PyObject_IsTrue(attach)) {
|
||||
getDocumentPtr()->addObject(pcFtr,sName);
|
||||
|
||||
try {
|
||||
Py::Callable method(pyobj.getAttr("attach"));
|
||||
if(!method.isNone()) {
|
||||
Py::TupleN arg(pyftr);
|
||||
method.apply(arg);
|
||||
}
|
||||
}catch (Py::Exception&) {
|
||||
Base::PyException e;
|
||||
e.ReportException();
|
||||
}
|
||||
}
|
||||
|
||||
// if a document class is set we also need a view provider defined which must be
|
||||
// something different to None
|
||||
Py::Object pyvp;
|
||||
@@ -290,21 +289,77 @@ PyObject* DocumentPy::removeObject(PyObject *args)
|
||||
|
||||
PyObject* DocumentPy::copyObject(PyObject *args)
|
||||
{
|
||||
// 'keep' is not needed any more but leave it there for backward compatibility
|
||||
PyObject *obj, *rec=Py_False, *keep=Py_False;
|
||||
if (!PyArg_ParseTuple(args, "O!|O!O!",&(DocumentObjectPy::Type),&obj,&PyBool_Type,&rec,&PyBool_Type,&keep))
|
||||
PyObject *obj, *rec=Py_False;
|
||||
if (!PyArg_ParseTuple(args, "O|O",&obj,&rec))
|
||||
return NULL; // NULL triggers exception
|
||||
|
||||
DocumentObjectPy* docObj = static_cast<DocumentObjectPy*>(obj);
|
||||
DocumentObject* copy = getDocumentPtr()->copyObject(docObj->getDocumentObjectPtr(),
|
||||
PyObject_IsTrue(rec) ? true : false);
|
||||
if (copy) {
|
||||
return copy->getPyObject();
|
||||
}
|
||||
else {
|
||||
std::string str("Failed to copy the object");
|
||||
throw Py::Exception(Base::BaseExceptionFreeCADError,str);
|
||||
std::vector<App::DocumentObject*> objs;
|
||||
bool single = false;
|
||||
if(PySequence_Check(obj)) {
|
||||
Py::Sequence seq(obj);
|
||||
for(size_t i=0;i<seq.size();++i) {
|
||||
if(!PyObject_TypeCheck(seq[i].ptr(),&DocumentObjectPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expect element in sequence to be of type document object");
|
||||
return 0;
|
||||
}
|
||||
objs.push_back(static_cast<DocumentObjectPy*>(seq[i].ptr())->getDocumentObjectPtr());
|
||||
}
|
||||
}else if(!PyObject_TypeCheck(obj,&DocumentObjectPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Expect first argument to be either a document object or sequence of document objects");
|
||||
return 0;
|
||||
}else {
|
||||
objs.push_back(static_cast<DocumentObjectPy*>(obj)->getDocumentObjectPtr());
|
||||
single = true;
|
||||
}
|
||||
|
||||
PY_TRY {
|
||||
auto ret = getDocumentPtr()->copyObject(objs,PyObject_IsTrue(rec));
|
||||
if(ret.size()==1 && single)
|
||||
return ret[0]->getPyObject();
|
||||
|
||||
Py::Tuple tuple(ret.size());
|
||||
for(size_t i=0;i<ret.size();++i)
|
||||
tuple.setItem(i,Py::Object(ret[i]->getPyObject(),true));
|
||||
return Py::new_reference_to(tuple);
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::importLinks(PyObject *args)
|
||||
{
|
||||
PyObject *obj = Py_None;
|
||||
if (!PyArg_ParseTuple(args, "|O",&obj))
|
||||
return NULL; // NULL triggers exception
|
||||
|
||||
std::vector<App::DocumentObject*> objs;
|
||||
if(PySequence_Check(obj)) {
|
||||
Py::Sequence seq(obj);
|
||||
for(size_t i=0;i<seq.size();++i) {
|
||||
if(!PyObject_TypeCheck(seq[i].ptr(),&DocumentObjectPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expect element in sequence to be of type document object");
|
||||
return 0;
|
||||
}
|
||||
objs.push_back(static_cast<DocumentObjectPy*>(seq[i].ptr())->getDocumentObjectPtr());
|
||||
}
|
||||
}else if(obj == Py_None) {
|
||||
}else if(!PyObject_TypeCheck(obj,&DocumentObjectPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Expect first argument to be either a document object or sequence of document objects");
|
||||
return 0;
|
||||
}else
|
||||
objs.push_back(static_cast<DocumentObjectPy*>(obj)->getDocumentObjectPtr());
|
||||
|
||||
if(objs.empty())
|
||||
objs = getDocumentPtr()->getObjects();
|
||||
|
||||
PY_TRY {
|
||||
auto ret = getDocumentPtr()->importLinks(objs);
|
||||
|
||||
Py::Tuple tuple(ret.size());
|
||||
for(size_t i=0;i<ret.size();++i)
|
||||
tuple.setItem(i,Py::Object(ret[i]->getPyObject(),true));
|
||||
return Py::new_reference_to(tuple);
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::moveObject(PyObject *args)
|
||||
@@ -374,6 +429,10 @@ PyObject* DocumentPy::commitTransaction(PyObject * args)
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
Py::Boolean DocumentPy::getHasPendingTransaction() const {
|
||||
return Py::Boolean(getDocumentPtr()->hasPendingTransaction());
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::undo(PyObject * args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) // convert args: Python->C
|
||||
@@ -402,25 +461,46 @@ PyObject* DocumentPy::clearUndos(PyObject * args)
|
||||
|
||||
PyObject* DocumentPy::recompute(PyObject * args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) // convert args: Python->C
|
||||
PyObject *pyobjs = Py_None;
|
||||
PyObject *force = Py_False;
|
||||
PyObject *checkCycle = Py_False;
|
||||
if (!PyArg_ParseTuple(args, "|OO!O!",&pyobjs,
|
||||
&PyBool_Type,&force,&PyBool_Type,&checkCycle)) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
try {
|
||||
int objectCount = getDocumentPtr()->recompute();
|
||||
PY_TRY {
|
||||
std::vector<App::DocumentObject *> objs;
|
||||
if(pyobjs!=Py_None) {
|
||||
if(!PySequence_Check(pyobjs)) {
|
||||
PyErr_SetString(PyExc_TypeError, "expect input of sequence of document objects");
|
||||
return 0;
|
||||
}
|
||||
Py::Sequence seq(pyobjs);
|
||||
for(size_t i=0;i<seq.size();++i) {
|
||||
if(!PyObject_TypeCheck(seq[i].ptr(),&DocumentObjectPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expect element in sequence to be of type document object");
|
||||
return 0;
|
||||
}
|
||||
objs.push_back(static_cast<DocumentObjectPy*>(seq[i].ptr())->getDocumentObjectPtr());
|
||||
}
|
||||
}
|
||||
int options = 0;
|
||||
if(PyObject_IsTrue(checkCycle))
|
||||
options = Document::DepNoCycle;
|
||||
int objectCount = getDocumentPtr()->recompute(objs,PyObject_IsTrue(force),0,options);
|
||||
return Py::new_reference_to(Py::Int(objectCount));
|
||||
}
|
||||
catch (const Base::RuntimeError& e) {
|
||||
PyErr_SetString(PyExc_RuntimeError, e.what());
|
||||
return 0;
|
||||
}
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::getObject(PyObject *args)
|
||||
{
|
||||
char *sName;
|
||||
if (!PyArg_ParseTuple(args, "s",&sName)) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
long id = -1;
|
||||
char *sName = 0;
|
||||
if (!PyArg_ParseTuple(args, "s",&sName)) { // convert args: Python->C
|
||||
if(!PyArg_ParseTuple(args, "l", &id))
|
||||
return NULL; // NULL triggers exception
|
||||
}
|
||||
|
||||
DocumentObject *pcFtr = getDocumentPtr()->getObject(sName);
|
||||
DocumentObject *pcFtr = sName?getDocumentPtr()->getObject(sName):getDocumentPtr()->getObjectByID(id);
|
||||
if (pcFtr)
|
||||
return pcFtr->getPyObject();
|
||||
else
|
||||
@@ -695,3 +775,94 @@ int DocumentPy::setCustomAttributes(const char* attr, PyObject *)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::getLinksTo(PyObject *args)
|
||||
{
|
||||
PyObject *pyobj = Py_None;
|
||||
int options = 0;
|
||||
short count = 0;
|
||||
if (!PyArg_ParseTuple(args, "|Oih", &pyobj,&options, &count))
|
||||
return NULL;
|
||||
|
||||
PY_TRY {
|
||||
DocumentObject *obj = 0;
|
||||
if(pyobj!=Py_None) {
|
||||
if(!PyObject_TypeCheck(pyobj,&DocumentObjectPy::Type)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expect the first argument of type document object");
|
||||
return 0;
|
||||
}
|
||||
obj = static_cast<DocumentObjectPy*>(pyobj)->getDocumentObjectPtr();
|
||||
}
|
||||
std::set<DocumentObject *> links;
|
||||
getDocumentPtr()->getLinksTo(links,obj,options,count);
|
||||
Py::Tuple ret(links.size());
|
||||
int i=0;
|
||||
for(auto o : links)
|
||||
ret.setItem(i++,Py::Object(o->getPyObject(),true));
|
||||
return Py::new_reference_to(ret);
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
Py::List DocumentPy::getInList(void) const
|
||||
{
|
||||
Py::List ret;
|
||||
auto lists = PropertyXLink::getDocumentInList(getDocumentPtr());
|
||||
if(lists.size()==1) {
|
||||
for(auto doc : lists.begin()->second)
|
||||
ret.append(Py::Object(doc->getPyObject(), true));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Py::List DocumentPy::getOutList(void) const
|
||||
{
|
||||
Py::List ret;
|
||||
auto lists = PropertyXLink::getDocumentOutList(getDocumentPtr());
|
||||
if(lists.size()==1) {
|
||||
for(auto doc : lists.begin()->second)
|
||||
ret.append(Py::Object(doc->getPyObject(), true));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyObject *DocumentPy::getDependentDocuments(PyObject *args) {
|
||||
PyObject *sort = Py_True;
|
||||
if (!PyArg_ParseTuple(args, "|O", &sort))
|
||||
return 0;
|
||||
PY_TRY {
|
||||
auto docs = getDocumentPtr()->getDependentDocuments(PyObject_IsTrue(sort));
|
||||
Py::List ret;
|
||||
for(auto doc : docs)
|
||||
ret.append(Py::Object(doc->getPyObject(), true));
|
||||
return Py::new_reference_to(ret);
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
Py::Boolean DocumentPy::getRestoring(void) const
|
||||
{
|
||||
return Py::Boolean(getDocumentPtr()->testStatus(Document::Status::Restoring));
|
||||
}
|
||||
|
||||
Py::Boolean DocumentPy::getPartial(void) const
|
||||
{
|
||||
return Py::Boolean(getDocumentPtr()->testStatus(Document::Status::PartialDoc));
|
||||
}
|
||||
|
||||
Py::Boolean DocumentPy::getImporting(void) const
|
||||
{
|
||||
return Py::Boolean(getDocumentPtr()->testStatus(Document::Status::Importing));
|
||||
}
|
||||
|
||||
Py::Boolean DocumentPy::getRecomputing(void) const
|
||||
{
|
||||
return Py::Boolean(getDocumentPtr()->testStatus(Document::Status::Recomputing));
|
||||
}
|
||||
|
||||
Py::Boolean DocumentPy::getTransacting() const {
|
||||
return Py::Boolean(getDocumentPtr()->isPerformingTransaction());
|
||||
}
|
||||
|
||||
Py::String DocumentPy::getOldLabel() const {
|
||||
return Py::String(getDocumentPtr()->getOldLabel());
|
||||
}
|
||||
|
||||
|
||||
+151
-300
@@ -37,202 +37,117 @@
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Tools.h>
|
||||
|
||||
FC_LOG_LEVEL_INIT("DynamicProperty",true,true)
|
||||
|
||||
|
||||
using namespace App;
|
||||
|
||||
|
||||
DynamicProperty::DynamicProperty(PropertyContainer* p) : pc(p)
|
||||
DynamicProperty::DynamicProperty()
|
||||
{
|
||||
}
|
||||
|
||||
DynamicProperty::~DynamicProperty()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void DynamicProperty::clear() {
|
||||
auto &index = props.get<0>();
|
||||
for(auto &v : index)
|
||||
delete v.property;
|
||||
index.clear();
|
||||
}
|
||||
|
||||
void DynamicProperty::getPropertyList(std::vector<Property*> &List) const
|
||||
{
|
||||
// get the properties of the base class first and insert the dynamic properties afterwards
|
||||
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
|
||||
static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyList(List);
|
||||
else
|
||||
this->pc->PropertyContainer::getPropertyList(List);
|
||||
|
||||
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it)
|
||||
List.push_back(it->second.property);
|
||||
for (auto &v : props.get<0>())
|
||||
List.push_back(v.property);
|
||||
}
|
||||
|
||||
void DynamicProperty::getPropertyMap(std::map<std::string,Property*> &Map) const
|
||||
{
|
||||
// get the properties of the base class first and insert the dynamic properties afterwards
|
||||
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
|
||||
static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyMap(Map);
|
||||
else
|
||||
this->pc->PropertyContainer::getPropertyMap(Map);
|
||||
|
||||
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it)
|
||||
Map[it->first] = it->second.property;
|
||||
}
|
||||
|
||||
Property *DynamicProperty::getPropertyByName(const char* name) const
|
||||
{
|
||||
std::map<std::string,PropData>::const_iterator it = props.find(name);
|
||||
if (it != props.end())
|
||||
return it->second.property;
|
||||
|
||||
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
|
||||
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyByName(name);
|
||||
|
||||
return this->pc->PropertyContainer::getPropertyByName(name);
|
||||
for (auto &v : props.get<0>())
|
||||
Map[v.name] = v.property;
|
||||
}
|
||||
|
||||
Property *DynamicProperty::getDynamicPropertyByName(const char* name) const
|
||||
{
|
||||
std::map<std::string,PropData>::const_iterator it = props.find(name);
|
||||
if (it != props.end())
|
||||
return it->second.property;
|
||||
auto &index = props.get<0>();
|
||||
auto it = index.find(name);
|
||||
if (it != index.end())
|
||||
return it->property;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> DynamicProperty::getDynamicPropertyNames() const
|
||||
{
|
||||
std::vector<std::string> names;
|
||||
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
|
||||
names.push_back(it->first);
|
||||
}
|
||||
auto &index = props.get<0>();
|
||||
names.reserve(index.size());
|
||||
for(auto &v : index)
|
||||
names.push_back(v.name);
|
||||
return names;
|
||||
}
|
||||
|
||||
void DynamicProperty::addDynamicProperties(const PropertyContainer* cont)
|
||||
{
|
||||
std::vector<std::string> names = cont->getDynamicPropertyNames();
|
||||
for (std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
|
||||
App::Property* prop = cont->getDynamicPropertyByName(it->c_str());
|
||||
if (prop) {
|
||||
addDynamicProperty(
|
||||
prop->getTypeId().getName(),
|
||||
prop->getName(),
|
||||
prop->getGroup(),
|
||||
prop->getDocumentation(),
|
||||
prop->getType(),
|
||||
cont->isReadOnly(prop),
|
||||
cont->isHidden(prop));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* DynamicProperty::getPropertyName(const Property* prop) const
|
||||
{
|
||||
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
|
||||
if (it->second.property == prop)
|
||||
return it->first.c_str();
|
||||
}
|
||||
|
||||
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
|
||||
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyName(prop);
|
||||
|
||||
return this->pc->PropertyContainer::getPropertyName(prop);
|
||||
}
|
||||
|
||||
unsigned int DynamicProperty::getMemSize (void) const
|
||||
{
|
||||
std::map<std::string,Property*> Map;
|
||||
getPropertyMap(Map);
|
||||
std::map<std::string,Property*>::const_iterator It;
|
||||
unsigned int size = 0;
|
||||
for (It = Map.begin(); It != Map.end();++It)
|
||||
size += It->second->getMemSize();
|
||||
return size;
|
||||
}
|
||||
|
||||
short DynamicProperty::getPropertyType(const Property* prop) const
|
||||
{
|
||||
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
|
||||
if (it->second.property == prop) {
|
||||
short attr = it->second.attr;
|
||||
if (it->second.hidden)
|
||||
attr |= Prop_Hidden;
|
||||
if (it->second.readonly)
|
||||
attr |= Prop_ReadOnly;
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
|
||||
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyType(prop);
|
||||
|
||||
return this->pc->PropertyContainer::getPropertyType(prop);
|
||||
return prop?prop->getType():0;
|
||||
}
|
||||
|
||||
short DynamicProperty::getPropertyType(const char *name) const
|
||||
{
|
||||
std::map<std::string,PropData>::const_iterator it = props.find(name);
|
||||
if (it != props.end()) {
|
||||
short attr = it->second.attr;
|
||||
if (it->second.hidden)
|
||||
auto &index = props.get<0>();
|
||||
auto it = index.find(name);
|
||||
if (it != index.end()) {
|
||||
short attr = it->attr;
|
||||
if (it->hidden)
|
||||
attr |= Prop_Hidden;
|
||||
if (it->second.readonly)
|
||||
if (it->readonly)
|
||||
attr |= Prop_ReadOnly;
|
||||
return attr;
|
||||
}
|
||||
|
||||
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
|
||||
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyType(name);
|
||||
|
||||
return this->pc->PropertyContainer::getPropertyType(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* DynamicProperty::getPropertyGroup(const Property* prop) const
|
||||
{
|
||||
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
|
||||
if (it->second.property == prop)
|
||||
return it->second.group.c_str();
|
||||
}
|
||||
|
||||
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
|
||||
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyGroup(prop);
|
||||
|
||||
return this->pc->PropertyContainer::getPropertyGroup(prop);
|
||||
auto &index = props.get<1>();
|
||||
auto it = index.find(const_cast<Property*>(prop));
|
||||
if(it!=index.end())
|
||||
return it->group.c_str();
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* DynamicProperty::getPropertyGroup(const char *name) const
|
||||
{
|
||||
std::map<std::string,PropData>::const_iterator it = props.find(name);
|
||||
if (it != props.end())
|
||||
return it->second.group.c_str();
|
||||
|
||||
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
|
||||
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyGroup(name);
|
||||
|
||||
return this->pc->PropertyContainer::getPropertyGroup(name);
|
||||
auto &index = props.get<0>();
|
||||
auto it = index.find(name);
|
||||
if (it != index.end())
|
||||
return it->group.c_str();
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* DynamicProperty::getPropertyDocumentation(const Property* prop) const
|
||||
{
|
||||
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
|
||||
if (it->second.property == prop)
|
||||
return it->second.doc.c_str();
|
||||
}
|
||||
|
||||
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
|
||||
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyDocumentation(prop);
|
||||
|
||||
return this->pc->PropertyContainer::getPropertyDocumentation(prop);
|
||||
auto &index = props.get<1>();
|
||||
auto it = index.find(const_cast<Property*>(prop));
|
||||
if(it!=index.end())
|
||||
return it->doc.c_str();
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* DynamicProperty::getPropertyDocumentation(const char *name) const
|
||||
{
|
||||
std::map<std::string,PropData>::const_iterator it = props.find(name);
|
||||
if (it != props.end())
|
||||
return it->second.doc.c_str();
|
||||
|
||||
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
|
||||
return static_cast<App::ExtensionContainer*>(this->pc)->ExtensionContainer::getPropertyDocumentation(name);
|
||||
|
||||
return this->pc->PropertyContainer::getPropertyDocumentation(name);
|
||||
auto &index = props.get<0>();
|
||||
auto it = index.find(name);
|
||||
if (it != index.end())
|
||||
return it->doc.c_str();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Property* DynamicProperty::addDynamicProperty(const char* type, const char* name, const char* group,
|
||||
const char* doc, short attr, bool ro, bool hidden)
|
||||
Property* DynamicProperty::addDynamicProperty(PropertyContainer &pc, const char* type,
|
||||
const char* name, const char* group, const char* doc, short attr, bool ro, bool hidden)
|
||||
{
|
||||
Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(type,true));
|
||||
if (!base)
|
||||
@@ -246,49 +161,76 @@ Property* DynamicProperty::addDynamicProperty(const char* type, const char* name
|
||||
|
||||
// get unique name
|
||||
Property* pcProperty = static_cast<Property*>(base);
|
||||
std::string ObjectName;
|
||||
if (name && *name != '\0')
|
||||
ObjectName = getUniquePropertyName(name);
|
||||
else
|
||||
ObjectName = getUniquePropertyName(type);
|
||||
if (!name || !name[0])
|
||||
name = type;
|
||||
|
||||
pcProperty->setContainer(this->pc);
|
||||
PropData data;
|
||||
data.property = pcProperty;
|
||||
data.group = (group ? group : "");
|
||||
data.doc = (doc ? doc : "");
|
||||
data.attr = attr;
|
||||
data.readonly = ro;
|
||||
data.hidden = hidden;
|
||||
props[ObjectName] = data;
|
||||
auto res = props.get<0>().emplace(pcProperty,
|
||||
getUniquePropertyName(pc,name), nullptr, group, doc, attr, ro, hidden);
|
||||
|
||||
pcProperty->setContainer(&pc);
|
||||
pcProperty->myName = res.first->name.c_str();
|
||||
|
||||
if(ro) attr |= Prop_ReadOnly;
|
||||
if(hidden) attr |= Prop_Hidden;
|
||||
|
||||
pcProperty->syncType(attr);
|
||||
pcProperty->StatusBits.set((size_t)Property::PropDynamic);
|
||||
|
||||
GetApplication().signalAppendDynamicProperty(*pcProperty);
|
||||
|
||||
return pcProperty;
|
||||
}
|
||||
|
||||
bool DynamicProperty::addProperty(Property *prop)
|
||||
{
|
||||
if(!prop || !prop->getName())
|
||||
return false;
|
||||
auto &index = props.get<0>();
|
||||
if(index.count(prop->getName()))
|
||||
return false;
|
||||
index.emplace(prop,std::string(),prop->getName(),
|
||||
prop->getGroup(),prop->getDocumentation(),prop->getType(),false,false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DynamicProperty::removeProperty(const Property *prop)
|
||||
{
|
||||
auto &index = props.get<1>();
|
||||
auto it = index.find(const_cast<Property*>(prop));
|
||||
if (it != index.end()) {
|
||||
index.erase(it);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DynamicProperty::removeDynamicProperty(const char* name)
|
||||
{
|
||||
std::map<std::string,PropData>::iterator it = props.find(name);
|
||||
if (it != props.end()) {
|
||||
GetApplication().signalRemoveDynamicProperty(*it->second.property);
|
||||
delete it->second.property;
|
||||
props.erase(it);
|
||||
auto &index = props.get<0>();
|
||||
auto it = index.find(name);
|
||||
if (it != index.end()) {
|
||||
if(it->property->testStatus(Property::LockDynamic))
|
||||
throw Base::RuntimeError("property is locked");
|
||||
else if(!it->property->testStatus(Property::PropDynamic))
|
||||
throw Base::RuntimeError("property is not dynamic");
|
||||
Property *prop = it->property;
|
||||
GetApplication().signalRemoveDynamicProperty(*prop);
|
||||
delete prop;
|
||||
index.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string DynamicProperty::getUniquePropertyName(const char *Name) const
|
||||
std::string DynamicProperty::getUniquePropertyName(PropertyContainer &pc, const char *Name) const
|
||||
{
|
||||
std::string CleanName = Base::Tools::getIdentifier(Name);
|
||||
|
||||
// name in use?
|
||||
std::map<std::string,Property*> objectProps;
|
||||
getPropertyMap(objectProps);
|
||||
std::map<std::string,Property*>::const_iterator pos;
|
||||
pos = objectProps.find(CleanName);
|
||||
pc.getPropertyMap(objectProps);
|
||||
auto pos = objectProps.find(CleanName);
|
||||
|
||||
if (pos == objectProps.end()) {
|
||||
// if not, name is OK
|
||||
@@ -304,151 +246,60 @@ std::string DynamicProperty::getUniquePropertyName(const char *Name) const
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicProperty::Save (Base::Writer &writer) const
|
||||
void DynamicProperty::save(const Property *prop, Base::Writer &writer) const
|
||||
{
|
||||
//extensions must be saved first, as they need to be read and initialised before properties (as
|
||||
//they have their own properties which they need to handle on restore)
|
||||
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
|
||||
static_cast<App::ExtensionContainer*>(this->pc)->saveExtensions(writer);
|
||||
|
||||
std::map<std::string,Property*> Map;
|
||||
getPropertyMap(Map);
|
||||
|
||||
writer.incInd(); // indentation for 'Properties Count'
|
||||
writer.Stream() << writer.ind() << "<Properties Count=\"" << Map.size() << "\">" << std::endl;
|
||||
std::map<std::string,Property*>::iterator it;
|
||||
for (it = Map.begin(); it != Map.end(); ++it)
|
||||
{
|
||||
writer.incInd(); // indentation for 'Property name'
|
||||
// check whether a static or dynamic property
|
||||
std::map<std::string,PropData>::const_iterator pt = props.find(it->first);
|
||||
if (pt == props.end()) {
|
||||
writer.Stream() << writer.ind() << "<Property name=\"" << it->first << "\" type=\""
|
||||
<< it->second->getTypeId().getName() << "\">" << std::endl;
|
||||
}
|
||||
else {
|
||||
writer.Stream() << writer.ind() << "<Property name=\"" << it->first
|
||||
<< "\" type=\"" << it->second->getTypeId().getName()
|
||||
<< "\" group=\"" << encodeAttribute(pt->second.group)
|
||||
<< "\" doc=\"" << encodeAttribute(pt->second.doc)
|
||||
<< "\" attr=\"" << pt->second.attr << "\" ro=\"" << pt->second.readonly
|
||||
<< "\" hide=\"" << pt->second.hidden << "\">" << std::endl;
|
||||
}
|
||||
|
||||
writer.incInd(); // indentation for the actual property
|
||||
try {
|
||||
// We must make sure to handle all exceptions accordingly so that
|
||||
// the project file doesn't get invalidated. In the error case this
|
||||
// means to proceed instead of aborting the write operation.
|
||||
|
||||
// Don't write transient properties
|
||||
if (!(getPropertyType(it->second) & Prop_Transient))
|
||||
it->second->Save(writer);
|
||||
}
|
||||
catch (const Base::Exception &e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
}
|
||||
catch (const char* e) {
|
||||
Base::Console().Error("%s\n", e);
|
||||
}
|
||||
#ifndef FC_DEBUG
|
||||
catch (...) {
|
||||
Base::Console().Error("DynamicProperty::Save: Unknown C++ exception thrown. Try to continue...\n");
|
||||
}
|
||||
#endif
|
||||
writer.decInd(); // indentation for the actual property
|
||||
writer.Stream() << writer.ind() << "</Property>" << std::endl;
|
||||
writer.decInd(); // indentation for 'Property name'
|
||||
auto &index = props.get<1>();
|
||||
auto it = index.find(const_cast<Property*>(prop));
|
||||
if(it != index.end()) {
|
||||
auto &data = *it;
|
||||
writer.Stream() << "\" group=\"" << Base::Persistence::encodeAttribute(data.group)
|
||||
<< "\" doc=\"" << Base::Persistence::encodeAttribute(data.doc)
|
||||
<< "\" attr=\"" << data.attr << "\" ro=\"" << data.readonly
|
||||
<< "\" hide=\"" << data.hidden;
|
||||
}
|
||||
writer.Stream() << writer.ind() << "</Properties>" << std::endl;
|
||||
writer.decInd(); // indentation for 'Properties Count'
|
||||
}
|
||||
|
||||
void DynamicProperty::Restore(Base::XMLReader &reader)
|
||||
Property *DynamicProperty::restore(PropertyContainer &pc,
|
||||
const char *PropName, const char *TypeName, Base::XMLReader &reader)
|
||||
{
|
||||
//first all extensions must be initialised so that they can handle their properties
|
||||
if (this->pc->isDerivedFrom(App::ExtensionContainer::getClassTypeId()))
|
||||
static_cast<App::ExtensionContainer*>(this->pc)->restoreExtensions(reader);
|
||||
if (!reader.hasAttribute("group"))
|
||||
return 0;
|
||||
|
||||
reader.readElement("Properties");
|
||||
int Cnt = reader.getAttributeAsInteger("Count");
|
||||
|
||||
for (int i=0 ;i<Cnt ;i++) {
|
||||
reader.readElement("Property");
|
||||
const char* PropName = reader.getAttribute("name");
|
||||
const char* TypeName = reader.getAttribute("type");
|
||||
Property* prop = getPropertyByName(PropName);
|
||||
try {
|
||||
if (!prop) {
|
||||
short attribute = 0;
|
||||
bool readonly = false, hidden = false;
|
||||
const char *group=0, *doc=0, *attr=0, *ro=0, *hide=0;
|
||||
if (reader.hasAttribute("group"))
|
||||
group = reader.getAttribute("group");
|
||||
if (reader.hasAttribute("doc"))
|
||||
doc = reader.getAttribute("doc");
|
||||
if (reader.hasAttribute("attr")) {
|
||||
attr = reader.getAttribute("attr");
|
||||
if (attr) attribute = attr[0]-48;
|
||||
}
|
||||
if (reader.hasAttribute("ro")) {
|
||||
ro = reader.getAttribute("ro");
|
||||
if (ro) readonly = (ro[0]-48) != 0;
|
||||
}
|
||||
if (reader.hasAttribute("hide")) {
|
||||
hide = reader.getAttribute("hide");
|
||||
if (hide) hidden = (hide[0]-48) != 0;
|
||||
}
|
||||
prop = addDynamicProperty(TypeName, PropName, group, doc, attribute, readonly, hidden);
|
||||
}
|
||||
}
|
||||
catch(const Base::Exception& e) {
|
||||
// only handle this exception type
|
||||
Base::Console().Warning(e.what());
|
||||
}
|
||||
|
||||
//NOTE: We must also check the type of the current property because a subclass of
|
||||
//PropertyContainer might change the type of a property but not its name. In this
|
||||
//case we would force to read-in a wrong property type and the behaviour would be
|
||||
//undefined.
|
||||
|
||||
// Don't read transient properties
|
||||
if (!(getPropertyType(prop) & Prop_Transient)) {
|
||||
if (prop && strcmp(prop->getTypeId().getName(), TypeName) == 0) {
|
||||
try {
|
||||
prop->Restore(reader);
|
||||
}
|
||||
catch (const Base::XMLParseException&) {
|
||||
throw; // re-throw
|
||||
}
|
||||
catch (const Base::Exception &e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
}
|
||||
#ifndef FC_DEBUG
|
||||
catch (...) {
|
||||
Base::Console().Error("DynamicProperty::Restore: Unknown C++ exception thrown\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (prop) {
|
||||
//Base::Console().Warning("%s: Overread data for property %s of type %s, expected type is %s\n",
|
||||
// pc->getTypeId().getName(), prop->getName(), prop->getTypeId().getName(), TypeName);
|
||||
pc->handleChangedPropertyType(reader, TypeName, prop);
|
||||
}
|
||||
else {
|
||||
//Base::Console().Warning("%s: No property found with name %s and type %s\n",
|
||||
// pc->getTypeId().getName(), PropName, TypeName);
|
||||
pc->handleChangedPropertyName(reader, TypeName, PropName);
|
||||
}
|
||||
}
|
||||
reader.readEndElement("Property");
|
||||
short attribute = 0;
|
||||
bool readonly = false, hidden = false;
|
||||
const char *group=0, *doc=0, *attr=0, *ro=0, *hide=0;
|
||||
group = reader.getAttribute("group");
|
||||
if (reader.hasAttribute("doc"))
|
||||
doc = reader.getAttribute("doc");
|
||||
if (reader.hasAttribute("attr")) {
|
||||
attr = reader.getAttribute("attr");
|
||||
if (attr) attribute = attr[0]-48;
|
||||
}
|
||||
if (reader.hasAttribute("ro")) {
|
||||
ro = reader.getAttribute("ro");
|
||||
if (ro) readonly = (ro[0]-48) != 0;
|
||||
}
|
||||
if (reader.hasAttribute("hide")) {
|
||||
hide = reader.getAttribute("hide");
|
||||
if (hide) hidden = (hide[0]-48) != 0;
|
||||
}
|
||||
return addDynamicProperty(pc,TypeName, PropName, group, doc, attribute, readonly, hidden);
|
||||
}
|
||||
|
||||
reader.readEndElement("Properties");
|
||||
DynamicProperty::PropData DynamicProperty::getDynamicPropertyData(const Property *prop) const
|
||||
{
|
||||
auto &index = props.get<1>();
|
||||
auto it = index.find(const_cast<Property*>(prop));
|
||||
if(it != index.end())
|
||||
return *it;
|
||||
return PropData();
|
||||
}
|
||||
|
||||
const char *DynamicProperty::getPropertyName(const Property *prop) const
|
||||
{
|
||||
auto &index = props.get<1>();
|
||||
auto it = index.find(const_cast<Property*>(prop));
|
||||
if(it != index.end())
|
||||
return it->getName();
|
||||
return 0;
|
||||
}
|
||||
|
||||
+74
-21
@@ -25,10 +25,18 @@
|
||||
#define APP_DYNAMICPROPERTY_H
|
||||
|
||||
#include <Base/Persistence.h>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/sequenced_index.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/multi_index/mem_fun.hpp>
|
||||
|
||||
namespace Base {
|
||||
class Writer;
|
||||
class XMLWriter;
|
||||
@@ -39,14 +47,28 @@ namespace App
|
||||
class Property;
|
||||
class PropertyContainer;
|
||||
|
||||
namespace bmi = boost::multi_index;
|
||||
|
||||
struct CStringHasher {
|
||||
inline std::size_t operator()(const char *s) const {
|
||||
if(!s) return 0;
|
||||
return boost::hash_range(s,s+std::strlen(s));
|
||||
}
|
||||
inline bool operator()(const char *a, const char *b) const {
|
||||
if(!a) return !b;
|
||||
if(!b) return false;
|
||||
return std::strcmp(a,b)==0;
|
||||
}
|
||||
};
|
||||
|
||||
/** This class implements an interface to add properties at run-time to an object
|
||||
* derived from PropertyContainer. The additional properties are made persistent.
|
||||
* @author Werner Mayer
|
||||
*/
|
||||
class AppExport DynamicProperty : public Base::Persistence
|
||||
class AppExport DynamicProperty
|
||||
{
|
||||
public:
|
||||
DynamicProperty(PropertyContainer* pc);
|
||||
DynamicProperty();
|
||||
virtual ~DynamicProperty();
|
||||
|
||||
/** @name Access properties */
|
||||
@@ -55,8 +77,6 @@ public:
|
||||
void getPropertyList(std::vector<Property*> &List) const;
|
||||
/// Get all properties of the class (including parent)
|
||||
void getPropertyMap(std::map<std::string,Property*> &Map) const;
|
||||
/// Find a property by its name
|
||||
Property *getPropertyByName(const char* name) const;
|
||||
/// Find a dynamic property by its name
|
||||
Property *getDynamicPropertyByName(const char* name) const;
|
||||
/*!
|
||||
@@ -76,20 +96,24 @@ public:
|
||||
addDynamicProperty(..., ..., "Base","blah", Prop_None, true, true);
|
||||
@endcode
|
||||
*/
|
||||
Property* addDynamicProperty(const char* type, const char* name=0, const char* group=0,
|
||||
Property* addDynamicProperty(PropertyContainer &pc, const char* type, const char* name=0, const char* group=0,
|
||||
const char* doc=0, short attr=0, bool ro=false, bool hidden=false);
|
||||
/** Add a pre-existing property
|
||||
*
|
||||
* The property is not treated as dynamic, and will not trigger signal.
|
||||
*
|
||||
* @return Return false if there is a property exist with the same name.
|
||||
*/
|
||||
bool addProperty(Property *prop);
|
||||
/*!
|
||||
Removes a dynamic property by name. Returns true if the property is part of the container, otherwise
|
||||
false is returned.
|
||||
*/
|
||||
bool removeDynamicProperty(const char* name);
|
||||
/// Remove pre-existing property, which will not be deleted.
|
||||
bool removeProperty(const Property *prop);
|
||||
/// Get a list of all dynamic properties.
|
||||
std::vector<std::string> getDynamicPropertyNames() const;
|
||||
/*!
|
||||
Get all dynamic properties of the given container and add these property types to this
|
||||
instance of DynamicProperty.
|
||||
*/
|
||||
void addDynamicProperties(const PropertyContainer*);
|
||||
/// Get the name of a property
|
||||
const char* getPropertyName(const Property* prop) const;
|
||||
//@}
|
||||
@@ -110,28 +134,57 @@ public:
|
||||
const char* getPropertyDocumentation(const char *name) const;
|
||||
//@}
|
||||
|
||||
/** @name Property serialization */
|
||||
//@{
|
||||
void Save (Base::Writer &writer) const;
|
||||
void Restore(Base::XMLReader &reader);
|
||||
unsigned int getMemSize (void) const;
|
||||
//@}
|
||||
/// Remove all properties
|
||||
void clear();
|
||||
|
||||
private:
|
||||
std::string getUniquePropertyName(const char *Name) const;
|
||||
/// Get property count
|
||||
size_t size() const { return props.size(); }
|
||||
|
||||
void save(const Property *prop, Base::Writer &writer) const;
|
||||
|
||||
Property *restore(PropertyContainer &pc,
|
||||
const char *PropName, const char *TypeName, Base::XMLReader &reader);
|
||||
|
||||
private:
|
||||
struct PropData {
|
||||
Property* property;
|
||||
std::string name;
|
||||
const char *pName;
|
||||
std::string group;
|
||||
std::string doc;
|
||||
short attr;
|
||||
bool readonly;
|
||||
bool hidden;
|
||||
|
||||
PropData(Property *prop=0, std::string &&n=std::string(), const char *pn=0,
|
||||
const char *g=0, const char *d=0, short a=0, bool ro=false, bool h=false)
|
||||
:property(prop),name(std::move(n)),pName(pn)
|
||||
,group(g?g:""),doc(d?d:""),attr(a),readonly(ro),hidden(h)
|
||||
{}
|
||||
|
||||
const char *getName() const {
|
||||
return pName?pName:name.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
PropertyContainer* pc;
|
||||
std::map<std::string,PropData> props;
|
||||
PropData getDynamicPropertyData(const Property* prop) const;
|
||||
|
||||
private:
|
||||
std::string getUniquePropertyName(PropertyContainer &pc, const char *Name) const;
|
||||
|
||||
private:
|
||||
bmi::multi_index_container<
|
||||
PropData,
|
||||
bmi::indexed_by<
|
||||
bmi::hashed_unique<
|
||||
bmi::const_mem_fun<PropData, const char*, &PropData::getName>,
|
||||
CStringHasher,
|
||||
CStringHasher
|
||||
>,
|
||||
bmi::hashed_unique<
|
||||
bmi::member<PropData, Property*, &PropData::property>
|
||||
>
|
||||
>
|
||||
> props;
|
||||
};
|
||||
|
||||
} // namespace App
|
||||
|
||||
+1148
-150
File diff suppressed because it is too large
Load Diff
+202
-58
@@ -27,7 +27,7 @@
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Unit.h>
|
||||
#include <App/Property.h>
|
||||
#include <App/PropertyLinks.h>
|
||||
#include <App/ObjectIdentifier.h>
|
||||
#include <Base/BaseClass.h>
|
||||
#include <Base/Quantity.h>
|
||||
@@ -35,35 +35,78 @@
|
||||
#include <deque>
|
||||
#include <App/Range.h>
|
||||
|
||||
//FIXME: PyObjectExpression hides overloaded virtual functions
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Woverloaded-virtual"
|
||||
#endif
|
||||
|
||||
namespace App {
|
||||
|
||||
class DocumentObject;
|
||||
class Expression;
|
||||
class Document;
|
||||
|
||||
typedef std::unique_ptr<Expression> ExpressionPtr;
|
||||
|
||||
AppExport bool isAnyEqual(const App::any &v1, const App::any &v2);
|
||||
AppExport Base::Quantity anyToQuantity(const App::any &value, const char *errmsg = 0);
|
||||
|
||||
typedef std::map<App::DocumentObject*, std::map<std::string, std::vector<ObjectIdentifier> > > ExpressionDeps;
|
||||
class AppExport ExpressionVisitor {
|
||||
public:
|
||||
virtual ~ExpressionVisitor() {}
|
||||
virtual void visit(Expression * e) = 0;
|
||||
virtual void visit(Expression &e) = 0;
|
||||
virtual void aboutToChange() {}
|
||||
virtual int changed() const { return 0;}
|
||||
virtual void reset() {}
|
||||
virtual App::PropertyLinkBase* getPropertyLink() {return 0;}
|
||||
|
||||
protected:
|
||||
void getIdentifiers(Expression &e, std::set<App::ObjectIdentifier> &);
|
||||
void getDeps(Expression &e, ExpressionDeps &);
|
||||
void getDepObjects(Expression &e, std::set<App::DocumentObject*> &, std::vector<std::string> *);
|
||||
bool adjustLinks(Expression &e, const std::set<App::DocumentObject*> &inList);
|
||||
bool relabeledDocument(Expression &e, const std::string &oldName, const std::string &newName);
|
||||
bool renameObjectIdentifier(Expression &e,
|
||||
const std::map<ObjectIdentifier,ObjectIdentifier> &, const ObjectIdentifier &);
|
||||
void collectReplacement(Expression &e, std::map<ObjectIdentifier,ObjectIdentifier> &,
|
||||
const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj) const;
|
||||
bool updateElementReference(Expression &e, App::DocumentObject *feature,bool reverse);
|
||||
void importSubNames(Expression &e, const ObjectIdentifier::SubNameMap &subNameMap);
|
||||
void updateLabelReference(Expression &e, App::DocumentObject *obj,
|
||||
const std::string &ref, const char *newLabel);
|
||||
void moveCells(Expression &e, const CellAddress &address, int rowCount, int colCount);
|
||||
void offsetCells(Expression &e, int rowOffset, int colOffset);
|
||||
};
|
||||
|
||||
template<class P> class ExpressionModifier : public ExpressionVisitor {
|
||||
public:
|
||||
ExpressionModifier(P & _prop)
|
||||
: prop(_prop) { }
|
||||
: prop(_prop)
|
||||
, propLink(Base::freecad_dynamic_cast<App::PropertyLinkBase>(&prop))
|
||||
, signaller(_prop,false)
|
||||
, _changed(0)
|
||||
{}
|
||||
|
||||
virtual ~ExpressionModifier() { }
|
||||
|
||||
void setExpressionChanged() {
|
||||
if (!signaller)
|
||||
signaller = boost::shared_ptr<typename AtomicPropertyChangeInterface<P>::AtomicPropertyChange>(AtomicPropertyChangeInterface<P>::getAtomicPropertyChange(prop));
|
||||
virtual void aboutToChange() override{
|
||||
++_changed;
|
||||
signaller.aboutToChange();
|
||||
}
|
||||
|
||||
bool getChanged() const { return signaller != 0; }
|
||||
virtual int changed() const override { return _changed; }
|
||||
|
||||
virtual void reset() override {_changed = 0;}
|
||||
|
||||
virtual App::PropertyLinkBase* getPropertyLink() override {return propLink;}
|
||||
|
||||
protected:
|
||||
P & prop;
|
||||
boost::shared_ptr<typename AtomicPropertyChangeInterface<P>::AtomicPropertyChange> signaller;
|
||||
App::PropertyLinkBase *propLink;
|
||||
typename AtomicPropertyChangeInterface<P>::AtomicPropertyChange signaller;
|
||||
int _changed;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -84,31 +127,78 @@ public:
|
||||
|
||||
virtual Expression * eval() const = 0;
|
||||
|
||||
virtual std::string toString() const = 0;
|
||||
virtual std::string toString(bool persistent=false) const = 0;
|
||||
|
||||
static Expression * parse(const App::DocumentObject * owner, const std::string& buffer);
|
||||
|
||||
virtual Expression * copy() const = 0;
|
||||
Expression * copy() const;
|
||||
|
||||
virtual int priority() const { return 0; }
|
||||
|
||||
virtual void getDeps(std::set<ObjectIdentifier> &/*props*/) const { }
|
||||
void getIdentifiers(std::set<App::ObjectIdentifier> &) const;
|
||||
std::set<App::ObjectIdentifier> getIdentifiers() const;
|
||||
|
||||
void getDeps(ExpressionDeps &deps) const;
|
||||
ExpressionDeps getDeps() const;
|
||||
|
||||
std::set<App::DocumentObject*> getDepObjects(std::vector<std::string> *labels=0) const;
|
||||
void getDepObjects(std::set<App::DocumentObject*> &, std::vector<std::string> *labels=0) const;
|
||||
|
||||
ExpressionPtr importSubNames(const std::map<std::string,std::string> &nameMap) const;
|
||||
|
||||
ExpressionPtr updateLabelReference(App::DocumentObject *obj,
|
||||
const std::string &ref, const char *newLabel) const;
|
||||
|
||||
ExpressionPtr replaceObject(const App::DocumentObject *parent,
|
||||
App::DocumentObject *oldObj, App::DocumentObject *newObj) const;
|
||||
|
||||
bool adjustLinks(const std::set<App::DocumentObject*> &inList);
|
||||
|
||||
virtual Expression * simplify() const = 0;
|
||||
|
||||
virtual void visit(ExpressionVisitor & v) { v.visit(this); }
|
||||
void visit(ExpressionVisitor & v);
|
||||
|
||||
class Exception : public Base::Exception {
|
||||
public:
|
||||
Exception(const char *sMessage) : Base::Exception(sMessage) { }
|
||||
};
|
||||
|
||||
const App::DocumentObject * getOwner() const { return owner; }
|
||||
App::DocumentObject * getOwner() const { return owner; }
|
||||
|
||||
virtual boost::any getValueAsAny() const { static boost::any empty; return empty; }
|
||||
|
||||
bool isSame(const Expression &other) const;
|
||||
|
||||
friend ExpressionVisitor;
|
||||
|
||||
protected:
|
||||
const App::DocumentObject * owner; /**< The document object used to access unqualified variables (i.e local scope) */
|
||||
virtual Expression *_copy() const = 0;
|
||||
virtual void _getDeps(ExpressionDeps &) const {}
|
||||
virtual void _getDepObjects(std::set<App::DocumentObject*> &, std::vector<std::string> *) const {}
|
||||
virtual void _getIdentifiers(std::set<App::ObjectIdentifier> &) const {}
|
||||
virtual bool _adjustLinks(const std::set<App::DocumentObject*> &, ExpressionVisitor &) {return false;}
|
||||
virtual bool _updateElementReference(App::DocumentObject *,bool,ExpressionVisitor &) {return false;}
|
||||
virtual bool _relabeledDocument(const std::string &, const std::string &, ExpressionVisitor &) {return false;}
|
||||
virtual void _importSubNames(const ObjectIdentifier::SubNameMap &) {}
|
||||
virtual void _updateLabelReference(App::DocumentObject *, const std::string &, const char *) {}
|
||||
virtual bool _renameObjectIdentifier(const std::map<ObjectIdentifier,ObjectIdentifier> &,
|
||||
const ObjectIdentifier &, ExpressionVisitor &) {return false;}
|
||||
virtual void _collectReplacement(std::map<ObjectIdentifier,ObjectIdentifier> &,
|
||||
const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj) const
|
||||
{
|
||||
(void)parent;
|
||||
(void)oldObj;
|
||||
(void)newObj;
|
||||
}
|
||||
virtual void _moveCells(const CellAddress &, int, int, ExpressionVisitor &) {}
|
||||
virtual void _offsetCells(int, int, ExpressionVisitor &) {}
|
||||
virtual void _visit(ExpressionVisitor &) {}
|
||||
|
||||
protected:
|
||||
App::DocumentObject * owner; /**< The document object used to access unqualified variables (i.e local scope) */
|
||||
|
||||
public:
|
||||
std::string comment;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -125,9 +215,9 @@ public:
|
||||
|
||||
virtual Expression * simplify() const;
|
||||
|
||||
virtual std::string toString() const;
|
||||
virtual std::string toString(bool persistent=false) const;
|
||||
|
||||
virtual Expression * copy() const;
|
||||
virtual Expression * _copy() const;
|
||||
|
||||
virtual int priority() const;
|
||||
|
||||
@@ -163,13 +253,15 @@ public:
|
||||
|
||||
virtual Expression * simplify() const;
|
||||
|
||||
virtual Expression * copy() const;
|
||||
virtual Expression * _copy() const;
|
||||
|
||||
virtual int priority() const;
|
||||
|
||||
void negate();
|
||||
|
||||
virtual std::string toString() const;
|
||||
virtual std::string toString(bool persistent=false) const;
|
||||
|
||||
bool isInteger(long *v=0) const;
|
||||
|
||||
protected:
|
||||
};
|
||||
@@ -179,9 +271,9 @@ class AppExport ConstantExpression : public NumberExpression {
|
||||
public:
|
||||
ConstantExpression(const App::DocumentObject *_owner = 0, std::string _name = "", const Base::Quantity &_quantity = Base::Quantity());
|
||||
|
||||
virtual std::string toString() const;
|
||||
virtual std::string toString(bool persistent=false) const;
|
||||
|
||||
virtual Expression * copy() const;
|
||||
virtual Expression * _copy() const;
|
||||
|
||||
virtual int priority() const;
|
||||
|
||||
@@ -196,7 +288,7 @@ class AppExport BooleanExpression : public NumberExpression {
|
||||
public:
|
||||
BooleanExpression(const App::DocumentObject *_owner = 0, bool _value = false);
|
||||
|
||||
virtual Expression * copy() const;
|
||||
virtual Expression * _copy() const;
|
||||
|
||||
};
|
||||
|
||||
@@ -236,15 +328,13 @@ public:
|
||||
|
||||
virtual Expression * simplify() const;
|
||||
|
||||
virtual std::string toString() const;
|
||||
virtual std::string toString(bool persistent=false) const;
|
||||
|
||||
virtual Expression * copy() const;
|
||||
virtual Expression * _copy() const;
|
||||
|
||||
virtual int priority() const;
|
||||
|
||||
virtual void getDeps(std::set<ObjectIdentifier> &props) const;
|
||||
|
||||
virtual void visit(ExpressionVisitor & v);
|
||||
virtual void _visit(ExpressionVisitor & v);
|
||||
|
||||
Operator getOperator() const { return op; }
|
||||
|
||||
@@ -278,15 +368,13 @@ public:
|
||||
|
||||
virtual Expression * simplify() const;
|
||||
|
||||
virtual std::string toString() const;
|
||||
virtual std::string toString(bool persistent=false) const;
|
||||
|
||||
virtual Expression * copy() const;
|
||||
virtual Expression * _copy() const;
|
||||
|
||||
virtual int priority() const;
|
||||
|
||||
virtual void getDeps(std::set<ObjectIdentifier> &props) const;
|
||||
|
||||
virtual void visit(ExpressionVisitor & v);
|
||||
virtual void _visit(ExpressionVisitor & v);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -355,15 +443,13 @@ public:
|
||||
|
||||
virtual Expression * simplify() const;
|
||||
|
||||
virtual std::string toString() const;
|
||||
virtual std::string toString(bool persistent=false) const;
|
||||
|
||||
virtual Expression * copy() const;
|
||||
virtual Expression * _copy() const;
|
||||
|
||||
virtual int priority() const;
|
||||
|
||||
virtual void getDeps(std::set<ObjectIdentifier> &props) const;
|
||||
|
||||
virtual void visit(ExpressionVisitor & v);
|
||||
virtual void _visit(ExpressionVisitor & v);
|
||||
|
||||
protected:
|
||||
Expression *evalAggregate() const;
|
||||
@@ -393,35 +479,74 @@ public:
|
||||
|
||||
virtual Expression * simplify() const;
|
||||
|
||||
virtual std::string toString() const { return var.toString(); }
|
||||
virtual std::string toString(bool persistent=false) const;
|
||||
|
||||
virtual Expression * copy() const;
|
||||
virtual Expression * _copy() const;
|
||||
|
||||
virtual int priority() const;
|
||||
|
||||
virtual void getDeps(std::set<ObjectIdentifier> &props) const;
|
||||
|
||||
std::string name() const { return var.getPropertyName(); }
|
||||
|
||||
ObjectIdentifier getPath() const { return var; }
|
||||
|
||||
void setPath(const ObjectIdentifier & path);
|
||||
|
||||
bool validDocumentObjectRename(const std::string & oldName, const std::string & newName);
|
||||
|
||||
bool renameDocumentObject(const std::string & oldName, const std::string & newName);
|
||||
|
||||
bool validDocumentRename(const std::string &oldName, const std::string &newName);
|
||||
|
||||
bool renameDocument(const std::string &oldName, const std::string &newName);
|
||||
|
||||
const App::Property *getProperty() const;
|
||||
|
||||
protected:
|
||||
virtual void _getDeps(ExpressionDeps &) const;
|
||||
virtual void _getDepObjects(std::set<App::DocumentObject*> &, std::vector<std::string> *) const;
|
||||
virtual void _getIdentifiers(std::set<App::ObjectIdentifier> &) const;
|
||||
virtual bool _adjustLinks(const std::set<App::DocumentObject*> &, ExpressionVisitor &);
|
||||
virtual void _importSubNames(const ObjectIdentifier::SubNameMap &);
|
||||
virtual void _updateLabelReference(App::DocumentObject *, const std::string &, const char *);
|
||||
virtual bool _updateElementReference(App::DocumentObject *,bool,ExpressionVisitor &);
|
||||
virtual bool _relabeledDocument(const std::string &, const std::string &, ExpressionVisitor &);
|
||||
virtual bool _renameObjectIdentifier(const std::map<ObjectIdentifier,ObjectIdentifier> &,
|
||||
const ObjectIdentifier &, ExpressionVisitor &);
|
||||
virtual void _collectReplacement(std::map<ObjectIdentifier,ObjectIdentifier> &,
|
||||
const App::DocumentObject *parent, App::DocumentObject *oldObj,
|
||||
App::DocumentObject *newObj) const;
|
||||
virtual void _moveCells(const CellAddress &, int, int, ExpressionVisitor &);
|
||||
virtual void _offsetCells(int, int, ExpressionVisitor &);
|
||||
|
||||
protected:
|
||||
|
||||
ObjectIdentifier var; /**< Variable name */
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
class AppExport PyObjectExpression : public Expression {
|
||||
TYPESYSTEM_HEADER();
|
||||
|
||||
public:
|
||||
PyObjectExpression(const App::DocumentObject *_owner=0, PyObject *pyobj=0, bool owned=false)
|
||||
:Expression(_owner)
|
||||
{
|
||||
setPyObject(pyobj,owned);
|
||||
}
|
||||
|
||||
virtual ~PyObjectExpression();
|
||||
|
||||
Py::Object getPyObject() const;
|
||||
|
||||
void setPyObject(Py::Object pyobj);
|
||||
void setPyObject(PyObject *pyobj, bool owned=false);
|
||||
|
||||
virtual std::string toString(bool) const;
|
||||
virtual boost::any getValueAsAny() const;
|
||||
|
||||
virtual Expression * eval() const { return copy(); }
|
||||
virtual Expression * simplify() const { return copy(); }
|
||||
|
||||
protected:
|
||||
virtual Expression* _copy() const;
|
||||
|
||||
protected:
|
||||
PyObject *pyObj = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Class implementing a string. Used to signal either a genuine string or
|
||||
* a failed evaluation of an expression.
|
||||
@@ -436,13 +561,13 @@ public:
|
||||
|
||||
virtual Expression * simplify() const;
|
||||
|
||||
virtual std::string toString() const;
|
||||
virtual std::string toString(bool persistent=false) const;
|
||||
|
||||
virtual std::string getText() const { return text; }
|
||||
|
||||
virtual int priority() const;
|
||||
|
||||
virtual Expression * copy() const;
|
||||
virtual Expression * _copy() const;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -460,22 +585,26 @@ public:
|
||||
|
||||
virtual Expression * eval() const;
|
||||
|
||||
virtual std::string toString() const;
|
||||
virtual std::string toString(bool persistent=false) const;
|
||||
|
||||
virtual Expression * copy() const;
|
||||
virtual Expression * _copy() const;
|
||||
|
||||
virtual int priority() const;
|
||||
|
||||
virtual void getDeps(std::set<App::ObjectIdentifier> &props) const;
|
||||
|
||||
virtual App::Expression * simplify() const;
|
||||
|
||||
Range getRange() const { return range; }
|
||||
|
||||
void setRange(const Range & r);
|
||||
Range getRange() const;
|
||||
|
||||
protected:
|
||||
Range range;
|
||||
virtual void _getDeps(ExpressionDeps &) const;
|
||||
virtual bool _renameObjectIdentifier(const std::map<ObjectIdentifier,ObjectIdentifier> &,
|
||||
const ObjectIdentifier &, ExpressionVisitor &);
|
||||
virtual void _moveCells(const CellAddress &, int, int, ExpressionVisitor &);
|
||||
virtual void _offsetCells(int, int, ExpressionVisitor &);
|
||||
|
||||
protected:
|
||||
std::string begin;
|
||||
std::string end;
|
||||
};
|
||||
|
||||
namespace ExpressionParser {
|
||||
@@ -486,6 +615,16 @@ AppExport bool isTokenAnIndentifier(const std::string & str);
|
||||
AppExport bool isTokenAUnit(const std::string & str);
|
||||
AppExport std::vector<boost::tuple<int, int, std::string> > tokenize(const std::string & str);
|
||||
|
||||
/// Convenient class to mark begin of importing
|
||||
class AppExport ExpressionImporter {
|
||||
public:
|
||||
ExpressionImporter(Base::XMLReader &reader);
|
||||
~ExpressionImporter();
|
||||
static Base::XMLReader *reader();
|
||||
};
|
||||
|
||||
AppExport bool isModuleImported(PyObject *);
|
||||
|
||||
/**
|
||||
* @brief The semantic_type class encapsulates the value in the parse tree during parsing.
|
||||
*/
|
||||
@@ -521,4 +660,9 @@ public:
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // EXPRESSION_H
|
||||
|
||||
+258
-234
@@ -382,18 +382,18 @@ union yyalloc
|
||||
#endif /* !YYCOPY_NEEDED */
|
||||
|
||||
/* YYFINAL -- State number of the termination state. */
|
||||
#define YYFINAL 34
|
||||
#define YYFINAL 38
|
||||
/* YYLAST -- Last index in YYTABLE. */
|
||||
#define YYLAST 188
|
||||
#define YYLAST 196
|
||||
|
||||
/* YYNTOKENS -- Number of terminals. */
|
||||
#define YYNTOKENS 40
|
||||
/* YYNNTS -- Number of nonterminals. */
|
||||
#define YYNNTS 14
|
||||
/* YYNRULES -- Number of rules. */
|
||||
#define YYNRULES 73
|
||||
#define YYNRULES 74
|
||||
/* YYNSTATES -- Number of states. */
|
||||
#define YYNSTATES 130
|
||||
#define YYNSTATES 132
|
||||
|
||||
/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
|
||||
by yylex, with out-of-bounds checking. */
|
||||
@@ -446,10 +446,10 @@ static const yytype_uint8 yyrline[] =
|
||||
76, 77, 78, 79, 80, 81, 82, 83, 84, 87,
|
||||
88, 89, 90, 92, 93, 94, 95, 96, 97, 100,
|
||||
101, 102, 103, 106, 107, 108, 109, 110, 111, 114,
|
||||
115, 116, 117, 118, 119, 122, 126, 131, 136, 144,
|
||||
145, 149, 150, 151, 152, 153, 154, 155, 156, 157,
|
||||
160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
|
||||
172, 173, 176, 177
|
||||
115, 116, 117, 118, 119, 122, 126, 131, 136, 141,
|
||||
149, 150, 154, 155, 156, 157, 158, 159, 160, 161,
|
||||
162, 165, 166, 167, 168, 169, 170, 171, 172, 173,
|
||||
174, 177, 178, 181, 182
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -481,12 +481,12 @@ static const yytype_uint16 yytoknum[] =
|
||||
};
|
||||
# endif
|
||||
|
||||
#define YYPACT_NINF -100
|
||||
#define YYPACT_NINF -102
|
||||
|
||||
#define yypact_value_is_default(Yystate) \
|
||||
(!!((Yystate) == (-100)))
|
||||
(!!((Yystate) == (-102)))
|
||||
|
||||
#define YYTABLE_NINF -74
|
||||
#define YYTABLE_NINF -75
|
||||
|
||||
#define yytable_value_is_error(Yytable_value) \
|
||||
0
|
||||
@@ -495,19 +495,20 @@ static const yytype_uint16 yytoknum[] =
|
||||
STATE-NUM. */
|
||||
static const yytype_int16 yypact[] =
|
||||
{
|
||||
61, 79, -100, -100, 137, -100, -100, -100, -28, 106,
|
||||
104, 104, 61, 18, 154, -1, 27, 89, -100, -100,
|
||||
16, 26, -15, -17, 104, 154, 143, -100, 7, 86,
|
||||
-100, -100, 112, 47, -100, 104, 104, 104, 104, 104,
|
||||
104, 104, 104, 104, 61, 104, -1, 31, 104, -1,
|
||||
-1, 43, 146, 22, 23, 24, -100, 79, 79, 39,
|
||||
-100, -100, -100, -100, 33, -100, 42, 56, -100, -100,
|
||||
154, 154, 154, 154, 154, 154, 128, 128, 72, 72,
|
||||
31, -100, 135, 31, 31, 48, -100, 81, -100, -100,
|
||||
69, -100, -100, -100, -100, -100, -100, 154, -100, 154,
|
||||
-100, 7, 127, 84, 96, 98, 104, -100, 22, -100,
|
||||
101, 119, 132, 7, 7, 7, 73, -100, 148, 149,
|
||||
152, -100, -100, -100, 7, 7, 7, -100, -100, -100
|
||||
67, 92, -102, -102, 131, -102, -102, -102, -32, -2,
|
||||
117, 117, 67, 3, 24, 167, -4, 15, 86, -102,
|
||||
-102, 23, 45, -7, -18, 117, 167, 153, -102, 73,
|
||||
74, -102, -102, 125, 60, 19, -102, -102, -102, 117,
|
||||
117, 117, 117, 117, 117, 117, 117, 117, 67, 117,
|
||||
-4, 59, 117, -4, -4, 4, 101, 3, 13, 26,
|
||||
-102, 92, 92, 30, -102, -102, -102, -102, 55, -102,
|
||||
75, 77, -102, -102, 167, 167, 167, 167, 167, 167,
|
||||
103, 103, 90, 90, 59, -102, 148, 59, 59, 29,
|
||||
-102, -102, -102, 95, -102, -102, -102, -102, -102, 167,
|
||||
-102, 167, -102, 73, 140, 97, 109, 111, 117, -102,
|
||||
3, -102, 116, 126, 132, 73, 73, 73, 38, -102,
|
||||
120, 134, 154, -102, -102, -102, 73, 73, 73, -102,
|
||||
-102, -102
|
||||
};
|
||||
|
||||
/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
|
||||
@@ -515,33 +516,34 @@ static const yytype_int16 yypact[] =
|
||||
means the default is an error. */
|
||||
static const yytype_uint8 yydefact[] =
|
||||
{
|
||||
0, 0, 19, 20, 51, 39, 21, 22, 52, 6,
|
||||
0, 0, 0, 0, 2, 4, 0, 3, 7, 45,
|
||||
0, 0, 51, 52, 0, 23, 0, 24, 0, 0,
|
||||
8, 9, 0, 0, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 5, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 17, 0, 0, 60,
|
||||
62, 61, 59, 50, 0, 49, 0, 0, 16, 44,
|
||||
33, 34, 35, 36, 37, 38, 11, 10, 12, 13,
|
||||
14, 15, 0, 41, 40, 0, 42, 51, 72, 47,
|
||||
0, 52, 46, 32, 31, 30, 29, 25, 27, 26,
|
||||
28, 0, 0, 56, 55, 53, 0, 43, 0, 69,
|
||||
0, 0, 0, 0, 0, 0, 18, 48, 66, 65,
|
||||
63, 58, 57, 54, 0, 0, 0, 68, 67, 64
|
||||
0, 0, 19, 20, 52, 39, 21, 22, 53, 6,
|
||||
0, 0, 0, 0, 0, 2, 4, 0, 3, 7,
|
||||
45, 0, 0, 52, 53, 0, 23, 0, 24, 0,
|
||||
0, 8, 9, 0, 0, 52, 53, 46, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 5, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 0, 0, 61, 63, 62, 60, 51, 0, 50,
|
||||
0, 0, 16, 44, 33, 34, 35, 36, 37, 38,
|
||||
11, 10, 12, 13, 14, 15, 0, 41, 40, 0,
|
||||
42, 73, 48, 0, 47, 32, 31, 30, 29, 25,
|
||||
27, 26, 28, 0, 0, 57, 56, 54, 0, 43,
|
||||
0, 70, 0, 0, 0, 0, 0, 0, 18, 49,
|
||||
67, 66, 64, 59, 58, 55, 0, 0, 0, 69,
|
||||
68, 65
|
||||
};
|
||||
|
||||
/* YYPGOTO[NTERM-NUM]. */
|
||||
static const yytype_int16 yypgoto[] =
|
||||
{
|
||||
-100, -100, 0, -100, -100, 129, -100, 5, -100, -39,
|
||||
-49, -99, -100, 130
|
||||
-102, -102, 0, -102, -102, 44, -102, 5, -102, -35,
|
||||
-6, -101, -102, 128
|
||||
};
|
||||
|
||||
/* YYDEFGOTO[NTERM-NUM]. */
|
||||
static const yytype_int8 yydefgoto[] =
|
||||
{
|
||||
-1, 13, 32, 15, 26, 27, 16, 33, 18, 67,
|
||||
19, 62, 20, 21
|
||||
-1, 14, 33, 16, 27, 28, 17, 34, 19, 71,
|
||||
20, 66, 21, 22
|
||||
};
|
||||
|
||||
/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If
|
||||
@@ -549,48 +551,50 @@ static const yytype_int8 yydefgoto[] =
|
||||
number is the opposite. If YYTABLE_NINF, syntax error. */
|
||||
static const yytype_int16 yytable[] =
|
||||
{
|
||||
14, 25, 109, 89, 92, 17, 5, 55, -73, 54,
|
||||
30, 31, 86, 59, 121, 122, 123, 60, 34, -73,
|
||||
47, 28, -71, 29, 61, 127, 128, 129, 87, 93,
|
||||
95, 46, 91, 94, 96, 70, 71, 72, 73, 74,
|
||||
75, 76, 77, 78, 79, 81, 107, 63, 82, 80,
|
||||
48, 65, 63, 52, 83, 84, 65, 97, 99, 117,
|
||||
51, 85, 53, 112, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 103, 49, 50, 101, 51, 102, 9, 10,
|
||||
69, 104, 1, 2, 3, 22, 11, 6, 7, 23,
|
||||
63, 41, 64, 12, 65, 105, 9, 10, 42, 43,
|
||||
44, 45, 45, 66, 11, 108, 116, 1, 2, 3,
|
||||
4, 24, 6, 7, 8, 49, 50, 28, 51, 29,
|
||||
113, 9, 10, 35, 36, 37, 38, 39, 40, 11,
|
||||
41, 63, 114, 110, 115, 65, 24, 42, 43, 44,
|
||||
118, 45, -72, -70, 111, 68, 35, 36, 37, 38,
|
||||
39, 40, 87, 41, 43, 44, 8, 45, 119, 106,
|
||||
42, 43, 44, 88, 45, 35, 36, 37, 38, 39,
|
||||
40, 120, 41, 28, -71, 29, 56, 57, 58, 42,
|
||||
43, 44, 90, 45, 124, 125, 98, 100, 126
|
||||
15, 26, 111, 5, -74, 18, 59, 37, 67, 35,
|
||||
31, 32, 69, 36, 123, 124, 125, 58, -74, 95,
|
||||
90, 51, 89, 96, 38, 129, 130, 131, 50, 29,
|
||||
-72, 30, 97, 67, -73, -71, 98, 69, 52, 74,
|
||||
75, 76, 77, 78, 79, 80, 81, 82, 83, 85,
|
||||
92, 94, 86, 84, 109, 29, 45, 30, 87, 88,
|
||||
56, 99, 101, 46, 47, 48, 103, 49, 104, 114,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 67, 63,
|
||||
68, 57, 69, 64, 9, 10, 53, 54, 55, 55,
|
||||
65, 70, 11, 73, 105, 1, 2, 3, 23, 12,
|
||||
6, 7, 24, 13, 119, 100, 102, 35, 118, 9,
|
||||
10, 8, 53, 54, 106, 55, 107, 11, 91, 49,
|
||||
1, 2, 3, 4, 25, 6, 7, 8, 13, 47,
|
||||
48, 110, 49, 115, 9, 10, 39, 40, 41, 42,
|
||||
43, 44, 11, 45, 67, 116, 112, 117, 69, 25,
|
||||
46, 47, 48, 13, 49, 120, 126, 113, 72, 39,
|
||||
40, 41, 42, 43, 44, 121, 45, 29, -72, 30,
|
||||
127, 122, 108, 46, 47, 48, 0, 49, 39, 40,
|
||||
41, 42, 43, 44, 93, 45, 60, 61, 62, 0,
|
||||
128, 0, 46, 47, 48, 0, 49
|
||||
};
|
||||
|
||||
static const yytype_uint8 yycheck[] =
|
||||
static const yytype_int16 yycheck[] =
|
||||
{
|
||||
0, 1, 101, 52, 53, 0, 7, 24, 36, 24,
|
||||
10, 11, 51, 6, 113, 114, 115, 10, 0, 36,
|
||||
15, 36, 37, 38, 17, 124, 125, 126, 6, 6,
|
||||
6, 32, 10, 10, 10, 35, 36, 37, 38, 39,
|
||||
40, 41, 42, 43, 44, 45, 85, 4, 48, 44,
|
||||
23, 8, 4, 37, 49, 50, 8, 57, 58, 108,
|
||||
29, 18, 36, 102, 3, 4, 5, 6, 7, 8,
|
||||
9, 10, 39, 26, 27, 36, 29, 38, 17, 18,
|
||||
33, 39, 3, 4, 5, 6, 25, 8, 9, 10,
|
||||
4, 18, 6, 32, 8, 39, 17, 18, 25, 26,
|
||||
27, 29, 29, 17, 25, 36, 106, 3, 4, 5,
|
||||
6, 32, 8, 9, 10, 26, 27, 36, 29, 38,
|
||||
36, 17, 18, 11, 12, 13, 14, 15, 16, 25,
|
||||
18, 4, 36, 6, 36, 8, 32, 25, 26, 27,
|
||||
39, 29, 36, 37, 17, 33, 11, 12, 13, 14,
|
||||
15, 16, 6, 18, 26, 27, 10, 29, 39, 24,
|
||||
25, 26, 27, 17, 29, 11, 12, 13, 14, 15,
|
||||
16, 39, 18, 36, 37, 38, 33, 34, 35, 25,
|
||||
26, 27, 52, 29, 36, 36, 57, 58, 36
|
||||
0, 1, 103, 7, 36, 0, 24, 13, 4, 6,
|
||||
10, 11, 8, 10, 115, 116, 117, 24, 36, 6,
|
||||
55, 16, 18, 10, 0, 126, 127, 128, 32, 36,
|
||||
37, 38, 6, 4, 36, 37, 10, 8, 23, 39,
|
||||
40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
|
||||
56, 57, 52, 48, 89, 36, 18, 38, 53, 54,
|
||||
37, 61, 62, 25, 26, 27, 36, 29, 38, 104,
|
||||
3, 4, 5, 6, 7, 8, 9, 10, 4, 6,
|
||||
6, 36, 8, 10, 17, 18, 26, 27, 29, 29,
|
||||
17, 17, 25, 33, 39, 3, 4, 5, 6, 32,
|
||||
8, 9, 10, 36, 110, 61, 62, 6, 108, 17,
|
||||
18, 10, 26, 27, 39, 29, 39, 25, 17, 29,
|
||||
3, 4, 5, 6, 32, 8, 9, 10, 36, 26,
|
||||
27, 36, 29, 36, 17, 18, 11, 12, 13, 14,
|
||||
15, 16, 25, 18, 4, 36, 6, 36, 8, 32,
|
||||
25, 26, 27, 36, 29, 39, 36, 17, 33, 11,
|
||||
12, 13, 14, 15, 16, 39, 18, 36, 37, 38,
|
||||
36, 39, 24, 25, 26, 27, -1, 29, 11, 12,
|
||||
13, 14, 15, 16, 56, 18, 33, 34, 35, -1,
|
||||
36, -1, 25, 26, 27, -1, 29
|
||||
};
|
||||
|
||||
/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
|
||||
@@ -598,18 +602,19 @@ static const yytype_uint8 yycheck[] =
|
||||
static const yytype_uint8 yystos[] =
|
||||
{
|
||||
0, 3, 4, 5, 6, 7, 8, 9, 10, 17,
|
||||
18, 25, 32, 41, 42, 43, 46, 47, 48, 50,
|
||||
52, 53, 6, 10, 32, 42, 44, 45, 36, 38,
|
||||
42, 42, 42, 47, 0, 11, 12, 13, 14, 15,
|
||||
16, 18, 25, 26, 27, 29, 32, 47, 23, 26,
|
||||
27, 29, 37, 36, 24, 24, 33, 34, 35, 6,
|
||||
10, 17, 51, 4, 6, 8, 17, 49, 33, 33,
|
||||
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
|
||||
47, 42, 42, 47, 47, 18, 49, 6, 17, 50,
|
||||
53, 10, 50, 6, 10, 6, 10, 42, 45, 42,
|
||||
45, 36, 38, 39, 39, 39, 24, 49, 36, 51,
|
||||
6, 17, 49, 36, 36, 36, 42, 50, 39, 39,
|
||||
39, 51, 51, 51, 36, 36, 36, 51, 51, 51
|
||||
18, 25, 32, 36, 41, 42, 43, 46, 47, 48,
|
||||
50, 52, 53, 6, 10, 32, 42, 44, 45, 36,
|
||||
38, 42, 42, 42, 47, 6, 10, 50, 0, 11,
|
||||
12, 13, 14, 15, 16, 18, 25, 26, 27, 29,
|
||||
32, 47, 23, 26, 27, 29, 37, 36, 24, 24,
|
||||
33, 34, 35, 6, 10, 17, 51, 4, 6, 8,
|
||||
17, 49, 33, 33, 42, 42, 42, 42, 42, 42,
|
||||
42, 42, 42, 42, 47, 42, 42, 47, 47, 18,
|
||||
49, 17, 50, 53, 50, 6, 10, 6, 10, 42,
|
||||
45, 42, 45, 36, 38, 39, 39, 39, 24, 49,
|
||||
36, 51, 6, 17, 49, 36, 36, 36, 42, 50,
|
||||
39, 39, 39, 51, 51, 51, 36, 36, 36, 51,
|
||||
51, 51
|
||||
};
|
||||
|
||||
/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
|
||||
@@ -619,10 +624,10 @@ static const yytype_uint8 yyr1[] =
|
||||
42, 42, 42, 42, 42, 42, 42, 42, 42, 43,
|
||||
43, 43, 43, 44, 44, 44, 44, 44, 44, 45,
|
||||
45, 45, 45, 46, 46, 46, 46, 46, 46, 47,
|
||||
47, 47, 47, 47, 47, 48, 48, 48, 48, 49,
|
||||
49, 50, 50, 50, 50, 50, 50, 50, 50, 50,
|
||||
51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
|
||||
52, 52, 53, 53
|
||||
47, 47, 47, 47, 47, 48, 48, 48, 48, 48,
|
||||
49, 49, 50, 50, 50, 50, 50, 50, 50, 50,
|
||||
50, 51, 51, 51, 51, 51, 51, 51, 51, 51,
|
||||
51, 52, 52, 53, 53
|
||||
};
|
||||
|
||||
/* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */
|
||||
@@ -632,10 +637,10 @@ static const yytype_uint8 yyr2[] =
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 5, 1,
|
||||
1, 1, 1, 1, 1, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 1,
|
||||
3, 3, 3, 4, 3, 1, 3, 3, 5, 1,
|
||||
1, 1, 1, 4, 6, 4, 4, 6, 6, 3,
|
||||
1, 1, 1, 4, 6, 4, 4, 6, 6, 3,
|
||||
1, 1, 1, 1
|
||||
3, 3, 3, 4, 3, 1, 2, 3, 3, 5,
|
||||
1, 1, 1, 1, 4, 6, 4, 4, 6, 6,
|
||||
3, 1, 1, 1, 4, 6, 4, 4, 6, 6,
|
||||
3, 1, 1, 1, 1
|
||||
};
|
||||
|
||||
|
||||
@@ -1061,25 +1066,25 @@ yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
|
||||
case 42: /* exp */
|
||||
#line 59 "ExpressionParser.y" /* yacc.c:1257 */
|
||||
{ delete ((*yyvaluep).expr); }
|
||||
#line 1065 "ExpressionParser.tab.c" /* yacc.c:1257 */
|
||||
#line 1070 "ExpressionParser.tab.c" /* yacc.c:1257 */
|
||||
break;
|
||||
|
||||
case 44: /* args */
|
||||
#line 60 "ExpressionParser.y" /* yacc.c:1257 */
|
||||
{ std::vector<Expression*>::const_iterator i = ((*yyvaluep).arguments).begin(); while (i != ((*yyvaluep).arguments).end()) { delete *i; ++i; } }
|
||||
#line 1071 "ExpressionParser.tab.c" /* yacc.c:1257 */
|
||||
#line 1076 "ExpressionParser.tab.c" /* yacc.c:1257 */
|
||||
break;
|
||||
|
||||
case 46: /* cond */
|
||||
#line 59 "ExpressionParser.y" /* yacc.c:1257 */
|
||||
{ delete ((*yyvaluep).expr); }
|
||||
#line 1077 "ExpressionParser.tab.c" /* yacc.c:1257 */
|
||||
#line 1082 "ExpressionParser.tab.c" /* yacc.c:1257 */
|
||||
break;
|
||||
|
||||
case 47: /* unit_exp */
|
||||
#line 59 "ExpressionParser.y" /* yacc.c:1257 */
|
||||
{ delete ((*yyvaluep).expr); }
|
||||
#line 1083 "ExpressionParser.tab.c" /* yacc.c:1257 */
|
||||
#line 1088 "ExpressionParser.tab.c" /* yacc.c:1257 */
|
||||
break;
|
||||
|
||||
|
||||
@@ -1343,259 +1348,259 @@ yyreduce:
|
||||
case 2:
|
||||
#line 66 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ ScanResult = (yyvsp[0].expr); valueExpression = true; }
|
||||
#line 1347 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1352 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 3:
|
||||
#line 67 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ ScanResult = (yyvsp[0].expr); unitExpression = true; }
|
||||
#line 1353 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1358 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 4:
|
||||
#line 70 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = (yyvsp[0].expr); }
|
||||
#line 1359 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1364 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 5:
|
||||
#line 71 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-1].expr), OperatorExpression::UNIT, (yyvsp[0].expr)); }
|
||||
#line 1365 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1370 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 6:
|
||||
#line 72 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new StringExpression(DocumentObject, (yyvsp[0].string)); }
|
||||
#line 1371 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1376 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 7:
|
||||
#line 73 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new VariableExpression(DocumentObject, (yyvsp[0].path)); }
|
||||
#line 1377 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1382 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 8:
|
||||
#line 74 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[0].expr), OperatorExpression::NEG, new NumberExpression(DocumentObject, Quantity(-1))); }
|
||||
#line 1383 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1388 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 9:
|
||||
#line 75 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[0].expr), OperatorExpression::POS, new NumberExpression(DocumentObject, Quantity(1))); }
|
||||
#line 1389 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1394 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 10:
|
||||
#line 76 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::ADD, (yyvsp[0].expr)); }
|
||||
#line 1395 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1400 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 11:
|
||||
#line 77 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::SUB, (yyvsp[0].expr)); }
|
||||
#line 1401 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1406 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 12:
|
||||
#line 78 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::MUL, (yyvsp[0].expr)); }
|
||||
#line 1407 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1412 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 13:
|
||||
#line 79 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::DIV, (yyvsp[0].expr)); }
|
||||
#line 1413 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1418 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 14:
|
||||
#line 80 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::DIV, (yyvsp[0].expr)); }
|
||||
#line 1419 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1424 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 15:
|
||||
#line 81 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::POW, (yyvsp[0].expr)); }
|
||||
#line 1425 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1430 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 16:
|
||||
#line 82 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = (yyvsp[-1].expr); }
|
||||
#line 1431 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1436 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 17:
|
||||
#line 83 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new FunctionExpression(DocumentObject, (yyvsp[-2].func), (yyvsp[-1].arguments)); }
|
||||
#line 1437 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1442 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 18:
|
||||
#line 84 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new ConditionalExpression(DocumentObject, (yyvsp[-4].expr), (yyvsp[-2].expr), (yyvsp[0].expr)); }
|
||||
#line 1443 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1448 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 19:
|
||||
#line 87 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new NumberExpression(DocumentObject, Quantity((yyvsp[0].fvalue))); }
|
||||
#line 1449 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1454 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 20:
|
||||
#line 88 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new NumberExpression(DocumentObject, Quantity((yyvsp[0].fvalue))); }
|
||||
#line 1455 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1460 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 21:
|
||||
#line 89 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new NumberExpression(DocumentObject, Quantity((double)(yyvsp[0].ivalue))); }
|
||||
#line 1461 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1466 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 22:
|
||||
#line 90 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new ConstantExpression(DocumentObject, (yyvsp[0].constant).name, Quantity((yyvsp[0].constant).fvalue)); }
|
||||
#line 1467 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1472 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 23:
|
||||
#line 92 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.arguments).push_back((yyvsp[0].expr)); }
|
||||
#line 1473 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1478 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 24:
|
||||
#line 93 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.arguments).push_back((yyvsp[0].expr)); }
|
||||
#line 1479 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1484 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 25:
|
||||
#line 94 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); }
|
||||
#line 1485 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1490 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 26:
|
||||
#line 95 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); }
|
||||
#line 1491 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1496 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 27:
|
||||
#line 96 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); }
|
||||
#line 1497 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1502 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 28:
|
||||
#line 97 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); }
|
||||
#line 1503 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1508 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 29:
|
||||
#line 100 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new RangeExpression(DocumentObject, (yyvsp[-2].string), (yyvsp[0].string)); }
|
||||
#line 1509 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1514 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 30:
|
||||
#line 101 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new RangeExpression(DocumentObject, (yyvsp[-2].string), (yyvsp[0].string)); }
|
||||
#line 1515 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1520 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 31:
|
||||
#line 102 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new RangeExpression(DocumentObject, (yyvsp[-2].string), (yyvsp[0].string)); }
|
||||
#line 1521 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1526 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 32:
|
||||
#line 103 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new RangeExpression(DocumentObject, (yyvsp[-2].string), (yyvsp[0].string)); }
|
||||
#line 1527 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1532 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 33:
|
||||
#line 106 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::EQ, (yyvsp[0].expr)); }
|
||||
#line 1533 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1538 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 34:
|
||||
#line 107 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::NEQ, (yyvsp[0].expr)); }
|
||||
#line 1539 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1544 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 35:
|
||||
#line 108 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::LT, (yyvsp[0].expr)); }
|
||||
#line 1545 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1550 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 36:
|
||||
#line 109 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::GT, (yyvsp[0].expr)); }
|
||||
#line 1551 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1556 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 37:
|
||||
#line 110 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::GTE, (yyvsp[0].expr)); }
|
||||
#line 1557 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1562 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 38:
|
||||
#line 111 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::LTE, (yyvsp[0].expr)); }
|
||||
#line 1563 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1568 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 39:
|
||||
#line 114 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new UnitExpression(DocumentObject, (yyvsp[0].quantity).scaler, (yyvsp[0].quantity).unitStr ); }
|
||||
#line 1569 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1574 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 40:
|
||||
#line 115 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::DIV, (yyvsp[0].expr)); }
|
||||
#line 1575 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1580 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 41:
|
||||
#line 116 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::MUL, (yyvsp[0].expr)); }
|
||||
#line 1581 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1586 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 42:
|
||||
#line 117 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::POW, new NumberExpression(DocumentObject, Quantity((double)(yyvsp[0].ivalue)))); }
|
||||
#line 1587 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1592 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 43:
|
||||
#line 118 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (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 1593 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1598 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 44:
|
||||
#line 119 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.expr) = (yyvsp[-1].expr); }
|
||||
#line 1599 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1604 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 45:
|
||||
@@ -1603,193 +1608,212 @@ yyreduce:
|
||||
{ /* Path to property within document object */
|
||||
(yyval.path) = ObjectIdentifier(DocumentObject);
|
||||
(yyval.path).addComponents((yyvsp[0].components));
|
||||
(yyval.path).resolveAmbiguity();
|
||||
}
|
||||
#line 1608 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1613 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 46:
|
||||
#line 126 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ /* Path to property within document object */
|
||||
(yyval.path) = ObjectIdentifier(DocumentObject);
|
||||
(yyval.path).setDocumentObjectName((yyvsp[-2].string_or_identifier), true);
|
||||
{ /* Path to property of the current document object */
|
||||
(yyval.path) = ObjectIdentifier(DocumentObject,true);
|
||||
(yyval.path).setDocumentObjectName(DocumentObject);
|
||||
(yyval.path).addComponents((yyvsp[0].components));
|
||||
}
|
||||
#line 1618 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1623 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 47:
|
||||
#line 131 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ /* Path to property from an external document, within a named document object */
|
||||
{ /* Path to property within document object */
|
||||
(yyval.path) = ObjectIdentifier(DocumentObject);
|
||||
(yyval.path).setDocumentName((yyvsp[-2].string_or_identifier), true);
|
||||
(yyvsp[-2].string_or_identifier).checkImport(DocumentObject);
|
||||
(yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[-2].string_or_identifier)));
|
||||
(yyval.path).addComponents((yyvsp[0].components));
|
||||
(yyval.path).resolveAmbiguity();
|
||||
}
|
||||
#line 1628 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1633 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 48:
|
||||
#line 136 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ /* Path to property from an external document, within a named document object */
|
||||
(yyval.path) = ObjectIdentifier(DocumentObject);
|
||||
(yyval.path).setDocumentName((yyvsp[-4].string_or_identifier), true);
|
||||
(yyval.path).setDocumentObjectName((yyvsp[-2].string_or_identifier), true);
|
||||
(yyval.path).setDocumentName(std::move(yyvsp[-2].string_or_identifier), true);
|
||||
if((yyvsp[0].components).size()) {
|
||||
(yyval.path).setDocumentObjectName(ObjectIdentifier::String((yyvsp[0].components).front().getName(),false,true), true);
|
||||
(yyvsp[0].components).pop_front();
|
||||
}
|
||||
(yyval.path).addComponents((yyvsp[0].components));
|
||||
(yyval.path).resolveAmbiguity();
|
||||
}
|
||||
#line 1639 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1643 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 49:
|
||||
#line 144 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.ivalue) = (yyvsp[0].ivalue); }
|
||||
#line 1645 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 141 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ /* 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);
|
||||
(yyval.path).setDocumentObjectName(std::move(yyvsp[-2].string_or_identifier), true);
|
||||
(yyval.path).addComponents((yyvsp[0].components));
|
||||
(yyval.path).resolveAmbiguity();
|
||||
}
|
||||
#line 1654 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 50:
|
||||
#line 145 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.ivalue) = (yyvsp[0].fvalue); }
|
||||
#line 1651 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 149 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.ivalue) = (yyvsp[0].ivalue); }
|
||||
#line 1660 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 51:
|
||||
#line 149 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::Component::SimpleComponent((yyvsp[0].string))); }
|
||||
#line 1657 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 150 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.ivalue) = (yyvsp[0].fvalue); }
|
||||
#line 1666 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 52:
|
||||
#line 150 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::Component::SimpleComponent((yyvsp[0].string))); }
|
||||
#line 1663 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 154 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[0].string))); }
|
||||
#line 1672 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 53:
|
||||
#line 151 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::Component::ArrayComponent((yyvsp[-3].string), (yyvsp[-1].ivalue))); }
|
||||
#line 1669 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 155 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[0].string))); }
|
||||
#line 1678 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 54:
|
||||
#line 152 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::Component::ArrayComponent((yyvsp[-5].string), (yyvsp[-3].ivalue))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1675 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 156 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::ArrayComponent((yyvsp[-1].ivalue))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-3].string))); }
|
||||
#line 1684 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 55:
|
||||
#line 153 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-3].string), ObjectIdentifier::String((yyvsp[-1].string), true))); }
|
||||
#line 1681 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 157 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::ArrayComponent((yyvsp[-3].ivalue))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-5].string))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1690 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 56:
|
||||
#line 154 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-3].string), (yyvsp[-1].string))); }
|
||||
#line 1687 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 158 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String((yyvsp[-1].string), true))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-3].string))); }
|
||||
#line 1696 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 57:
|
||||
#line 155 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-5].string), ObjectIdentifier::String((yyvsp[-3].string), true))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1693 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 159 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::MapComponent((yyvsp[-1].string))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-3].string))); }
|
||||
#line 1702 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 58:
|
||||
#line 156 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-5].string), (yyvsp[-3].string))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1699 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 160 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String((yyvsp[-3].string), true))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-5].string))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1708 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 59:
|
||||
#line 157 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::Component::SimpleComponent((yyvsp[-2].string))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1705 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 161 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::MapComponent((yyvsp[-3].string))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-5].string))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1714 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 60:
|
||||
#line 160 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::Component::SimpleComponent((yyvsp[0].string))); }
|
||||
#line 1711 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 162 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-2].string))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1720 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 61:
|
||||
#line 161 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::Component::SimpleComponent((yyvsp[0].string))); }
|
||||
#line 1717 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 165 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[0].string))); }
|
||||
#line 1726 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 62:
|
||||
#line 162 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::Component::SimpleComponent((yyvsp[0].string))); }
|
||||
#line 1723 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 166 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[0].string))); }
|
||||
#line 1732 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 63:
|
||||
#line 163 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::Component::ArrayComponent((yyvsp[-3].string), (yyvsp[-1].ivalue))); }
|
||||
#line 1729 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 167 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[0].string))); }
|
||||
#line 1738 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 64:
|
||||
#line 164 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::Component::ArrayComponent((yyvsp[-5].string), (yyvsp[-3].ivalue))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1735 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 168 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::ArrayComponent((yyvsp[-1].ivalue))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-3].string))); }
|
||||
#line 1744 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 65:
|
||||
#line 165 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-3].string), ObjectIdentifier::String((yyvsp[-1].string), true))); }
|
||||
#line 1741 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 169 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::ArrayComponent((yyvsp[-3].ivalue))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-5].string))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1750 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 66:
|
||||
#line 166 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-3].string), (yyvsp[-1].string))); }
|
||||
#line 1747 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 170 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String((yyvsp[-1].string), true))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-3].string))); }
|
||||
#line 1756 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 67:
|
||||
#line 167 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-5].string), ObjectIdentifier::String((yyvsp[-3].string), true))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1753 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 171 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.components).push_front(ObjectIdentifier::MapComponent((yyvsp[-1].string))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-3].string))); }
|
||||
#line 1762 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 68:
|
||||
#line 168 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-5].string), (yyvsp[-3].string))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1759 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 172 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String((yyvsp[-3].string), true))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-5].string))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1768 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 69:
|
||||
#line 169 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::Component::SimpleComponent((yyvsp[-2].string))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1765 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 173 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::MapComponent((yyvsp[-3].string))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-5].string))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1774 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 70:
|
||||
#line 172 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.string_or_identifier) = ObjectIdentifier::String((yyvsp[0].string), true); }
|
||||
#line 1771 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 174 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyvsp[0].components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-2].string))); (yyval.components) = (yyvsp[0].components); }
|
||||
#line 1780 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 71:
|
||||
#line 173 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.string_or_identifier) = ObjectIdentifier::String((yyvsp[0].string)); }
|
||||
#line 1777 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 177 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.string_or_identifier) = ObjectIdentifier::String((yyvsp[0].string), true); }
|
||||
#line 1786 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 72:
|
||||
#line 176 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.string_or_identifier) = ObjectIdentifier::String((yyvsp[0].string), true); }
|
||||
#line 1783 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 178 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.string_or_identifier) = ObjectIdentifier::String((yyvsp[0].string)); }
|
||||
#line 1792 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 73:
|
||||
#line 177 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
#line 181 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.string_or_identifier) = ObjectIdentifier::String((yyvsp[0].string), true); }
|
||||
#line 1789 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1798 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
case 74:
|
||||
#line 182 "ExpressionParser.y" /* yacc.c:1646 */
|
||||
{ (yyval.string_or_identifier) = ObjectIdentifier::String((yyvsp[0].string), true); }
|
||||
#line 1804 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
break;
|
||||
|
||||
|
||||
#line 1793 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
#line 1808 "ExpressionParser.tab.c" /* yacc.c:1646 */
|
||||
default: break;
|
||||
}
|
||||
/* User semantic actions sometimes alter yychar, and that requires
|
||||
@@ -2017,5 +2041,5 @@ yyreturn:
|
||||
#endif
|
||||
return yyresult;
|
||||
}
|
||||
#line 180 "ExpressionParser.y" /* yacc.c:1906 */
|
||||
#line 185 "ExpressionParser.y" /* yacc.c:1906 */
|
||||
|
||||
|
||||
+33
-23
@@ -122,22 +122,32 @@ unit_exp: UNIT { $$ = new UnitExpression(Docume
|
||||
identifier: path { /* Path to property within document object */
|
||||
$$ = ObjectIdentifier(DocumentObject);
|
||||
$$.addComponents($1);
|
||||
$$.resolveAmbiguity();
|
||||
}
|
||||
| '.' path { /* Path to property of the current document object */
|
||||
$$ = ObjectIdentifier(DocumentObject,true);
|
||||
$$.setDocumentObjectName(DocumentObject);
|
||||
$$.addComponents($2);
|
||||
}
|
||||
| object '.' path { /* Path to property within document object */
|
||||
$$ = ObjectIdentifier(DocumentObject);
|
||||
$$.setDocumentObjectName($1, true);
|
||||
$1.checkImport(DocumentObject);
|
||||
$$.addComponent(ObjectIdentifier::SimpleComponent($1));
|
||||
$$.addComponents($3);
|
||||
$$.resolveAmbiguity();
|
||||
}
|
||||
| document '#' path { /* Path to property from an external document, within a named document object */
|
||||
$$ = ObjectIdentifier(DocumentObject);
|
||||
$$.setDocumentName($1, true);
|
||||
$$.setDocumentName(std::move($1), true);
|
||||
$$.addComponents($3);
|
||||
$$.resolveAmbiguity();
|
||||
}
|
||||
| document '#' object '.' path { /* Path to property from an external document, within a named document object */
|
||||
$$ = ObjectIdentifier(DocumentObject);
|
||||
$$.setDocumentName($1, true);
|
||||
$$.setDocumentObjectName($3, true);
|
||||
$$.setDocumentName(std::move($1), true);
|
||||
$$.setDocumentObjectName(std::move($3), true);
|
||||
$$.addComponents($5);
|
||||
$$.resolveAmbiguity();
|
||||
}
|
||||
;
|
||||
|
||||
@@ -146,27 +156,27 @@ integer: INTEGER { $$ = $1; }
|
||||
;
|
||||
|
||||
|
||||
path: IDENTIFIER { $$.push_front(ObjectIdentifier::Component::SimpleComponent($1)); }
|
||||
| CELLADDRESS { $$.push_front(ObjectIdentifier::Component::SimpleComponent($1)); }
|
||||
| IDENTIFIER '[' integer ']' { $$.push_front(ObjectIdentifier::Component::ArrayComponent($1, $3)); }
|
||||
| IDENTIFIER '[' integer ']' '.' subpath { $6.push_front(ObjectIdentifier::Component::ArrayComponent($1, $3)); $$ = $6; }
|
||||
| IDENTIFIER '[' STRING ']' { $$.push_front(ObjectIdentifier::Component::MapComponent($1, ObjectIdentifier::String($3, true))); }
|
||||
| IDENTIFIER '[' IDENTIFIER ']' { $$.push_front(ObjectIdentifier::Component::MapComponent($1, $3)); }
|
||||
| IDENTIFIER '[' STRING ']' '.' subpath { $6.push_front(ObjectIdentifier::Component::MapComponent($1, ObjectIdentifier::String($3, true))); $$ = $6; }
|
||||
| IDENTIFIER '[' IDENTIFIER ']' '.' subpath { $6.push_front(ObjectIdentifier::Component::MapComponent($1, $3)); $$ = $6; }
|
||||
| IDENTIFIER '.' subpath { $3.push_front(ObjectIdentifier::Component::SimpleComponent($1)); $$ = $3; }
|
||||
path: IDENTIFIER { $$.push_front(ObjectIdentifier::SimpleComponent($1)); }
|
||||
| CELLADDRESS { $$.push_front(ObjectIdentifier::SimpleComponent($1)); }
|
||||
| IDENTIFIER '[' integer ']' { $$.push_front(ObjectIdentifier::ArrayComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1); }
|
||||
| IDENTIFIER '[' integer ']' '.' subpath { $6.push_front(ObjectIdentifier::ArrayComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1); $$ = $6; }
|
||||
| IDENTIFIER '[' STRING ']' { $$.push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String($3, true))); }
|
||||
| IDENTIFIER '[' IDENTIFIER ']' { $$.push_front(ObjectIdentifier::MapComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1); }
|
||||
| IDENTIFIER '[' STRING ']' '.' subpath { $6.push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String($3, true))); $$.push_front(ObjectIdentifier::SimpleComponent($1); $$ = $6; }
|
||||
| IDENTIFIER '[' IDENTIFIER ']' '.' subpath { $6.push_front(ObjectIdentifier::MapComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1); $$ = $6; }
|
||||
| IDENTIFIER '.' subpath { $3.push_front(ObjectIdentifier::SimpleComponent($1)); $$ = $3; }
|
||||
;
|
||||
|
||||
subpath: IDENTIFIER { $$.push_front(ObjectIdentifier::Component::SimpleComponent($1)); }
|
||||
| STRING { $$.push_front(ObjectIdentifier::Component::SimpleComponent($1)); }
|
||||
| CELLADDRESS { $$.push_front(ObjectIdentifier::Component::SimpleComponent($1)); }
|
||||
| IDENTIFIER '[' integer ']' { $$.push_front(ObjectIdentifier::Component::ArrayComponent($1, $3)); }
|
||||
| IDENTIFIER '[' integer ']' '.' subpath { $6.push_front(ObjectIdentifier::Component::ArrayComponent($1, $3)); $$ = $6; }
|
||||
| IDENTIFIER '[' STRING ']' { $$.push_front(ObjectIdentifier::Component::MapComponent($1, ObjectIdentifier::String($3, true))); }
|
||||
| IDENTIFIER '[' IDENTIFIER ']' { $$.push_front(ObjectIdentifier::Component::MapComponent($1, $3)); }
|
||||
| IDENTIFIER '[' STRING ']' '.' subpath { $6.push_front(ObjectIdentifier::Component::MapComponent($1, ObjectIdentifier::String($3, true))); $$ = $6; }
|
||||
| IDENTIFIER '[' IDENTIFIER ']' '.' subpath { $6.push_front(ObjectIdentifier::Component::MapComponent($1, $3)); $$ = $6; }
|
||||
| IDENTIFIER '.' subpath { $3.push_front(ObjectIdentifier::Component::SimpleComponent($1)); $$ = $3; }
|
||||
subpath: IDENTIFIER { $$.push_front(ObjectIdentifier::SimpleComponent($1)); }
|
||||
| STRING { $$.push_front(ObjectIdentifier::SimpleComponent($1)); }
|
||||
| CELLADDRESS { $$.push_front(ObjectIdentifier::SimpleComponent($1)); }
|
||||
| IDENTIFIER '[' integer ']' { $$.push_front(ObjectIdentifier::ArrayComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1)); }
|
||||
| IDENTIFIER '[' integer ']' '.' subpath { $6.push_front(ObjectIdentifier::ArrayComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1));$$ = $6; }
|
||||
| IDENTIFIER '[' STRING ']' { $$.push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String($3, true))); $$.push_front(ObjectIdentifier::SimpleComponent($1)); }
|
||||
| IDENTIFIER '[' IDENTIFIER ']' { $$.push_front(ObjectIdentifier::MapComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1)); }
|
||||
| IDENTIFIER '[' STRING ']' '.' subpath { $6.push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String($3, true))); $$.push_front(ObjectIdentifier::SimpleComponent($1));$$ = $6; }
|
||||
| IDENTIFIER '[' IDENTIFIER ']' '.' subpath { $6.push_front(ObjectIdentifier::MapComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1));$$ = $6; }
|
||||
| IDENTIFIER '.' subpath { $3.push_front(ObjectIdentifier::SimpleComponent($1)); $$ = $3; }
|
||||
;
|
||||
|
||||
document: STRING { $$ = ObjectIdentifier::String($1, true); }
|
||||
|
||||
@@ -43,18 +43,8 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void visit(Expression *node) {
|
||||
VariableExpression *expr = Base::freecad_dynamic_cast<VariableExpression>(node);
|
||||
|
||||
if (expr) {
|
||||
const App::ObjectIdentifier & oldPath = expr->getPath().canonicalPath();
|
||||
const std::map<ObjectIdentifier, ObjectIdentifier>::const_iterator it = paths.find(oldPath);
|
||||
|
||||
if (it != paths.end()) {
|
||||
ExpressionModifier<P>::setExpressionChanged();
|
||||
expr->setPath(it->second.relativeTo(owner));
|
||||
}
|
||||
}
|
||||
void visit(Expression &node) {
|
||||
this->renameObjectIdentifier(node,paths,owner);
|
||||
}
|
||||
|
||||
|
||||
@@ -63,64 +53,72 @@ private:
|
||||
const ObjectIdentifier owner; /**< Owner of expression */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The RelabelDocumentObjectExpressionVisitor class is a functor class used to rename variables in an expression.
|
||||
*/
|
||||
|
||||
template<class P> class RelabelDocumentObjectExpressionVisitor : public ExpressionModifier<P> {
|
||||
template<class P> class UpdateElementReferenceExpressionVisitor : public ExpressionModifier<P> {
|
||||
public:
|
||||
|
||||
RelabelDocumentObjectExpressionVisitor(P & _prop, const std::string & _oldName, const std::string & _newName)
|
||||
: ExpressionModifier<P>(_prop)
|
||||
, oldName(_oldName)
|
||||
, newName(_newName)
|
||||
UpdateElementReferenceExpressionVisitor(P & _prop, App::DocumentObject *feature=0, bool reverse=false)
|
||||
: ExpressionModifier<P>(_prop),feature(feature),reverse(reverse)
|
||||
{
|
||||
}
|
||||
|
||||
~RelabelDocumentObjectExpressionVisitor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Visit each node in the expression, and if it is a VariableExpression object, incoke renameDocumentObject in it.
|
||||
* @param node Node to visit
|
||||
*/
|
||||
|
||||
void visit(Expression * node) {
|
||||
VariableExpression *expr = Base::freecad_dynamic_cast<VariableExpression>(node);
|
||||
|
||||
if (expr && expr->validDocumentObjectRename(oldName, newName)) {
|
||||
ExpressionModifier<P>::setExpressionChanged();
|
||||
expr->renameDocumentObject(oldName, newName);
|
||||
}
|
||||
void visit(Expression &node) {
|
||||
this->updateElementReference(node,feature,reverse);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string oldName; /**< Document object name to replace */
|
||||
std::string newName; /**< New document object name */
|
||||
App::DocumentObject *feature;
|
||||
bool reverse;
|
||||
};
|
||||
|
||||
template<class P> class RelabelDocumentExpressionVisitor : public ExpressionModifier<P> {
|
||||
// Document relabel is not undoable, so we don't derive from
|
||||
// ExpressionModifier, hence not calling aboutToSetValue/hasSetValue().
|
||||
// By right, modification of document label should not change evaluation result
|
||||
// of any expression.
|
||||
class RelabelDocumentExpressionVisitor : public ExpressionVisitor {
|
||||
public:
|
||||
|
||||
RelabelDocumentExpressionVisitor(P & prop, const std::string & _oldName, const std::string & _newName)
|
||||
: ExpressionModifier<P>(prop)
|
||||
, oldName(_oldName)
|
||||
, newName(_newName)
|
||||
RelabelDocumentExpressionVisitor(const App::Document &doc)
|
||||
: doc(doc)
|
||||
{
|
||||
}
|
||||
|
||||
void visit(Expression * node) {
|
||||
VariableExpression *expr = Base::freecad_dynamic_cast<VariableExpression>(node);
|
||||
|
||||
if (expr && expr->validDocumentRename(oldName, newName)) {
|
||||
ExpressionModifier<P>::setExpressionChanged();
|
||||
expr->renameDocument(oldName, newName);
|
||||
}
|
||||
void visit(Expression &node) {
|
||||
this->relabeledDocument(node,doc.getOldLabel(),doc.Label.getStrValue());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string oldName;
|
||||
std::string newName;
|
||||
const App::Document &doc;
|
||||
};
|
||||
|
||||
template<class P> class MoveCellsExpressionVisitor : public ExpressionModifier<P> {
|
||||
public:
|
||||
MoveCellsExpressionVisitor(P &prop, const CellAddress &address, int rowCount, int colCount)
|
||||
: ExpressionModifier<P>(prop),address(address),rowCount(rowCount),colCount(colCount)
|
||||
{}
|
||||
|
||||
void visit(Expression &node) {
|
||||
this->moveCells(node,address,rowCount,colCount);
|
||||
}
|
||||
|
||||
private:
|
||||
CellAddress address;
|
||||
int rowCount;
|
||||
int colCount;
|
||||
};
|
||||
|
||||
template<class P> class OffsetCellsExpressionVisitor : public ExpressionModifier<P> {
|
||||
public:
|
||||
OffsetCellsExpressionVisitor(P &prop, int rowOffset, int colOffset)
|
||||
: ExpressionModifier<P>(prop),rowOffset(rowOffset),colOffset(colOffset)
|
||||
{}
|
||||
|
||||
void visit(Expression &node) {
|
||||
this->offsetCells(node,rowOffset,colOffset);
|
||||
}
|
||||
|
||||
private:
|
||||
int rowOffset;
|
||||
int colOffset;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+13
-6
@@ -301,17 +301,24 @@ private:
|
||||
};
|
||||
|
||||
// Property define
|
||||
#define EXTENSION_ADD_PROPERTY(_prop_, _defaultval_) \
|
||||
#define _EXTENSION_ADD_PROPERTY(_name, _prop_, _defaultval_) \
|
||||
do { \
|
||||
this->_prop_.setValue _defaultval_;\
|
||||
propertyData.addProperty(static_cast<App::Extension*>(this), #_prop_, &this->_prop_); \
|
||||
propertyData.addProperty(static_cast<App::Extension*>(this), _name, &this->_prop_); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define EXTENSION_ADD_PROPERTY(_prop_, _defaultval_) \
|
||||
_EXTENSION_ADD_PROPERTY(#_prop_, _prop_, _defaultval_)
|
||||
|
||||
#define _EXTENSION_ADD_PROPERTY_TYPE(_name, _prop_, _defaultval_, _group_,_type_,_Docu_) \
|
||||
do { \
|
||||
this->_prop_.setValue _defaultval_;\
|
||||
propertyData.addProperty(static_cast<App::Extension*>(this), _name, &this->_prop_, (_group_),(_type_),(_Docu_)); \
|
||||
} while (0)
|
||||
|
||||
#define EXTENSION_ADD_PROPERTY_TYPE(_prop_, _defaultval_, _group_,_type_,_Docu_) \
|
||||
do { \
|
||||
this->_prop_.setValue _defaultval_;\
|
||||
propertyData.addProperty(static_cast<App::Extension*>(this), #_prop_, &this->_prop_, (_group_),(_type_),(_Docu_)); \
|
||||
} while (0)
|
||||
_EXTENSION_ADD_PROPERTY_TYPE(#_prop_, _prop_, _defaultval_, _group_,_type_,_Docu_)
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -94,8 +94,8 @@ bool ExtensionContainer::hasExtension(const std::string& name) const {
|
||||
}
|
||||
|
||||
|
||||
Extension* ExtensionContainer::getExtension(Base::Type t, bool derived) const {
|
||||
|
||||
Extension* ExtensionContainer::getExtension(Base::Type t, bool derived, bool no_except) const {
|
||||
|
||||
auto result = _extensions.find(t);
|
||||
if((result == _extensions.end()) && derived) {
|
||||
//we need to check for derived types
|
||||
@@ -103,7 +103,7 @@ Extension* ExtensionContainer::getExtension(Base::Type t, bool derived) const {
|
||||
if(entry.first.isDerivedFrom(t))
|
||||
return entry.second;
|
||||
}
|
||||
|
||||
if(no_except) return 0;
|
||||
//if we arrive here we don't have anything matching
|
||||
throw Base::TypeError("ExtensionContainer::getExtension: No extension of given type available");
|
||||
}
|
||||
@@ -111,6 +111,7 @@ Extension* ExtensionContainer::getExtension(Base::Type t, bool derived) const {
|
||||
return result->second;
|
||||
}
|
||||
else {
|
||||
if(no_except) return 0;
|
||||
//if we arrive here we don't have anything matching
|
||||
throw Base::TypeError("ExtensionContainer::getExtension: No extension of given type available");
|
||||
}
|
||||
|
||||
@@ -127,24 +127,24 @@ public:
|
||||
bool hasExtension(Base::Type, bool derived=true) const; //returns first of type (or derived from if set to true) and throws otherwise
|
||||
bool hasExtension(const std::string& name) const; //this version does not check derived classes
|
||||
bool hasExtensions() const;
|
||||
App::Extension* getExtension(Base::Type, bool derived = true) const;
|
||||
App::Extension* getExtension(Base::Type, bool derived = true, bool no_except=false) const;
|
||||
App::Extension* getExtension(const std::string& name) const; //this version does not check derived classes
|
||||
|
||||
//returns first of type (or derived from) and throws otherwise
|
||||
template<typename ExtensionT>
|
||||
ExtensionT* getExtensionByType() const {
|
||||
return dynamic_cast<ExtensionT*>(getExtension(ExtensionT::getExtensionClassTypeId()));
|
||||
ExtensionT* getExtensionByType(bool no_except=false, bool derived=true) const {
|
||||
return static_cast<ExtensionT*>(getExtension(ExtensionT::getExtensionClassTypeId(),derived,no_except));
|
||||
};
|
||||
|
||||
//get all extensions which have the given base class
|
||||
std::vector<Extension*> getExtensionsDerivedFrom(Base::Type type) const;
|
||||
template<typename ExtensionT>
|
||||
std::vector<ExtensionT*> getExtensionsDerivedFromType() const {
|
||||
auto vec = getExtensionsDerivedFrom(ExtensionT::getExtensionClassTypeId());
|
||||
std::vector<ExtensionT*> typevec;
|
||||
for(auto ext : vec)
|
||||
typevec.push_back(dynamic_cast<ExtensionT*>(ext));
|
||||
|
||||
for(auto entry : _extensions) {
|
||||
if(entry.first.isDerivedFrom(ExtensionT::getExtensionClassTypeId()))
|
||||
typevec.push_back(static_cast<ExtensionT*>(entry.second));
|
||||
}
|
||||
return typevec;
|
||||
};
|
||||
|
||||
@@ -193,7 +193,7 @@ private:
|
||||
};
|
||||
|
||||
#define PROPERTY_HEADER_WITH_EXTENSIONS(_class_) \
|
||||
PROPERTY_HEADER(_class)
|
||||
PROPERTY_HEADER_WITH_OVERRIDE(_class)
|
||||
|
||||
/// We make sur that the PropertyData of the container is not connected to the one of the extension
|
||||
#define PROPERTY_SOURCE_WITH_EXTENSIONS(_class_, _parentclass_) \
|
||||
|
||||
+1
-84
@@ -28,7 +28,6 @@
|
||||
|
||||
#include <Base/Writer.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/DynamicProperty.h>
|
||||
|
||||
namespace App
|
||||
{
|
||||
@@ -50,10 +49,9 @@ class FeatureCustomT : public FeatureT
|
||||
|
||||
public:
|
||||
FeatureCustomT() {
|
||||
props = new DynamicProperty(this);
|
||||
}
|
||||
|
||||
virtual ~FeatureCustomT() {
|
||||
delete props;
|
||||
}
|
||||
|
||||
/** @name methods override DocumentObject */
|
||||
@@ -70,84 +68,6 @@ public:
|
||||
return FeatureT::getViewProviderName();
|
||||
}
|
||||
|
||||
/** @name Access properties */
|
||||
//@{
|
||||
Property* addDynamicProperty(
|
||||
const char* type, const char* name=0,
|
||||
const char* group=0, const char* doc=0,
|
||||
short attr=0, bool ro=false, bool hidden=false) {
|
||||
return props->addDynamicProperty(type, name, group, doc, attr, ro, hidden);
|
||||
}
|
||||
virtual bool removeDynamicProperty(const char* name) {
|
||||
FeatureT::onAboutToRemoveProperty(name);
|
||||
return props->removeDynamicProperty(name);
|
||||
}
|
||||
std::vector<std::string> getDynamicPropertyNames() const {
|
||||
return props->getDynamicPropertyNames();
|
||||
}
|
||||
Property *getDynamicPropertyByName(const char* name) const {
|
||||
return props->getDynamicPropertyByName(name);
|
||||
}
|
||||
virtual void addDynamicProperties(const PropertyContainer* cont) {
|
||||
return props->addDynamicProperties(cont);
|
||||
}
|
||||
/// get all properties of the class (including properties of the parent)
|
||||
virtual void getPropertyList(std::vector<Property*> &List) const {
|
||||
props->getPropertyList(List);
|
||||
}
|
||||
/// get all properties of the class (including parent)
|
||||
void getPropertyMap(std::map<std::string,Property*> &Map) const {
|
||||
props->getPropertyMap(Map);
|
||||
}
|
||||
/// find a property by its name
|
||||
virtual Property *getPropertyByName(const char* name) const {
|
||||
return props->getPropertyByName(name);
|
||||
}
|
||||
/// get the name of a property
|
||||
virtual const char* getPropertyName(const Property* prop) const {
|
||||
return props->getPropertyName(prop);
|
||||
}
|
||||
//@}
|
||||
|
||||
/** @name Property attributes */
|
||||
//@{
|
||||
/// get the Type of a Property
|
||||
short getPropertyType(const Property* prop) const {
|
||||
return props->getPropertyType(prop);
|
||||
}
|
||||
/// get the Type of a named Property
|
||||
short getPropertyType(const char *name) const {
|
||||
return props->getPropertyType(name);
|
||||
}
|
||||
/// get the Group of a Property
|
||||
const char* getPropertyGroup(const Property* prop) const {
|
||||
return props->getPropertyGroup(prop);
|
||||
}
|
||||
/// get the Group of a named Property
|
||||
const char* getPropertyGroup(const char *name) const {
|
||||
return props->getPropertyGroup(name);
|
||||
}
|
||||
/// get the Documentation of a Property
|
||||
const char* getPropertyDocumentation(const Property* prop) const {
|
||||
return props->getPropertyDocumentation(prop);
|
||||
}
|
||||
/// get the Group of a named Property
|
||||
const char* getPropertyDocumentation(const char *name) const {
|
||||
return props->getPropertyDocumentation(name);
|
||||
}
|
||||
//@}
|
||||
|
||||
/** @name Property serialization */
|
||||
//@{
|
||||
void Save (Base::Writer &writer) const {
|
||||
writer.ObjectName = this->getNameInDocument();
|
||||
props->Save(writer);
|
||||
}
|
||||
void Restore(Base::XMLReader &reader) {
|
||||
props->Restore(reader);
|
||||
}
|
||||
//@}
|
||||
|
||||
PyObject *getPyObject(void) {
|
||||
return FeatureT::getPyObject();
|
||||
}
|
||||
@@ -168,9 +88,6 @@ protected:
|
||||
virtual void onSettingDocument() {
|
||||
FeatureT::onSettingDocument();
|
||||
}
|
||||
|
||||
private:
|
||||
DynamicProperty* props;
|
||||
};
|
||||
|
||||
} //namespace App
|
||||
|
||||
+391
-96
@@ -30,15 +30,16 @@
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <Base/Reader.h>
|
||||
#include <Base/MatrixPy.h>
|
||||
#include <Base/Tools.h>
|
||||
|
||||
#include <App/DocumentObjectPy.h>
|
||||
#include "FeaturePython.h"
|
||||
#include "FeaturePythonPyImp.h"
|
||||
|
||||
using namespace App;
|
||||
|
||||
FeaturePythonImp::FeaturePythonImp(App::DocumentObject* o) : object(o)
|
||||
FeaturePythonImp::FeaturePythonImp(App::DocumentObject* o)
|
||||
: object(o), has__object__(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -46,43 +47,40 @@ FeaturePythonImp::~FeaturePythonImp()
|
||||
{
|
||||
}
|
||||
|
||||
void FeaturePythonImp::init(PyObject *pyobj) {
|
||||
Base::PyGILStateLocker lock;
|
||||
has__object__ = !!PyObject_HasAttrString(pyobj, "__object__");
|
||||
|
||||
#undef FC_PY_ELEMENT
|
||||
#define FC_PY_ELEMENT(_name) FC_PY_ELEMENT_INIT(_name)
|
||||
|
||||
FC_PY_FEATURE_PYTHON
|
||||
}
|
||||
|
||||
#define FC_PY_CALL_CHECK(_name) _FC_PY_CALL_CHECK(_name,return(false))
|
||||
|
||||
/*!
|
||||
Calls the execute() method of the Python feature class. If the Python feature class doesn't have an execute()
|
||||
method or if it returns False this method also return false and true otherwise.
|
||||
*/
|
||||
bool FeaturePythonImp::execute()
|
||||
{
|
||||
// avoid recursive calls of execute()
|
||||
if (object->testStatus(App::PythonCall))
|
||||
return false;
|
||||
|
||||
// Run the execute method of the proxy object.
|
||||
FC_PY_CALL_CHECK(execute)
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Property* proxy = object->getPropertyByName("Proxy");
|
||||
if (proxy && proxy->getTypeId() == PropertyPythonObject::getClassTypeId()) {
|
||||
Py::Object feature = static_cast<PropertyPythonObject*>(proxy)->getValue();
|
||||
if (feature.hasAttr(std::string("execute"))) {
|
||||
if (feature.hasAttr("__object__")) {
|
||||
Base::ObjectStatusLocker<ObjectStatus, DocumentObject> exe(App::PythonCall, object);
|
||||
Py::Callable method(feature.getAttr(std::string("execute")));
|
||||
Py::Tuple args;
|
||||
Py::Object res = method.apply(args);
|
||||
if (res.isBoolean() && !res.isTrue())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Base::ObjectStatusLocker<ObjectStatus, DocumentObject> exe(App::PythonCall, object);
|
||||
Py::Callable method(feature.getAttr(std::string("execute")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Py::Object res = method.apply(args);
|
||||
if (res.isBoolean() && !res.isTrue())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (has__object__) {
|
||||
Py::Object res = Base::pyCall(py_execute.ptr());
|
||||
if (res.isBoolean() && !res.isTrue())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Py::Object res = Base::pyCall(py_execute.ptr(),args.ptr());
|
||||
if (res.isBoolean() && !res.isTrue())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -96,35 +94,51 @@ bool FeaturePythonImp::execute()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FeaturePythonImp::mustExecute() const
|
||||
{
|
||||
FC_PY_CALL_CHECK(mustExecute)
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (has__object__) {
|
||||
Py::Object res(Base::pyCall(py_mustExecute.ptr()));
|
||||
return res.isTrue();
|
||||
}
|
||||
else {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Py::Object res(Base::pyCall(py_mustExecute.ptr(),args.ptr()));
|
||||
return res.isTrue();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void FeaturePythonImp::onBeforeChange(const Property* prop)
|
||||
{
|
||||
if(py_onBeforeChange.isNone())
|
||||
return;
|
||||
|
||||
// Run the execute method of the proxy object.
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Property* proxy = object->getPropertyByName("Proxy");
|
||||
if (proxy && proxy->getTypeId() == PropertyPythonObject::getClassTypeId()) {
|
||||
Py::Object feature = static_cast<PropertyPythonObject*>(proxy)->getValue();
|
||||
if (feature.hasAttr(std::string("onBeforeChange"))) {
|
||||
if (feature.hasAttr("__object__")) {
|
||||
Py::Callable method(feature.getAttr(std::string("onBeforeChange")));
|
||||
Py::Tuple args(1);
|
||||
const char* prop_name = object->getPropertyName(prop);
|
||||
if (prop_name) {
|
||||
args.setItem(0, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Py::Callable method(feature.getAttr(std::string("onBeforeChange")));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
const char* prop_name = object->getPropertyName(prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
const char *prop_name = object->getPropertyName(prop);
|
||||
if(prop_name == 0)
|
||||
return;
|
||||
if (has__object__) {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::String(prop_name));
|
||||
Base::pyCall(py_onBeforeChange.ptr(),args.ptr());
|
||||
}
|
||||
else {
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
Base::pyCall(py_onBeforeChange.ptr(),args.ptr());
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -133,35 +147,52 @@ void FeaturePythonImp::onBeforeChange(const Property* prop)
|
||||
}
|
||||
}
|
||||
|
||||
void FeaturePythonImp::onChanged(const Property* prop)
|
||||
bool FeaturePythonImp::onBeforeChangeLabel(std::string &newLabel)
|
||||
{
|
||||
if(py_onBeforeChangeLabel.isNone())
|
||||
return false;
|
||||
|
||||
// Run the execute method of the proxy object.
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Property* proxy = object->getPropertyByName("Proxy");
|
||||
if (proxy && proxy->getTypeId() == PropertyPythonObject::getClassTypeId()) {
|
||||
Py::Object feature = static_cast<PropertyPythonObject*>(proxy)->getValue();
|
||||
if (feature.hasAttr(std::string("onChanged"))) {
|
||||
if (feature.hasAttr("__object__")) {
|
||||
Py::Callable method(feature.getAttr(std::string("onChanged")));
|
||||
Py::Tuple args(1);
|
||||
const char* prop_name = object->getPropertyName(prop);
|
||||
if (prop_name) {
|
||||
args.setItem(0, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Py::Callable method(feature.getAttr(std::string("onChanged")));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
const char* prop_name = object->getPropertyName(prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1,Py::String(newLabel));
|
||||
Py::Object ret(Base::pyCall(py_onBeforeChangeLabel.ptr(),args.ptr()));
|
||||
if(!ret.isNone()) {
|
||||
if(!ret.isString())
|
||||
throw Base::TypeError("onBeforeChangeLabel expects to return a string");
|
||||
newLabel = ret.as_string();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FeaturePythonImp::onChanged(const Property* prop)
|
||||
{
|
||||
if(py_onChanged.isNone())
|
||||
return;
|
||||
// Run the execute method of the proxy object.
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
const char *prop_name = object->getPropertyName(prop);
|
||||
if(prop_name == 0)
|
||||
return;
|
||||
if (has__object__) {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::String(prop_name));
|
||||
Base::pyCall(py_onChanged.ptr(),args.ptr());
|
||||
}
|
||||
else {
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
Base::pyCall(py_onChanged.ptr(),args.ptr());
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -172,25 +203,18 @@ void FeaturePythonImp::onChanged(const Property* prop)
|
||||
|
||||
void FeaturePythonImp::onDocumentRestored()
|
||||
{
|
||||
_FC_PY_CALL_CHECK(onDocumentRestored,return);
|
||||
|
||||
// Run the execute method of the proxy object.
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Property* proxy = object->getPropertyByName("Proxy");
|
||||
if (proxy && proxy->getTypeId() == PropertyPythonObject::getClassTypeId()) {
|
||||
Py::Object feature = static_cast<PropertyPythonObject*>(proxy)->getValue();
|
||||
if (feature.hasAttr(std::string("onDocumentRestored"))) {
|
||||
if (feature.hasAttr("__object__")) {
|
||||
Py::Callable method(feature.getAttr(std::string("onDocumentRestored")));
|
||||
Py::Tuple args;
|
||||
method.apply(args);
|
||||
}
|
||||
else {
|
||||
Py::Callable method(feature.getAttr(std::string("onDocumentRestored")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
method.apply(args);
|
||||
}
|
||||
}
|
||||
if (has__object__) {
|
||||
Base::pyCall(py_onDocumentRestored.ptr());
|
||||
}
|
||||
else {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Base::pyCall(py_onDocumentRestored.ptr(),args.ptr());
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -199,12 +223,283 @@ void FeaturePythonImp::onDocumentRestored()
|
||||
}
|
||||
}
|
||||
|
||||
bool FeaturePythonImp::getSubObject(DocumentObject *&ret, const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *_mat, bool transform, int depth) const
|
||||
{
|
||||
FC_PY_CALL_CHECK(getSubObject);
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(6);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
if(!subname) subname = "";
|
||||
args.setItem(1,Py::String(subname));
|
||||
args.setItem(2,Py::Int(pyObj?2:1));
|
||||
Base::MatrixPy *pyMat = new Base::MatrixPy(new Base::Matrix4D);
|
||||
if(_mat) *pyMat->getMatrixPtr() = *_mat;
|
||||
args.setItem(3,Py::Object(pyMat));
|
||||
args.setItem(4,Py::Boolean(transform));
|
||||
args.setItem(5,Py::Int(depth));
|
||||
|
||||
Py::Object res(Base::pyCall(py_getSubObject.ptr(),args.ptr()));
|
||||
if(res.isNone()) {
|
||||
ret = 0;
|
||||
return true;
|
||||
}
|
||||
if(!res.isTrue())
|
||||
return false;
|
||||
if(!res.isSequence())
|
||||
throw Base::TypeError("getSubObject expects return type of tuple");
|
||||
Py::Sequence seq(res);
|
||||
if(seq.length() < 2 ||
|
||||
(!seq.getItem(0).isNone() &&
|
||||
!PyObject_TypeCheck(seq.getItem(0).ptr(),&DocumentObjectPy::Type)) ||
|
||||
!PyObject_TypeCheck(seq.getItem(1).ptr(),&Base::MatrixPy::Type))
|
||||
{
|
||||
throw Base::TypeError("getSubObject expects return type of (obj,matrix,pyobj)");
|
||||
}
|
||||
if(_mat)
|
||||
*_mat = *static_cast<Base::MatrixPy*>(seq.getItem(1).ptr())->getMatrixPtr();
|
||||
if(pyObj) {
|
||||
if(seq.length()>2)
|
||||
*pyObj = Py::new_reference_to(seq.getItem(2));
|
||||
else
|
||||
*pyObj = Py::new_reference_to(Py::None());
|
||||
}
|
||||
if(seq.getItem(0).isNone())
|
||||
ret = 0;
|
||||
else
|
||||
ret = static_cast<DocumentObjectPy*>(seq.getItem(0).ptr())->getDocumentObjectPtr();
|
||||
return true;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
ret = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool FeaturePythonImp::getSubObjects(std::vector<std::string> &ret, int reason) const {
|
||||
FC_PY_CALL_CHECK(getSubObjects);
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1, Py::Int(reason));
|
||||
Py::Object res(Base::pyCall(py_getSubObjects.ptr(),args.ptr()));
|
||||
if(!res.isTrue())
|
||||
return true;
|
||||
if(!res.isSequence())
|
||||
throw Base::TypeError("getSubObjects expects return type of tuple");
|
||||
Py::Sequence seq(res);
|
||||
for(size_t i=0;i<seq.length();++i) {
|
||||
Py::Object name(seq[i].ptr());
|
||||
if(!name.isString())
|
||||
throw Base::TypeError("getSubObjects expects string in returned sequence");
|
||||
ret.push_back(name.as_string());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool FeaturePythonImp::getLinkedObject(DocumentObject *&ret, bool recurse,
|
||||
Base::Matrix4D *_mat, bool transform, int depth) const
|
||||
{
|
||||
FC_PY_CALL_CHECK(getLinkedObject);
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(5);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1,Py::Boolean(recurse));
|
||||
Base::MatrixPy *pyMat = new Base::MatrixPy(new Base::Matrix4D);
|
||||
if(_mat) *pyMat->getMatrixPtr() = *_mat;
|
||||
args.setItem(2,Py::Object(pyMat));
|
||||
args.setItem(3,Py::Boolean(transform));
|
||||
args.setItem(4,Py::Int(depth));
|
||||
|
||||
Py::Object res(Base::pyCall(py_getLinkedObject.ptr(),args.ptr()));
|
||||
if(!res.isTrue()) {
|
||||
ret = object;
|
||||
return true;
|
||||
}
|
||||
if(!res.isSequence())
|
||||
throw Base::TypeError("getLinkedObject expects return type of (object,matrix)");
|
||||
Py::Sequence seq(res);
|
||||
if(seq.length() != 2 ||
|
||||
(!seq.getItem(0).isNone() &&
|
||||
!PyObject_TypeCheck(seq.getItem(0).ptr(),&DocumentObjectPy::Type)) ||
|
||||
!PyObject_TypeCheck(seq.getItem(1).ptr(),&Base::MatrixPy::Type))
|
||||
{
|
||||
throw Base::TypeError("getLinkedObject expects return type of (object,matrix)");
|
||||
}
|
||||
if(_mat)
|
||||
*_mat = *static_cast<Base::MatrixPy*>(seq.getItem(1).ptr())->getMatrixPtr();
|
||||
if(seq.getItem(0).isNone())
|
||||
ret = object;
|
||||
else
|
||||
ret = static_cast<DocumentObjectPy*>(seq.getItem(0).ptr())->getDocumentObjectPtr();
|
||||
return true;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
ret = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject *FeaturePythonImp::getPyObject(void)
|
||||
{
|
||||
// ref counter is set to 1
|
||||
return new FeaturePythonPyT<DocumentObjectPy>(object);
|
||||
}
|
||||
|
||||
int FeaturePythonImp::hasChildElement() const {
|
||||
_FC_PY_CALL_CHECK(hasChildElement,return(-1));
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Py::Boolean ok(Base::pyCall(py_hasChildElement.ptr(),args.ptr()));
|
||||
return static_cast<bool>(ok) ? 1 : 0;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int FeaturePythonImp::isElementVisible(const char *element) const {
|
||||
_FC_PY_CALL_CHECK(isElementVisible,return(-2));
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1,Py::String(element?element:""));
|
||||
return Py::Int(Base::pyCall(py_isElementVisible.ptr(),args.ptr()));
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
int FeaturePythonImp::setElementVisible(const char *element, bool visible) {
|
||||
_FC_PY_CALL_CHECK(setElementVisible,return(-2));
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(3);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1,Py::String(element?element:""));
|
||||
args.setItem(2,Py::Boolean(visible));
|
||||
return Py::Int(Base::pyCall(py_setElementVisible.ptr(),args.ptr()));
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
||||
std::string FeaturePythonImp::getViewProviderName()
|
||||
{
|
||||
_FC_PY_CALL_CHECK(getViewProviderName,return(std::string()));
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::TupleN args(Py::Object(object->getPyObject(), true));
|
||||
Py::String ret(Base::pyCall(py_getViewProviderName.ptr(),args.ptr()));
|
||||
return ret.as_string();
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
int FeaturePythonImp::canLinkProperties() const {
|
||||
_FC_PY_CALL_CHECK(canLinkProperties,return(-1));
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Py::Boolean ok(Base::pyCall(py_canLinkProperties.ptr(),args.ptr()));
|
||||
return ok?1:0;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int FeaturePythonImp::allowDuplicateLabel() const {
|
||||
_FC_PY_CALL_CHECK(allowDuplicateLabel,return(-1));
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Py::Boolean ok(Base::pyCall(py_allowDuplicateLabel.ptr(),args.ptr()));
|
||||
return ok?1:0;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int FeaturePythonImp::canLoadPartial() const {
|
||||
_FC_PY_CALL_CHECK(canLoadPartial,return(-1));
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Py::Int ret(Base::pyCall(py_canLoadPartial.ptr(),args.ptr()));
|
||||
return ret;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool FeaturePythonImp::redirectSubName(std::ostringstream &ss,
|
||||
App::DocumentObject *topParent, App::DocumentObject *child) const
|
||||
{
|
||||
FC_PY_CALL_CHECK(redirectSubName);
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(4);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1,Py::String(ss.str()));
|
||||
args.setItem(2,topParent?Py::Object(topParent->getPyObject(),true):Py::Object());
|
||||
args.setItem(3,child?Py::Object(child->getPyObject(),true):Py::Object());
|
||||
Py::Object ret(Base::pyCall(py_redirectSubName.ptr(),args.ptr()));
|
||||
if(ret.isNone())
|
||||
return false;
|
||||
ss.str("");
|
||||
ss << ret.as_string();
|
||||
return true;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
|
||||
namespace App {
|
||||
|
||||
+190
-89
@@ -46,13 +46,109 @@ public:
|
||||
~FeaturePythonImp();
|
||||
|
||||
bool execute();
|
||||
bool mustExecute() const;
|
||||
void onBeforeChange(const Property* prop);
|
||||
bool onBeforeChangeLabel(std::string &newLabel);
|
||||
void onChanged(const Property* prop);
|
||||
void onDocumentRestored();
|
||||
std::string getViewProviderName();
|
||||
PyObject *getPyObject(void);
|
||||
|
||||
bool getSubObject(App::DocumentObject *&ret, const char *subname, PyObject **pyObj,
|
||||
Base::Matrix4D *mat, bool transform, int depth) const;
|
||||
|
||||
bool getSubObjects(std::vector<std::string> &ret, int reason) const;
|
||||
|
||||
bool getLinkedObject(App::DocumentObject *&ret, bool recurse,
|
||||
Base::Matrix4D *mat, bool transform, int depth) const;
|
||||
|
||||
int canLinkProperties() const;
|
||||
|
||||
int allowDuplicateLabel() const;
|
||||
|
||||
bool redirectSubName(std::ostringstream &ss,
|
||||
App::DocumentObject *topParent, App::DocumentObject *child) const;
|
||||
|
||||
int canLoadPartial() const;
|
||||
|
||||
/// return true to activate tree view group object handling
|
||||
int hasChildElement() const;
|
||||
/// Get sub-element visibility
|
||||
int isElementVisible(const char *) const;
|
||||
/// Set sub-element visibility
|
||||
int setElementVisible(const char *, bool);
|
||||
|
||||
private:
|
||||
App::DocumentObject* object;
|
||||
bool has__object__;
|
||||
|
||||
#define FC_PY_FEATURE_PYTHON \
|
||||
FC_PY_ELEMENT(execute)\
|
||||
FC_PY_ELEMENT(mustExecute)\
|
||||
FC_PY_ELEMENT(onBeforeChange)\
|
||||
FC_PY_ELEMENT(onBeforeChangeLabel)\
|
||||
FC_PY_ELEMENT(onChanged)\
|
||||
FC_PY_ELEMENT(onDocumentRestored)\
|
||||
FC_PY_ELEMENT(getViewProviderName)\
|
||||
FC_PY_ELEMENT(getSubObject)\
|
||||
FC_PY_ELEMENT(getSubObjects)\
|
||||
FC_PY_ELEMENT(getLinkedObject)\
|
||||
FC_PY_ELEMENT(canLinkProperties)\
|
||||
FC_PY_ELEMENT(allowDuplicateLabel)\
|
||||
FC_PY_ELEMENT(redirectSubName)\
|
||||
FC_PY_ELEMENT(canLoadPartial)\
|
||||
FC_PY_ELEMENT(hasChildElement)\
|
||||
FC_PY_ELEMENT(isElementVisible)\
|
||||
FC_PY_ELEMENT(setElementVisible)
|
||||
|
||||
#define FC_PY_ELEMENT_DEFINE(_name) \
|
||||
Py::Object py_##_name;
|
||||
|
||||
#define FC_PY_ELEMENT_INIT(_name) \
|
||||
FC_PY_GetCallable(pyobj,#_name,py_##_name);\
|
||||
if(!py_##_name.isNone()) {\
|
||||
PyObject *pyRecursive = PyObject_GetAttrString(pyobj, \
|
||||
"__allow_recursive_" #_name);\
|
||||
if(!pyRecursive) {\
|
||||
PyErr_Clear();\
|
||||
_Flags.set(FlagAllowRecursive_##_name, false);\
|
||||
}else{\
|
||||
_Flags.set(FlagAllowRecursive_##_name, PyObject_IsTrue(pyRecursive));\
|
||||
Py_DECREF(pyRecursive);\
|
||||
}\
|
||||
}
|
||||
|
||||
#define FC_PY_ELEMENT_FLAG(_name) \
|
||||
FlagCalling_##_name,\
|
||||
FlagAllowRecursive_##_name,
|
||||
|
||||
#define _FC_PY_CALL_CHECK(_name,_ret) \
|
||||
if((!_Flags.test(FlagAllowRecursive_##_name) \
|
||||
&& _Flags.test(FlagCalling_##_name)) \
|
||||
|| py_##_name.isNone()) \
|
||||
{\
|
||||
_ret;\
|
||||
}\
|
||||
Base::BitsetLocker<Flags> guard(_Flags, FlagCalling_##_name);
|
||||
|
||||
#undef FC_PY_ELEMENT
|
||||
#define FC_PY_ELEMENT(_name) FC_PY_ELEMENT_DEFINE(_name)
|
||||
|
||||
FC_PY_FEATURE_PYTHON
|
||||
|
||||
#undef FC_PY_ELEMENT
|
||||
#define FC_PY_ELEMENT(_name) FC_PY_ELEMENT_FLAG(_name)
|
||||
|
||||
enum Flag {
|
||||
FC_PY_FEATURE_PYTHON
|
||||
FlagMax,
|
||||
};
|
||||
|
||||
typedef std::bitset<FlagMax> Flags;
|
||||
mutable Flags _Flags;
|
||||
|
||||
public:
|
||||
void init(PyObject *pyobj);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -63,29 +159,29 @@ private:
|
||||
template <class FeatureT>
|
||||
class FeaturePythonT : public FeatureT
|
||||
{
|
||||
PROPERTY_HEADER(App::FeaturePythonT<FeatureT>);
|
||||
PROPERTY_HEADER_WITH_OVERRIDE(App::FeaturePythonT<FeatureT>);
|
||||
|
||||
public:
|
||||
FeaturePythonT() {
|
||||
ADD_PROPERTY(Proxy,(Py::Object()));
|
||||
// cannot move this to the initializer list to avoid warning
|
||||
imp = new FeaturePythonImp(this);
|
||||
props = new DynamicProperty(this);
|
||||
}
|
||||
virtual ~FeaturePythonT() {
|
||||
delete imp;
|
||||
delete props;
|
||||
}
|
||||
|
||||
/** @name methods override DocumentObject */
|
||||
//@{
|
||||
short mustExecute() const {
|
||||
short mustExecute() const override {
|
||||
if (this->isTouched())
|
||||
return 1;
|
||||
return FeatureT::mustExecute();
|
||||
auto ret = FeatureT::mustExecute();
|
||||
if(ret) return ret;
|
||||
return imp->mustExecute()?1:0;
|
||||
}
|
||||
/// recalculate the Feature
|
||||
virtual DocumentObjectExecReturn *execute(void) {
|
||||
virtual DocumentObjectExecReturn *execute(void) override {
|
||||
try {
|
||||
bool handled = imp->execute();
|
||||
if (!handled)
|
||||
@@ -96,102 +192,101 @@ public:
|
||||
}
|
||||
return DocumentObject::StdReturn;
|
||||
}
|
||||
virtual const char* getViewProviderNameOverride(void) const override{
|
||||
viewProviderName = imp->getViewProviderName();
|
||||
if(viewProviderName.size())
|
||||
return viewProviderName.c_str();
|
||||
return FeatureT::getViewProviderNameOverride();
|
||||
}
|
||||
/// returns the type name of the ViewProvider
|
||||
virtual const char* getViewProviderName(void) const {
|
||||
virtual const char* getViewProviderName(void) const override {
|
||||
return FeatureT::getViewProviderName();
|
||||
//return "Gui::ViewProviderPythonFeature";
|
||||
}
|
||||
|
||||
/** @name Access properties */
|
||||
//@{
|
||||
Property* addDynamicProperty(
|
||||
const char* type, const char* name=0,
|
||||
const char* group=0, const char* doc=0,
|
||||
short attr=0, bool ro=false, bool hidden=false) {
|
||||
return props->addDynamicProperty(type, name, group, doc, attr, ro, hidden);
|
||||
virtual App::DocumentObject *getSubObject(const char *subname, PyObject **pyObj,
|
||||
Base::Matrix4D *mat, bool transform, int depth) const override
|
||||
{
|
||||
App::DocumentObject *ret = 0;
|
||||
if(imp->getSubObject(ret,subname,pyObj,mat,transform,depth))
|
||||
return ret;
|
||||
return FeatureT::getSubObject(subname,pyObj,mat,transform,depth);
|
||||
}
|
||||
virtual bool removeDynamicProperty(const char* name) {
|
||||
FeatureT::onAboutToRemoveProperty(name);
|
||||
return props->removeDynamicProperty(name);
|
||||
}
|
||||
std::vector<std::string> getDynamicPropertyNames() const {
|
||||
return props->getDynamicPropertyNames();
|
||||
}
|
||||
Property *getDynamicPropertyByName(const char* name) const {
|
||||
return props->getDynamicPropertyByName(name);
|
||||
}
|
||||
virtual void addDynamicProperties(const PropertyContainer* cont) {
|
||||
return props->addDynamicProperties(cont);
|
||||
}
|
||||
/// get all properties of the class (including properties of the parent)
|
||||
virtual void getPropertyList(std::vector<Property*> &List) const {
|
||||
props->getPropertyList(List);
|
||||
}
|
||||
/// get all properties of the class (including parent)
|
||||
void getPropertyMap(std::map<std::string,Property*> &Map) const {
|
||||
props->getPropertyMap(Map);
|
||||
}
|
||||
/// find a property by its name
|
||||
virtual Property *getPropertyByName(const char* name) const {
|
||||
return props->getPropertyByName(name);
|
||||
}
|
||||
/// get the name of a property
|
||||
virtual const char* getPropertyName(const Property* prop) const {
|
||||
return props->getPropertyName(prop);
|
||||
}
|
||||
//@}
|
||||
|
||||
/** @name Property attributes */
|
||||
//@{
|
||||
/// get the Type of a Property
|
||||
short getPropertyType(const Property* prop) const {
|
||||
return props->getPropertyType(prop);
|
||||
virtual std::vector<std::string> getSubObjects(int reason=0) const override {
|
||||
std::vector<std::string> ret;
|
||||
if(imp->getSubObjects(ret,reason))
|
||||
return ret;
|
||||
return FeatureT::getSubObjects(reason);
|
||||
}
|
||||
/// get the Type of a named Property
|
||||
short getPropertyType(const char *name) const {
|
||||
return props->getPropertyType(name);
|
||||
}
|
||||
/// get the Group of a Property
|
||||
const char* getPropertyGroup(const Property* prop) const {
|
||||
return props->getPropertyGroup(prop);
|
||||
}
|
||||
/// get the Group of a named Property
|
||||
const char* getPropertyGroup(const char *name) const {
|
||||
return props->getPropertyGroup(name);
|
||||
}
|
||||
/// get the Documentation of a Property
|
||||
const char* getPropertyDocumentation(const Property* prop) const {
|
||||
return props->getPropertyDocumentation(prop);
|
||||
}
|
||||
/// get the Group of a named Property
|
||||
const char* getPropertyDocumentation(const char *name) const {
|
||||
return props->getPropertyDocumentation(name);
|
||||
}
|
||||
//@}
|
||||
|
||||
/** @name Property serialization */
|
||||
//@{
|
||||
void Save (Base::Writer &writer) const {
|
||||
const char* objname = this->getNameInDocument();
|
||||
// if null then it's not part of the document
|
||||
if (objname) {
|
||||
writer.ObjectName = objname;
|
||||
props->Save(writer);
|
||||
}
|
||||
virtual App::DocumentObject *getLinkedObject(bool recurse,
|
||||
Base::Matrix4D *mat, bool transform, int depth) const override
|
||||
{
|
||||
App::DocumentObject *ret = 0;
|
||||
if(imp->getLinkedObject(ret,recurse,mat,transform,depth))
|
||||
return ret;
|
||||
return FeatureT::getLinkedObject(recurse,mat,transform,depth);
|
||||
}
|
||||
void Restore(Base::XMLReader &reader) {
|
||||
props->Restore(reader);
|
||||
}
|
||||
//@}
|
||||
|
||||
PyObject *getPyObject(void) {
|
||||
/// return true to activate tree view group object handling
|
||||
virtual bool hasChildElement() const override {
|
||||
int ret = imp->hasChildElement();
|
||||
if(ret<0)
|
||||
return FeatureT::hasChildElement();
|
||||
return ret?true:false;
|
||||
}
|
||||
/// Get sub-element visibility
|
||||
virtual int isElementVisible(const char *element) const override {
|
||||
int ret = imp->isElementVisible(element);
|
||||
if(ret == -2)
|
||||
return FeatureT::isElementVisible(element);
|
||||
return ret;
|
||||
}
|
||||
/// Set sub-element visibility
|
||||
virtual int setElementVisible(const char *element, bool visible) override {
|
||||
int ret = imp->setElementVisible(element,visible);
|
||||
if(ret == -2)
|
||||
return FeatureT::setElementVisible(element,visible);
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual bool canLinkProperties() const override {
|
||||
int ret = imp->canLinkProperties();
|
||||
if(ret < 0)
|
||||
return FeatureT::canLinkProperties();
|
||||
return ret?true:false;
|
||||
}
|
||||
|
||||
virtual bool allowDuplicateLabel() const override {
|
||||
int ret = imp->allowDuplicateLabel();
|
||||
if(ret < 0)
|
||||
return FeatureT::allowDuplicateLabel();
|
||||
return ret?true:false;
|
||||
}
|
||||
|
||||
virtual bool redirectSubName(std::ostringstream &ss,
|
||||
App::DocumentObject *topParent, App::DocumentObject *child) const override
|
||||
{
|
||||
return imp->redirectSubName(ss,topParent,child) ||
|
||||
FeatureT::redirectSubName(ss,topParent,child);
|
||||
}
|
||||
|
||||
virtual int canLoadPartial() const override {
|
||||
int ret = imp->canLoadPartial();
|
||||
if(ret>=0)
|
||||
return ret;
|
||||
return FeatureT::canLoadPartial();
|
||||
}
|
||||
|
||||
PyObject *getPyObject(void) override {
|
||||
if (FeatureT::PythonObject.is(Py::_None())) {
|
||||
// ref counter is set to 1
|
||||
FeatureT::PythonObject = Py::Object(imp->getPyObject(),true);
|
||||
}
|
||||
return Py::new_reference_to(FeatureT::PythonObject);
|
||||
}
|
||||
void setPyObject(PyObject *obj) {
|
||||
void setPyObject(PyObject *obj) override {
|
||||
if (obj)
|
||||
FeatureT::PythonObject = obj;
|
||||
else
|
||||
@@ -199,23 +294,29 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void onBeforeChange(const Property* prop) {
|
||||
virtual void onBeforeChange(const Property* prop) override {
|
||||
FeatureT::onBeforeChange(prop);
|
||||
imp->onBeforeChange(prop);
|
||||
}
|
||||
virtual void onChanged(const Property* prop) {
|
||||
virtual void onBeforeChangeLabel(std::string &newLabel) override{
|
||||
if(!imp->onBeforeChangeLabel(newLabel))
|
||||
FeatureT::onBeforeChangeLabel(newLabel);
|
||||
}
|
||||
virtual void onChanged(const Property* prop) override {
|
||||
if(prop == &Proxy)
|
||||
imp->init(Proxy.getValue().ptr());
|
||||
imp->onChanged(prop);
|
||||
FeatureT::onChanged(prop);
|
||||
}
|
||||
virtual void onDocumentRestored() {
|
||||
virtual void onDocumentRestored() override {
|
||||
imp->onDocumentRestored();
|
||||
FeatureT::onDocumentRestored();
|
||||
}
|
||||
|
||||
private:
|
||||
FeaturePythonImp* imp;
|
||||
DynamicProperty* props;
|
||||
PropertyPythonObject Proxy;
|
||||
mutable std::string viewProviderName;
|
||||
};
|
||||
|
||||
// Special Feature-Python classes
|
||||
|
||||
+403
-24
@@ -50,11 +50,6 @@ FreeCAD._importFromFreeCAD = removeFromPath
|
||||
|
||||
|
||||
def InitApplications():
|
||||
try:
|
||||
import sys,os,traceback,io
|
||||
except ImportError:
|
||||
FreeCAD.Console.PrintError("\n\nSeems the python standard libs are not installed, bailing out!\n\n")
|
||||
raise
|
||||
# Checking on FreeCAD module path ++++++++++++++++++++++++++++++++++++++++++
|
||||
ModDir = FreeCAD.getHomePath()+'Mod'
|
||||
ModDir = os.path.realpath(ModDir)
|
||||
@@ -62,10 +57,22 @@ def InitApplications():
|
||||
ExtDir = os.path.realpath(ExtDir)
|
||||
BinDir = FreeCAD.getHomePath()+'bin'
|
||||
BinDir = os.path.realpath(BinDir)
|
||||
libpaths = []
|
||||
LibDir = FreeCAD.getHomePath()+'lib'
|
||||
LibDir = os.path.realpath(LibDir)
|
||||
if os.path.exists(LibDir):
|
||||
libpaths.append(LibDir)
|
||||
Lib64Dir = FreeCAD.getHomePath()+'lib64'
|
||||
Lib64Dir = os.path.realpath(Lib64Dir)
|
||||
if os.path.exists(Lib64Dir):
|
||||
libpaths.append(Lib64Dir)
|
||||
if sys.version_info[0] == 3:
|
||||
LibPyDir = FreeCAD.getHomePath()+'lib-py3'
|
||||
else:
|
||||
LibPyDir = FreeCAD.getHomePath()+'lib-py2'
|
||||
LibPyDir = os.path.realpath(LibPyDir)
|
||||
if (os.path.exists(LibPyDir)):
|
||||
libpaths.append(LibPyDir)
|
||||
AddPath = FreeCAD.ConfigGet("AdditionalModulePaths").split(";")
|
||||
HomeMod = FreeCAD.getUserAppDataDir()+"Mod"
|
||||
HomeMod = os.path.realpath(HomeMod)
|
||||
@@ -117,12 +124,12 @@ def InitApplications():
|
||||
|
||||
# this allows importing with:
|
||||
# from FreeCAD.Module import package
|
||||
FreeCAD.__path__ = [ModDir, Lib64Dir, LibDir, HomeMod]
|
||||
FreeCAD.__path__ = [ModDir] + libpaths + [HomeMod]
|
||||
|
||||
# also add these directories to the sys.path to
|
||||
# not change the old behaviour. once we have moved to
|
||||
# proper python modules this can eventuelly be removed.
|
||||
sys.path = [ModDir, Lib64Dir, LibDir, ExtDir] + sys.path
|
||||
sys.path = [ModDir] + libpaths + [ExtDir] + sys.path
|
||||
|
||||
for Dir in ModDict.values():
|
||||
if ((Dir != '') & (Dir != 'CVS') & (Dir != '__init__.py')):
|
||||
@@ -140,7 +147,7 @@ def InitApplications():
|
||||
Log('-'*100+'\n')
|
||||
Log(traceback.format_exc())
|
||||
Log('-'*100+'\n')
|
||||
Err('During initialization the error ' + str(inst) + ' occurred in ' + InstallFile + '\n')
|
||||
Err('During initialization the error "' + str(inst) + '" occurred in ' + InstallFile + '\n')
|
||||
Err('Please look into the log file for further information\n')
|
||||
else:
|
||||
Log('Init: Initializing ' + Dir + '... done\n')
|
||||
@@ -156,25 +163,25 @@ def InitApplications():
|
||||
for _, freecad_module_name, freecad_module_ispkg in pkgutil.iter_modules(freecad.__path__, "freecad."):
|
||||
if freecad_module_ispkg:
|
||||
Log('Init: Initializing ' + freecad_module_name + '\n')
|
||||
freecad_module = importlib.import_module(freecad_module_name)
|
||||
extension_modules += [freecad_module_name]
|
||||
if any (module_name == 'init' for _, module_name, ispkg in pkgutil.iter_modules(freecad_module.__path__)):
|
||||
try:
|
||||
try:
|
||||
freecad_module = importlib.import_module(freecad_module_name)
|
||||
extension_modules += [freecad_module_name]
|
||||
if any (module_name == 'init' for _, module_name, ispkg in pkgutil.iter_modules(freecad_module.__path__)):
|
||||
importlib.import_module(freecad_module_name + '.init')
|
||||
Log('Init: Initializing ' + freecad_module_name + '... done\n')
|
||||
except Exception as inst:
|
||||
Err('During initialization the error ' + str(inst) + ' occurred in ' + freecad_module_name + '\n')
|
||||
Err('-'*80+'\n')
|
||||
Err(traceback.format_exc())
|
||||
Err('-'*80+'\n')
|
||||
Log('Init: Initializing ' + freecad_module_name + '... failed\n')
|
||||
Log('-'*80+'\n')
|
||||
Log(traceback.format_exc())
|
||||
Log('-'*80+'\n')
|
||||
else:
|
||||
Log('Init: No init module found in ' + freecad_module_name + ', skipping\n')
|
||||
else:
|
||||
Log('Init: No init module found in ' + freecad_module_name + ', skipping\n')
|
||||
except Exception as inst:
|
||||
Err('During initialization the error "' + str(inst) + '" occurred in ' + freecad_module_name + '\n')
|
||||
Err('-'*80+'\n')
|
||||
Err(traceback.format_exc())
|
||||
Err('-'*80+'\n')
|
||||
Log('Init: Initializing ' + freecad_module_name + '... failed\n')
|
||||
Log('-'*80+'\n')
|
||||
Log(traceback.format_exc())
|
||||
Log('-'*80+'\n')
|
||||
except ImportError as inst:
|
||||
Err('During initialization the error ' + str(inst) + ' occurred\n')
|
||||
Err('During initialization the error "' + str(inst) + '" occurred\n')
|
||||
|
||||
Log("Using "+ModDir+" as module path!\n")
|
||||
# In certain cases the PathExtension list can contain invalid strings. We concatenate them to a single string
|
||||
@@ -235,6 +242,378 @@ App.__unit_test__ = []
|
||||
|
||||
Log ('Init: starting App::FreeCADInit.py\n')
|
||||
|
||||
try:
|
||||
import sys,os,traceback,io,inspect
|
||||
from datetime import datetime
|
||||
except ImportError:
|
||||
FreeCAD.Console.PrintError("\n\nSeems the python standard libs are not installed, bailing out!\n\n")
|
||||
raise
|
||||
|
||||
class FCADLogger(object):
|
||||
'''Convenient class for tagged logging.
|
||||
|
||||
Example usage:
|
||||
>>> logger = FreeCAD.Logger('MyModule')
|
||||
>>> logger.info('log test {}',1)
|
||||
24.36053 <MyModule> <input>(1): test log 1
|
||||
|
||||
The default output format is:
|
||||
<timestamp> <tag> <source file>(line number): message
|
||||
|
||||
The message is formatted using new style Python string formatting, e.g.
|
||||
'test {}'.format(1). It is strongly recommended to not directly use
|
||||
Python string formatting, but pass additional argument indirectly through
|
||||
various logger print function, because the logger can skip string
|
||||
evaluation in case the logging level is disabled. For more options,
|
||||
please consult the docstring of __init__(), catch() and report().
|
||||
|
||||
To set/get logger level:
|
||||
>>> FreeCAD.setLogLevel('MyModule','Trace')
|
||||
>>> FreeCAD.getLogLevel('MyModule')
|
||||
4
|
||||
|
||||
There are five predefined logger level, each corresponding to an integer
|
||||
value, as shown below together with the corresponding logger print
|
||||
method,
|
||||
0: Error, Logger.error()
|
||||
1: Warning, Logger.warn()
|
||||
2: Message, Logger.msg() or info()
|
||||
3: Log, Logger.log() or debug()
|
||||
4: Trace, Logger.trace()
|
||||
|
||||
FreeCAD.setLogLevel() supports both text and integer value, which allows
|
||||
you to define your own levels. The level set is persisted to user
|
||||
configuration file.
|
||||
|
||||
By default any tag has a log level of 2 for release, and 3 for debug
|
||||
build.
|
||||
'''
|
||||
|
||||
_string_type = str if sys.version_info[0] >= 3 else basestring
|
||||
|
||||
_levels = { 'Error':0, 'error':0,
|
||||
'Warning':1, 'warn':1,
|
||||
'Message':2, 'msg':2, 'info':2,
|
||||
'Log':3, 'log':3, 'debug':3,
|
||||
'Trace':4, 'trace':4,}
|
||||
_printer = [
|
||||
FreeCAD.Console.PrintError,
|
||||
FreeCAD.Console.PrintWarning,
|
||||
FreeCAD.Console.PrintMessage,
|
||||
FreeCAD.Console.PrintLog,
|
||||
FreeCAD.Console.PrintLog ]
|
||||
|
||||
def __init__(self, tag, **kargs):
|
||||
'''Construct a logger instance.
|
||||
|
||||
Supported arguments are their default values are,
|
||||
|
||||
* tag: a string tag for this logger. The log level of this logger can be
|
||||
accessed using FreeCAD.getLogLevel(tag)/setLogLevel(tag,level).
|
||||
All logger instance with the same tag shares the same level
|
||||
setting.
|
||||
|
||||
* printTag (True): whether to print tag
|
||||
|
||||
* noUpdateUI (True): whether to update GUI when printing. This is useful
|
||||
to show log output on lengthy operations. Be
|
||||
careful though, this may allow unexpected user
|
||||
interaction when the application is busy, which may
|
||||
lead to crash
|
||||
|
||||
* timing (True): whether to print time stamp
|
||||
|
||||
* lineno (True): whether to print source file and line number
|
||||
|
||||
* parent (None): provide a parent logger, so that the log printing will
|
||||
check for parent's log level in addition of its own
|
||||
|
||||
* title ('FreeCAD'): message box title used by report()
|
||||
'''
|
||||
self.tag = tag
|
||||
self.laststamp = datetime.now()
|
||||
for key,default in (('printTag',True),('noUpdateUI',True),
|
||||
('timing',True),('lineno',True),('parent',None),
|
||||
('title','FreeCAD')) :
|
||||
setattr(self,key,kargs.get(key,default))
|
||||
|
||||
def _isEnabledFor(self,level):
|
||||
'''Internal function to check for an integer log level.
|
||||
|
||||
* level: integer log level
|
||||
'''
|
||||
|
||||
if self.parent and not self.parent._isEnabledFor(level):
|
||||
return False
|
||||
return FreeCAD.getLogLevel(self.tag) >= level
|
||||
|
||||
def isEnabledFor(self,level):
|
||||
'''To check for an integer or text log level.
|
||||
|
||||
* level: integer or text log level
|
||||
'''
|
||||
if not isinstance(level,int):
|
||||
level = self.__class__._levels[level]
|
||||
return self._isEnabledFor(level)
|
||||
|
||||
def error(self,msg,*args,**kargs):
|
||||
'''"Error" level log printer
|
||||
|
||||
* msg: message string. May contain new style Python string formatter.
|
||||
|
||||
This function accepts additional positional and keyword arguments,
|
||||
which are forward to string.format() to generate the logging
|
||||
message. It is strongly recommended to not directly use Python
|
||||
string formatting, but pass additional arguments here, because the
|
||||
printer can skip string evaluation in case the logging level is
|
||||
disabled.
|
||||
'''
|
||||
if self._isEnabledFor(0):
|
||||
frame = kargs.pop('frame',0)+1
|
||||
self._log(0,msg,frame,args,kargs)
|
||||
|
||||
def warn(self,msg,*args,**kargs):
|
||||
'''"Warning" level log printer
|
||||
|
||||
* msg: message string. May contain new style Python string formatter.
|
||||
|
||||
This function accepts additional positional and keyword arguments,
|
||||
which are forward to string.format() to generate the logging
|
||||
message. It is strongly recommended to not directly use Python
|
||||
string formatting, but pass additional arguments here, because the
|
||||
printer can skip string evaluation in case the logging level is
|
||||
disabled.
|
||||
'''
|
||||
if self._isEnabledFor(1):
|
||||
frame = kargs.pop('frame',0)+1
|
||||
self._log(1,msg,frame,args,kargs)
|
||||
|
||||
def msg(self,msg,*args,**kargs):
|
||||
'''"Message" level log printer
|
||||
|
||||
* msg: message string. May contain new style Python string formatter.
|
||||
|
||||
This function accepts additional positional and keyword arguments,
|
||||
which are forward to string.format() to generate the logging
|
||||
message. It is strongly recommended to not directly use Python
|
||||
string formatting, but pass additional arguments here, because the
|
||||
printer can skip string evaluation in case the logging level is
|
||||
disabled.
|
||||
'''
|
||||
if self._isEnabledFor(2):
|
||||
frame = kargs.pop('frame',0)+1
|
||||
self._log(2,msg,frame,args,kargs)
|
||||
|
||||
info = msg
|
||||
|
||||
def log(self,msg,*args,**kargs):
|
||||
'''"Log" level log printer
|
||||
|
||||
* msg: message string. May contain new style Python string formatter.
|
||||
|
||||
This function accepts additional positional and keyword arguments,
|
||||
which are forward to string.format() to generate the logging
|
||||
message. It is strongly recommended to not directly use Python
|
||||
string formatting, but pass additional arguments here, because the
|
||||
printer can skip string evaluation in case the logging level is
|
||||
disabled.
|
||||
'''
|
||||
if self._isEnabledFor(3):
|
||||
frame = kargs.pop('frame',0)+1
|
||||
self._log(3,msg,frame,args,kargs)
|
||||
|
||||
debug = log
|
||||
|
||||
def trace(self,msg,*args,**kargs):
|
||||
'''"Trace" level log printer
|
||||
|
||||
* msg: message string. May contain new style Python string formatter.
|
||||
|
||||
This function accepts additional positional and keyword arguments,
|
||||
which are forward to string.format() to generate the logging
|
||||
message. It is strongly recommended to not directly use Python
|
||||
string formatting, but pass additional arguments here, because the
|
||||
printer can skip string evaluation in case the logging level is
|
||||
disabled.
|
||||
'''
|
||||
if self._isEnabledFor(4):
|
||||
frame = kargs.pop('frame',0)+1
|
||||
self._log(4,msg,frame,args,kargs)
|
||||
|
||||
def _log(self,level,msg,frame=0,args=(),kargs=None):
|
||||
'''Internal log printing function.
|
||||
|
||||
* level: integer log level
|
||||
|
||||
* msg: message, may contain new style string format specifier
|
||||
|
||||
* frame (0): the calling frame for printing source file and line
|
||||
number. For example, in case you have your own logging
|
||||
function, and you want to show the callers source
|
||||
location, then set frame to one.
|
||||
|
||||
* args: tuple for postiional arguments to be passed to
|
||||
string.format()
|
||||
|
||||
* kargs: dictionary for keyword arguments to be passed to
|
||||
string.format()
|
||||
'''
|
||||
|
||||
if (args or kargs) and isinstance(msg,self.__class__._string_type):
|
||||
if not kargs:
|
||||
msg = msg.format(*args)
|
||||
else:
|
||||
msg = msg.format(*args,**kargs)
|
||||
|
||||
prefix = ''
|
||||
|
||||
if self.timing:
|
||||
now = datetime.now()
|
||||
prefix += '{} '.format((now-self.laststamp).total_seconds())
|
||||
self.laststamp = now
|
||||
|
||||
if self.printTag:
|
||||
prefix += '<{}> '.format(self.tag)
|
||||
|
||||
if self.lineno:
|
||||
try:
|
||||
frame = sys._getframe(frame+1)
|
||||
prefix += '{}({}): '.format(os.path.basename(
|
||||
frame.f_code.co_filename),frame.f_lineno)
|
||||
except Exception:
|
||||
frame = inspect.stack()[frame+1]
|
||||
prefix += '{}({}): '.format(os.path.basename(frame[1]),frame[2])
|
||||
|
||||
self.__class__._printer[level]('{}{}\n'.format(prefix,msg))
|
||||
|
||||
if not self.noUpdateUI and FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
try:
|
||||
FreeCADGui.updateGui()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _catch(self,level,msg,func,args=None,kargs=None):
|
||||
'''Internal function to log exception of any callable.
|
||||
|
||||
* level: integer log level
|
||||
|
||||
* msg: message string. Unlike _log(), this argument must not contain
|
||||
any string formatter.
|
||||
|
||||
* func: a callable object
|
||||
|
||||
* args: tuple of positional arguments to be passed to func.
|
||||
|
||||
* kargs: dictionary of keyword arguments to be passed to func.
|
||||
'''
|
||||
try:
|
||||
if not args:
|
||||
args = []
|
||||
if not kargs:
|
||||
kargs = {}
|
||||
return func(*args,**kargs)
|
||||
except Exception:
|
||||
if self._isEnabledFor(level):
|
||||
self._log(level,msg+'\n'+traceback.format_exc(),frame=2)
|
||||
|
||||
def catch(self,msg,func,*args,**kargs):
|
||||
'''Catch any exception from a function and print as "Error".
|
||||
|
||||
* msg: message string. Unlike log printer, this argument must not
|
||||
contain any string formatter.
|
||||
|
||||
* func: a callable object
|
||||
|
||||
* args: tuple of positional arguments to be passed to func.
|
||||
|
||||
* kargs: dictionary of keyword arguments to be passed to func.
|
||||
'''
|
||||
return self._catch(0,msg,func,args,kargs)
|
||||
|
||||
def catchWarn(self,msg,func,*args,**kargs):
|
||||
'''Catch any exception from a function and print as "Warning".
|
||||
|
||||
* msg: message string. Unlike log printer, this argument must not
|
||||
contain any string formatter.
|
||||
|
||||
* func: a callable object
|
||||
|
||||
* args: tuple of positional arguments to be passed to func.
|
||||
|
||||
* kargs: dictionary of keyword arguments to be passed to func.
|
||||
'''
|
||||
return self._catch(1,msg,func,args,kargs)
|
||||
|
||||
def catchMsg(self,msg,func,*args,**kargs):
|
||||
'''Catch any exception from a function and print as "Message".
|
||||
|
||||
* msg: message string. Unlike log printer, this argument must not
|
||||
contain any string formatter.
|
||||
|
||||
* func: a callable object
|
||||
|
||||
* args: tuple of positional arguments to be passed to func.
|
||||
|
||||
* kargs: dictionary of keyword arguments to be passed to func.
|
||||
'''
|
||||
return self._catch(2,msg,func,args,kargs)
|
||||
|
||||
catchInfo = catchMsg
|
||||
|
||||
def catchLog(self,msg,func,*args,**kargs):
|
||||
'''Catch any exception from a function and print as "Log".
|
||||
|
||||
* msg: message string. Unlike log printer, this argument must not
|
||||
contain any string formatter.
|
||||
|
||||
* func: a callable object
|
||||
|
||||
* args: tuple of positional arguments to be passed to func.
|
||||
|
||||
* kargs: dictionary of keyword arguments to be passed to func.
|
||||
'''
|
||||
return self._catch(3,msg,func,args,kargs)
|
||||
|
||||
catchDebug = catchLog
|
||||
|
||||
def catchTrace(self,msg,func,*args,**kargs):
|
||||
'''Catch any exception from a function and print as "Trace".
|
||||
|
||||
* msg: message string. Unlike log printer, this argument must not
|
||||
contain any string formatter.
|
||||
|
||||
* func: a callable object
|
||||
|
||||
* args: tuple of positional arguments to be passed to func.
|
||||
|
||||
* kargs: dictionary of keyword arguments to be passed to func.
|
||||
'''
|
||||
return self._catch(4,msg,func,args,kargs)
|
||||
|
||||
def report(self,msg,func,*args,**kargs):
|
||||
'''Catch any exception report it with a message box.
|
||||
|
||||
* msg: message string. Unlike log printer, this argument must not
|
||||
contain any string formatter.
|
||||
|
||||
* func: a callable object
|
||||
|
||||
* args: tuple of positional arguments to be passed to func.
|
||||
|
||||
* kargs: dictionary of keyword arguments to be passed to func.
|
||||
'''
|
||||
try:
|
||||
return func(*args,**kargs)
|
||||
except Exception as e:
|
||||
self.error(msg+'\n'+traceback.format_exc(),frame=1)
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui,PySide
|
||||
PySide.QtGui.QMessageBox.critical(
|
||||
FreeCADGui.getMainWindow(),self.title,str(e))
|
||||
|
||||
FreeCAD.Logger = FCADLogger
|
||||
|
||||
# init every application by importing Init.py
|
||||
try:
|
||||
import traceback
|
||||
|
||||
@@ -79,3 +79,62 @@ PyObject* GeoFeature::getPyObject(void)
|
||||
}
|
||||
return Py::new_reference_to(PythonObject);
|
||||
}
|
||||
|
||||
|
||||
std::pair<std::string,std::string> GeoFeature::getElementName(
|
||||
const char *name, ElementNameType type) const
|
||||
{
|
||||
(void)type;
|
||||
|
||||
std::pair<std::string,std::string> ret;
|
||||
if(!name) return ret;
|
||||
|
||||
ret.second = name;
|
||||
return ret;
|
||||
}
|
||||
|
||||
DocumentObject *GeoFeature::resolveElement(DocumentObject *obj, const char *subname,
|
||||
std::pair<std::string,std::string> &elementName, bool append,
|
||||
ElementNameType type, const DocumentObject *filter,
|
||||
const char **_element, GeoFeature **geoFeature)
|
||||
{
|
||||
if(!obj || !obj->getNameInDocument())
|
||||
return 0;
|
||||
if(!subname)
|
||||
subname = "";
|
||||
const char *element = Data::ComplexGeoData::findElementName(subname);
|
||||
if(_element) *_element = element;
|
||||
auto sobj = obj->getSubObject(subname);
|
||||
if(!sobj)
|
||||
return 0;
|
||||
obj = sobj->getLinkedObject(true);
|
||||
auto geo = dynamic_cast<GeoFeature*>(obj);
|
||||
if(geoFeature)
|
||||
*geoFeature = geo;
|
||||
if(!obj || (filter && obj!=filter))
|
||||
return 0;
|
||||
if(!element || !element[0]) {
|
||||
if(append)
|
||||
elementName.second = Data::ComplexGeoData::oldElementName(subname);
|
||||
return sobj;
|
||||
}
|
||||
|
||||
if(!geo || hasHiddenMarker(element)) {
|
||||
if(!append)
|
||||
elementName.second = element;
|
||||
else
|
||||
elementName.second = Data::ComplexGeoData::oldElementName(subname);
|
||||
return sobj;
|
||||
}
|
||||
if(!append)
|
||||
elementName = geo->getElementName(element,type);
|
||||
else{
|
||||
const auto &names = geo->getElementName(element,type);
|
||||
std::string prefix(subname,element-subname);
|
||||
if(names.first.size())
|
||||
elementName.first = prefix + names.first;
|
||||
elementName.second = prefix + names.second;
|
||||
}
|
||||
return sobj;
|
||||
}
|
||||
|
||||
|
||||
+42
-1
@@ -66,7 +66,48 @@ public:
|
||||
* @return the Python binding object
|
||||
*/
|
||||
virtual PyObject* getPyObject(void);
|
||||
|
||||
|
||||
/// Specify the type of element name to return when calling getElementName()
|
||||
enum ElementNameType {
|
||||
/// Normal usage
|
||||
Normal=0,
|
||||
/// For importing
|
||||
Import=1,
|
||||
/// For exporting
|
||||
Export=2,
|
||||
};
|
||||
/** Return the new and old style sub-element name
|
||||
*
|
||||
* @param name: input name
|
||||
* @param type: desired element name type to return
|
||||
*
|
||||
* @return a pair(newName,oldName). New element name may be empty.
|
||||
*
|
||||
* This function currently is does nothing. The new style element name
|
||||
* generation will be added in the next batch of patches.
|
||||
*/
|
||||
virtual std::pair<std::string,std::string> getElementName(
|
||||
const char *name, ElementNameType type=Normal) const;
|
||||
|
||||
/** Resolve both the new and old style element name
|
||||
*
|
||||
* @param obj: top parent object
|
||||
* @param subname: subname reference
|
||||
* @param elementName: output of a pair(newElementName,oldElementName)
|
||||
* @param append: Whether to include subname prefix into the returned
|
||||
* element name
|
||||
* @param type: the type of element name to request
|
||||
* @param filter: If none zero, then only perform lookup when the element
|
||||
* owner object is the same as this filter
|
||||
* @param element: return the start of element name in subname
|
||||
*
|
||||
* @return Return the owner object of the element
|
||||
*/
|
||||
static DocumentObject *resolveElement(App::DocumentObject *obj,
|
||||
const char *subname, std::pair<std::string,std::string> &elementName,
|
||||
bool append=false, ElementNameType type=Normal,
|
||||
const DocumentObject *filter=0,const char **element=0, GeoFeature **geo=0);
|
||||
|
||||
/**
|
||||
* @brief Calculates the placement in the global reference coordinate system
|
||||
*
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "Origin.h"
|
||||
#include "OriginGroupExtension.h"
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Tools.h>
|
||||
//#include "GeoFeatureGroupPy.h"
|
||||
//#include "FeaturePythonPyImp.h"
|
||||
|
||||
@@ -100,11 +101,9 @@ DocumentObject* GeoFeatureGroupExtension::getGroupOfObject(const DocumentObject*
|
||||
//There is a chance that a derived geofeaturegroup links with a local link and hence is not
|
||||
//the parent group even though it links to the object. We use hasObject as one and only truth
|
||||
//if it has the object within the group
|
||||
if(inObj->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId()) &&
|
||||
inObj->getExtensionByType<GeoFeatureGroupExtension>()->hasObject(obj)) {
|
||||
|
||||
auto group = inObj->getExtensionByType<GeoFeatureGroupExtension>(true);
|
||||
if(group && group->hasObject(obj))
|
||||
return inObj;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -124,10 +123,9 @@ Base::Placement GeoFeatureGroupExtension::recursiveGroupPlacement(GeoFeatureGrou
|
||||
|
||||
auto inList = group->getExtendedObject()->getInList();
|
||||
for(auto* link : inList) {
|
||||
if(link->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId())){
|
||||
if (link->getExtensionByType<GeoFeatureGroupExtension>()->hasObject(group->getExtendedObject()))
|
||||
return recursiveGroupPlacement(link->getExtensionByType<GeoFeatureGroupExtension>()) * group->placement().getValue();
|
||||
}
|
||||
auto parent = link->getExtensionByType<GeoFeatureGroupExtension>(true);
|
||||
if(parent && parent->hasObject(group->getExtendedObject()))
|
||||
return recursiveGroupPlacement(parent) * group->placement().getValue();
|
||||
}
|
||||
|
||||
return group->placement().getValue();
|
||||
@@ -193,9 +191,10 @@ std::vector<DocumentObject*> GeoFeatureGroupExtension::removeObjects(std::vector
|
||||
void GeoFeatureGroupExtension::extensionOnChanged(const Property* p) {
|
||||
|
||||
//objects are only allowed in a single GeoFeatureGroup
|
||||
if((strcmp(p->getName(), "Group")==0)) {
|
||||
if(p == &Group && !Group.testStatus(Property::User3)) {
|
||||
|
||||
if(!getExtendedObject()->getDocument()->isPerformingTransaction()) {
|
||||
if(!getExtendedObject()->isRestoring() &&
|
||||
!getExtendedObject()->getDocument()->isPerformingTransaction()) {
|
||||
|
||||
bool error = false;
|
||||
auto corrected = Group.getValues();
|
||||
@@ -205,9 +204,10 @@ void GeoFeatureGroupExtension::extensionOnChanged(const Property* p) {
|
||||
//would return anyone of it and hence it is possible that we miss an error. We need a custom check
|
||||
auto list = obj->getInList();
|
||||
for (auto in : list) {
|
||||
if(in->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId()) && //is GeoFeatureGroup?
|
||||
in != getExtendedObject() && //is a different one?
|
||||
in->getExtensionByType<App::GeoFeatureGroupExtension>()->hasObject(obj)) { //is not a non-grouping link?
|
||||
if(in == getExtendedObject())
|
||||
continue;
|
||||
auto parent = in->getExtensionByType<GeoFeatureGroupExtension>(true);
|
||||
if(parent && parent->hasObject(obj)) {
|
||||
error = true;
|
||||
corrected.erase(std::remove(corrected.begin(), corrected.end(), obj), corrected.end());
|
||||
}
|
||||
@@ -216,6 +216,7 @@ void GeoFeatureGroupExtension::extensionOnChanged(const Property* p) {
|
||||
|
||||
//if an error was found we need to correct the values and inform the user
|
||||
if(error) {
|
||||
Base::ObjectStatusLocker<Property::Status, Property> guard(Property::User3, &Group);
|
||||
Group.setValues(corrected);
|
||||
throw Base::RuntimeError("Object can only be in a single GeoFeatureGroup");
|
||||
}
|
||||
@@ -253,40 +254,17 @@ std::vector< DocumentObject* > GeoFeatureGroupExtension::getScopedObjectsFromLin
|
||||
return std::vector< DocumentObject* >();
|
||||
|
||||
std::vector< App::DocumentObject* > result;
|
||||
auto link = Base::freecad_dynamic_cast<PropertyLinkBase>(prop);
|
||||
if(link && link->getScope()==scope)
|
||||
link->getLinks(result);
|
||||
|
||||
if(prop->getTypeId().isDerivedFrom(App::PropertyLink::getClassTypeId()) &&
|
||||
static_cast<App::PropertyLink*>(prop)->getScope() == scope) {
|
||||
|
||||
result.push_back(static_cast<App::PropertyLink*>(prop)->getValue());
|
||||
}
|
||||
|
||||
if(prop->getTypeId().isDerivedFrom(App::PropertyLinkList::getClassTypeId()) &&
|
||||
static_cast<App::PropertyLinkList*>(prop)->getScope() == scope) {
|
||||
|
||||
auto vec = static_cast<App::PropertyLinkList*>(prop)->getValues();
|
||||
result.insert(result.end(), vec.begin(), vec.end());
|
||||
}
|
||||
|
||||
if(prop->getTypeId().isDerivedFrom(App::PropertyLinkSub::getClassTypeId()) &&
|
||||
static_cast<App::PropertyLinkSub*>(prop)->getScope() == scope) {
|
||||
|
||||
result.push_back(static_cast<App::PropertyLinkSub*>(prop)->getValue());
|
||||
}
|
||||
|
||||
if(prop->getTypeId().isDerivedFrom(App::PropertyLinkSubList::getClassTypeId()) &&
|
||||
static_cast<App::PropertyLinkSubList*>(prop)->getScope() == scope) {
|
||||
|
||||
auto vec = static_cast<App::PropertyLinkSubList*>(prop)->getValues();
|
||||
result.insert(result.end(), vec.begin(), vec.end());
|
||||
}
|
||||
|
||||
//getLinks() guarantees no nullptrs
|
||||
//
|
||||
//it is important to remove all nullptrs
|
||||
result.erase(std::remove(result.begin(), result.end(), nullptr), result.end());
|
||||
// result.erase(std::remove(result.begin(), result.end(), nullptr), result.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GeoFeatureGroupExtension::getCSOutList(const App::DocumentObject* obj,
|
||||
std::vector< DocumentObject* >& vec) {
|
||||
|
||||
@@ -373,6 +351,59 @@ void GeoFeatureGroupExtension::recursiveCSRelevantLinks(const DocumentObject* ob
|
||||
}
|
||||
}
|
||||
|
||||
bool GeoFeatureGroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
|
||||
{
|
||||
ret = 0;
|
||||
const char *dot;
|
||||
if(!subname || *subname==0) {
|
||||
auto obj = dynamic_cast<const DocumentObject*>(getExtendedContainer());
|
||||
ret = const_cast<DocumentObject*>(obj);
|
||||
if(mat && transform)
|
||||
*mat *= const_cast<GeoFeatureGroupExtension*>(this)->placement().getValue().toMatrix();
|
||||
}else if((dot=strchr(subname,'.'))) {
|
||||
if(subname[0]!='$')
|
||||
ret = Group.find(std::string(subname,dot));
|
||||
else{
|
||||
std::string name = std::string(subname+1,dot);
|
||||
for(auto child : Group.getValues()) {
|
||||
if(name == child->Label.getStrValue()){
|
||||
ret = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(ret) {
|
||||
if(dot) ++dot;
|
||||
if(dot && *dot && !ret->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId())) {
|
||||
// Consider this
|
||||
// Body
|
||||
// | -- Pad
|
||||
// | -- Sketch
|
||||
//
|
||||
// Suppose we want to getSubObject(Body,"Pad.Sketch.")
|
||||
// Because of the special property of geo feature group, both
|
||||
// Pad and Sketch are children of Body, so we can't call
|
||||
// getSubObject(Pad,"Sketch.") as this will transform Sketch
|
||||
// using Pad's placement.
|
||||
//
|
||||
const char *next = strchr(dot,'.');
|
||||
if(next) {
|
||||
App::DocumentObject *nret=0;
|
||||
extensionGetSubObject(nret,dot,pyObj,mat,transform,depth+1);
|
||||
if(nret) {
|
||||
ret = nret;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(mat && transform)
|
||||
*mat *= const_cast<GeoFeatureGroupExtension*>(this)->placement().getValue().toMatrix();
|
||||
ret = ret->getSubObject(dot?dot:"",pyObj,mat,true,depth+1);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeoFeatureGroupExtension::areLinksValid(const DocumentObject* obj) {
|
||||
|
||||
@@ -448,6 +479,14 @@ void GeoFeatureGroupExtension::getInvalidLinkObjects(const DocumentObject* obj,
|
||||
}
|
||||
}
|
||||
|
||||
bool GeoFeatureGroupExtension::extensionGetSubObjects(std::vector<std::string> &ret, int) const {
|
||||
for(auto obj : Group.getValues()) {
|
||||
if(obj && obj->getNameInDocument() && !obj->testStatus(ObjectStatus::GeoExcluded))
|
||||
ret.push_back(std::string(obj->getNameInDocument())+'.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Python feature ---------------------------------------------------------
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ namespace App
|
||||
*/
|
||||
class AppExport GeoFeatureGroupExtension : public App::GroupExtension
|
||||
{
|
||||
typedef App::GroupExtension inherited;
|
||||
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::GeoFeatureGroupExtension);
|
||||
|
||||
public:
|
||||
@@ -95,6 +96,11 @@ public:
|
||||
return obj->hasExtension(GroupExtension::getExtensionClassTypeId()) &&
|
||||
!obj->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId());
|
||||
}
|
||||
|
||||
virtual bool extensionGetSubObject(DocumentObject *&ret, const char *subname, PyObject **pyObj,
|
||||
Base::Matrix4D *mat, bool transform, int depth) const override;
|
||||
|
||||
virtual bool extensionGetSubObjects(std::vector<std::string> &ret, int reason) const override;
|
||||
|
||||
virtual std::vector< DocumentObject* > addObjects(std::vector< DocumentObject* > obj) override;
|
||||
virtual std::vector< DocumentObject* > removeObjects(std::vector< DocumentObject* > obj) override;
|
||||
|
||||
+93
-13
@@ -33,6 +33,7 @@
|
||||
#include "FeaturePythonPyImp.h"
|
||||
#include "GeoFeatureGroupExtension.h"
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Tools.h>
|
||||
|
||||
using namespace App;
|
||||
|
||||
@@ -42,7 +43,10 @@ GroupExtension::GroupExtension()
|
||||
{
|
||||
initExtensionType(GroupExtension::getExtensionClassTypeId());
|
||||
|
||||
EXTENSION_ADD_PROPERTY_TYPE(Group,(0),"Base",(App::PropertyType)(Prop_Output),"List of referenced objects");
|
||||
EXTENSION_ADD_PROPERTY_TYPE(Group,(0),"Base",(App::PropertyType)(Prop_None),"List of referenced objects");
|
||||
|
||||
EXTENSION_ADD_PROPERTY_TYPE(_GroupTouched, (false), "Base",
|
||||
PropertyType(Prop_Hidden|Prop_Transient),0);
|
||||
}
|
||||
|
||||
GroupExtension::~GroupExtension()
|
||||
@@ -251,15 +255,12 @@ bool GroupExtension::recursiveHasObject(const DocumentObject* obj, const GroupEx
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool GroupExtension::isChildOf(const GroupExtension* group, bool recursive) const
|
||||
{
|
||||
return group->hasObject(getExtendedObject(), recursive);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::vector<DocumentObject*> GroupExtension::getObjects() const
|
||||
const std::vector<DocumentObject*> &GroupExtension::getObjects() const
|
||||
{
|
||||
return Group.getValues();
|
||||
}
|
||||
@@ -293,10 +294,9 @@ DocumentObject* GroupExtension::getGroupOfObject(const DocumentObject* obj)
|
||||
//note that we return here only Groups, but nothing derived from it, e.g. no GeoFeatureGroups.
|
||||
//That is important as there are clear differences between groups/geofeature groups (e.g. an object
|
||||
//can be in only one group, and only one geofeaturegroup, however, it can be in both at the same time)
|
||||
auto list = obj->getInList();
|
||||
for (auto obj : list) {
|
||||
if(obj->hasExtension(App::GroupExtension::getExtensionClassTypeId(), false))
|
||||
return obj;
|
||||
for (auto o : obj->getInList()) {
|
||||
if(o->hasExtension(App::GroupExtension::getExtensionClassTypeId(), false))
|
||||
return o;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -316,10 +316,11 @@ void GroupExtension::extensionOnChanged(const Property* p) {
|
||||
|
||||
//objects are only allowed in a single group. Note that this check must only be done for normal
|
||||
//groups, not any derived classes
|
||||
if((this->getExtensionTypeId() == GroupExtension::getExtensionClassTypeId()) &&
|
||||
(strcmp(p->getName(), "Group")==0)) {
|
||||
|
||||
if(!getExtendedObject()->getDocument()->isPerformingTransaction()) {
|
||||
if((this->getExtensionTypeId() == GroupExtension::getExtensionClassTypeId())
|
||||
&& p == &Group && !Group.testStatus(Property::User3))
|
||||
{
|
||||
if(!getExtendedObject()->isRestoring() &&
|
||||
!getExtendedObject()->getDocument()->isPerformingTransaction()) {
|
||||
|
||||
bool error = false;
|
||||
auto corrected = Group.getValues();
|
||||
@@ -339,15 +340,94 @@ void GroupExtension::extensionOnChanged(const Property* p) {
|
||||
|
||||
//if an error was found we need to correct the values and inform the user
|
||||
if(error) {
|
||||
Base::ObjectStatusLocker<Property::Status, Property> guard(Property::User3, &Group);
|
||||
Group.setValues(corrected);
|
||||
throw Base::RuntimeError("Object can only be in a single Group");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(p == &Group) {
|
||||
_Conns.clear();
|
||||
for(auto obj : Group.getValue()) {
|
||||
if(obj && obj->getNameInDocument()) {
|
||||
_Conns[obj] = obj->signalChanged.connect(boost::bind(
|
||||
&GroupExtension::slotChildChanged,this,_1,_2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
App::Extension::extensionOnChanged(p);
|
||||
}
|
||||
|
||||
void GroupExtension::slotChildChanged(const DocumentObject &obj, const Property &prop) {
|
||||
if(&prop == &obj.Visibility)
|
||||
_GroupTouched.touch();
|
||||
}
|
||||
|
||||
bool GroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *mat, bool /*transform*/, int depth) const
|
||||
{
|
||||
const char *dot;
|
||||
if(!subname || *subname==0) {
|
||||
auto obj = Base::freecad_dynamic_cast<const DocumentObject>(getExtendedContainer());
|
||||
ret = const_cast<DocumentObject*>(obj);
|
||||
return true;
|
||||
}
|
||||
dot=strchr(subname,'.');
|
||||
if(!dot)
|
||||
return false;
|
||||
if(subname[0]!='$')
|
||||
ret = Group.find(std::string(subname,dot));
|
||||
else{
|
||||
std::string name = std::string(subname+1,dot);
|
||||
for(auto child : Group.getValues()) {
|
||||
if(name == child->Label.getStrValue()){
|
||||
ret = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!ret)
|
||||
return false;
|
||||
return ret->getSubObject(dot+1,pyObj,mat,true,depth+1);
|
||||
}
|
||||
|
||||
bool GroupExtension::extensionGetSubObjects(std::vector<std::string> &ret, int) const {
|
||||
for(auto obj : Group.getValues()) {
|
||||
if(obj && obj->getNameInDocument())
|
||||
ret.push_back(std::string(obj->getNameInDocument())+'.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
App::DocumentObjectExecReturn *GroupExtension::extensionExecute(void) {
|
||||
// This touch property is for propagating changes to upper group
|
||||
_GroupTouched.touch();
|
||||
return inherited::extensionExecute();
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*> GroupExtension::getAllChildren() const {
|
||||
std::vector<DocumentObject*> res;
|
||||
std::set<DocumentObject*> rset;
|
||||
getAllChildren(res,rset);
|
||||
return res;
|
||||
}
|
||||
|
||||
void GroupExtension::getAllChildren(std::vector<App::DocumentObject*> &res,
|
||||
std::set<App::DocumentObject*> &rset) const
|
||||
{
|
||||
for(auto obj : Group.getValues()) {
|
||||
if(!obj || !obj->getNameInDocument())
|
||||
continue;
|
||||
if(!rset.insert(obj).second)
|
||||
continue;
|
||||
res.push_back(obj);
|
||||
auto ext = obj->getExtensionByType<GroupExtension>(true,false);
|
||||
if(ext)
|
||||
ext->getAllChildren(res,rset);
|
||||
}
|
||||
}
|
||||
|
||||
namespace App {
|
||||
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::GroupExtensionPython, App::GroupExtension)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#ifndef APP_GROUPEXTENSION_H
|
||||
#define APP_GROUPEXTENSION_H
|
||||
|
||||
#include <boost/signals2.hpp>
|
||||
#include "FeaturePython.h"
|
||||
#include "DocumentObject.h"
|
||||
#include "PropertyLinks.h"
|
||||
@@ -38,6 +39,7 @@ class GroupExtensionPy;
|
||||
class AppExport GroupExtension : public DocumentObjectExtension
|
||||
{
|
||||
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::GroupExtension);
|
||||
typedef DocumentObjectExtension inherited;
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
@@ -91,7 +93,7 @@ public:
|
||||
bool isChildOf(const GroupExtension* group, bool recursive = true) const;
|
||||
/** Returns a list of all objects this group does have.
|
||||
*/
|
||||
std::vector<DocumentObject*> getObjects() const;
|
||||
const std::vector<DocumentObject*> &getObjects() const;
|
||||
/** Returns a list of all objects of \a typeId this group does have.
|
||||
*/
|
||||
std::vector<DocumentObject*> getObjectsOfType(const Base::Type& typeId) const;
|
||||
@@ -109,15 +111,30 @@ public:
|
||||
virtual PyObject* getExtensionPyObject(void) override;
|
||||
|
||||
virtual void extensionOnChanged(const Property* p) override;
|
||||
|
||||
virtual bool extensionGetSubObject(DocumentObject *&ret, const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const override;
|
||||
|
||||
virtual bool extensionGetSubObjects(std::vector<std::string> &ret, int reason) const override;
|
||||
|
||||
virtual App::DocumentObjectExecReturn *extensionExecute(void) override;
|
||||
|
||||
std::vector<DocumentObject*> getAllChildren() const;
|
||||
void getAllChildren(std::vector<DocumentObject*> &, std::set<DocumentObject*> &) const;
|
||||
|
||||
/// Properties
|
||||
PropertyLinkList Group;
|
||||
PropertyBool _GroupTouched;
|
||||
|
||||
private:
|
||||
void removeObjectFromDocument(DocumentObject*);
|
||||
//this version if has object stores the already searched objects to prevent infinite recursion
|
||||
//in case of a cyclic group graph
|
||||
bool recursiveHasObject(const DocumentObject* obj, const GroupExtension* group, std::vector<const GroupExtension*> history) const;
|
||||
|
||||
// for tracking children visibility
|
||||
void slotChildChanged(const App::DocumentObject&, const App::Property&);
|
||||
std::unordered_map<const App::DocumentObject*, boost::signals2::scoped_connection> _Conns;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+1390
File diff suppressed because it is too large
Load Diff
+530
@@ -0,0 +1,530 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
|
||||
* *
|
||||
* 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 APP_LINK_H
|
||||
#define APP_LINK_H
|
||||
|
||||
#include <unordered_set>
|
||||
#include <boost/signals2.hpp>
|
||||
#include <boost/preprocessor/facilities/expand.hpp>
|
||||
#include <boost/preprocessor/cat.hpp>
|
||||
#include <boost/preprocessor/seq/for_each.hpp>
|
||||
#include <boost/preprocessor/seq/elem.hpp>
|
||||
#include <boost/preprocessor/seq/cat.hpp>
|
||||
#include <boost/preprocessor/tuple/elem.hpp>
|
||||
#include <boost/preprocessor/tuple/enum.hpp>
|
||||
#include "DocumentObject.h"
|
||||
#include "FeaturePython.h"
|
||||
#include "PropertyLinks.h"
|
||||
#include "DocumentObjectExtension.h"
|
||||
#include "FeaturePython.h"
|
||||
#include "GroupExtension.h"
|
||||
|
||||
#define LINK_THROW(_type,_msg) do{\
|
||||
if(FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG))\
|
||||
FC_ERR(_msg);\
|
||||
throw _type(_msg);\
|
||||
}while(0)
|
||||
|
||||
namespace App
|
||||
{
|
||||
|
||||
class AppExport LinkBaseExtension : public App::DocumentObjectExtension
|
||||
{
|
||||
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::LinkExtension);
|
||||
typedef App::DocumentObjectExtension inherited;
|
||||
|
||||
public:
|
||||
LinkBaseExtension();
|
||||
virtual ~LinkBaseExtension();
|
||||
|
||||
PropertyBool _LinkTouched;
|
||||
PropertyLinkList _ChildCache; // cache for plain group expansion
|
||||
|
||||
enum {
|
||||
LinkModeNone,
|
||||
LinkModeAutoDelete,
|
||||
LinkModeAutoLink,
|
||||
LinkModeAutoUnlink,
|
||||
};
|
||||
|
||||
/** \name Parameter definition
|
||||
*
|
||||
* Parameter definition (Name, Type, Property Type, Default, Document).
|
||||
* The variadic is here so that the parameter can be extended by adding
|
||||
* extra fields. See LINK_PARAM_EXT() for an example
|
||||
*/
|
||||
//@{
|
||||
|
||||
#define LINK_PARAM_LINK_PLACEMENT(...) \
|
||||
(LinkPlacement, Base::Placement, App::PropertyPlacement, Base::Placement(), "Link placement", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM_PLACEMENT(...) \
|
||||
(Placement, Base::Placement, App::PropertyPlacement, Base::Placement(), \
|
||||
"Alias to LinkPlacement to make the link object compatibale with other objects", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM_OBJECT(...) \
|
||||
(LinkedObject, App::DocumentObject*, App::PropertyLink, 0, "Linked object", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM_SUB_ELEMENT(...) \
|
||||
(SubElements, std::vector<std::string>, App::PropertyStringList, std::vector<std::string>(), \
|
||||
"Non-object Sub-element list of the linked object, e.g. Face1", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM_TRANSFORM(...) \
|
||||
(LinkTransform, bool, App::PropertyBool, false, \
|
||||
"Set to false to override linked object's placement", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM_SCALE(...) \
|
||||
(Scale, double, App::PropertyFloat, 1.0, "Scale factor", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM_SCALE_VECTOR(...) \
|
||||
(ScaleVector, Base::Vector3d, App::PropertyVector, Base::Vector3d(1,1,1), "Scale factors", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM_PLACEMENTS(...) \
|
||||
(PlacementList, std::vector<Base::Placement>, App::PropertyPlacementList, std::vector<Base::Placement>(),\
|
||||
"The placement for each link element", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM_SCALES(...) \
|
||||
(ScaleList, std::vector<Base::Vector3d>, App::PropertyVectorList, std::vector<Base::Vector3d>(),\
|
||||
"The scale factors for each link element", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM_VISIBILITIES(...) \
|
||||
(VisibilityList, boost::dynamic_bitset<>, App::PropertyBoolList, boost::dynamic_bitset<>(),\
|
||||
"The visibility state of each link element", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM_COUNT(...) \
|
||||
(ElementCount, int, App::PropertyInteger, 0, "Link element count", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM_ELEMENTS(...) \
|
||||
(ElementList, std::vector<App::DocumentObject*>, App::PropertyLinkList, std::vector<App::DocumentObject*>(),\
|
||||
"The link element object list", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM_SHOW_ELEMENT(...) \
|
||||
(ShowElement, bool, App::PropertyBool, true, "Enable link element list", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM_MODE(...) \
|
||||
(LinkMode, long, App::PropertyEnumeration, ((long)0), "Link group mode", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM_COLORED_ELEMENTS(...) \
|
||||
(ColoredElements, App::DocumentObject*, App::PropertyLinkSubHidden, \
|
||||
0, "Link colored elements", ##__VA_ARGS__)
|
||||
|
||||
#define LINK_PARAM(_param) (LINK_PARAM_##_param())
|
||||
|
||||
#define LINK_PNAME(_param) BOOST_PP_TUPLE_ELEM(0,_param)
|
||||
#define LINK_PTYPE(_param) BOOST_PP_TUPLE_ELEM(1,_param)
|
||||
#define LINK_PPTYPE(_param) BOOST_PP_TUPLE_ELEM(2,_param)
|
||||
#define LINK_PDEF(_param) BOOST_PP_TUPLE_ELEM(3,_param)
|
||||
#define LINK_PDOC(_param) BOOST_PP_TUPLE_ELEM(4,_param)
|
||||
|
||||
#define LINK_PINDEX(_param) BOOST_PP_CAT(Prop,LINK_PNAME(_param))
|
||||
//@}
|
||||
|
||||
#define LINK_PARAMS \
|
||||
LINK_PARAM(PLACEMENT)\
|
||||
LINK_PARAM(LINK_PLACEMENT)\
|
||||
LINK_PARAM(OBJECT)\
|
||||
LINK_PARAM(SUB_ELEMENT)\
|
||||
LINK_PARAM(TRANSFORM)\
|
||||
LINK_PARAM(SCALE)\
|
||||
LINK_PARAM(SCALE_VECTOR)\
|
||||
LINK_PARAM(PLACEMENTS)\
|
||||
LINK_PARAM(SCALES)\
|
||||
LINK_PARAM(VISIBILITIES)\
|
||||
LINK_PARAM(COUNT)\
|
||||
LINK_PARAM(ELEMENTS)\
|
||||
LINK_PARAM(SHOW_ELEMENT)\
|
||||
LINK_PARAM(MODE)\
|
||||
LINK_PARAM(COLORED_ELEMENTS)
|
||||
|
||||
enum PropIndex {
|
||||
#define LINK_PINDEX_DEFINE(_1,_2,_param) LINK_PINDEX(_param),
|
||||
|
||||
// defines Prop##Name enumeration value
|
||||
BOOST_PP_SEQ_FOR_EACH(LINK_PINDEX_DEFINE,_,LINK_PARAMS)
|
||||
PropMax
|
||||
};
|
||||
|
||||
virtual void setProperty(int idx, Property *prop);
|
||||
Property *getProperty(int idx);
|
||||
Property *getProperty(const char *);
|
||||
|
||||
struct PropInfo {
|
||||
int index;
|
||||
const char *name;
|
||||
Base::Type type;
|
||||
const char *doc;
|
||||
|
||||
PropInfo(int index, const char *name,Base::Type type,const char *doc)
|
||||
:index(index),name(name),type(type),doc(doc)
|
||||
{}
|
||||
|
||||
PropInfo() {}
|
||||
};
|
||||
|
||||
#define LINK_PROP_INFO(_1,_var,_param) \
|
||||
_var.push_back(PropInfo(BOOST_PP_CAT(Prop,LINK_PNAME(_param)),\
|
||||
BOOST_PP_STRINGIZE(LINK_PNAME(_param)),\
|
||||
LINK_PPTYPE(_param)::getClassTypeId(), \
|
||||
LINK_PDOC(_param)));
|
||||
|
||||
virtual const std::vector<PropInfo> &getPropertyInfo() const;
|
||||
|
||||
typedef std::map<std::string, PropInfo> PropInfoMap;
|
||||
virtual const PropInfoMap &getPropertyInfoMap() const;
|
||||
|
||||
#define LINK_PROP_GET(_1,_2,_param) \
|
||||
LINK_PTYPE(_param) BOOST_PP_SEQ_CAT((get)(LINK_PNAME(_param))(Value)) () const {\
|
||||
auto prop = props[LINK_PINDEX(_param)];\
|
||||
if(!prop) return LINK_PDEF(_param);\
|
||||
return static_cast<const LINK_PPTYPE(_param) *>(prop)->getValue();\
|
||||
}\
|
||||
const LINK_PPTYPE(_param) *BOOST_PP_SEQ_CAT((get)(LINK_PNAME(_param))(Property)) () const {\
|
||||
auto prop = props[LINK_PINDEX(_param)];\
|
||||
return static_cast<const LINK_PPTYPE(_param) *>(prop);\
|
||||
}\
|
||||
LINK_PPTYPE(_param) *BOOST_PP_SEQ_CAT((get)(LINK_PNAME(_param))(Property)) () {\
|
||||
auto prop = props[LINK_PINDEX(_param)];\
|
||||
return static_cast<LINK_PPTYPE(_param) *>(prop);\
|
||||
}\
|
||||
|
||||
// defines get##Name() and get##Name##Property() accessor
|
||||
BOOST_PP_SEQ_FOR_EACH(LINK_PROP_GET,_,LINK_PARAMS)
|
||||
|
||||
PropertyLinkList *_getElementListProperty() const;
|
||||
const std::vector<App::DocumentObject*> &_getElementListValue() const;
|
||||
|
||||
PropertyBool *_getShowElementProperty() const;
|
||||
bool _getShowElementValue() const;
|
||||
|
||||
PropertyInteger *_getElementCountProperty() const;
|
||||
int _getElementCountValue() const;
|
||||
|
||||
std::vector<DocumentObject*> getLinkedChildren(bool filter=true) const;
|
||||
|
||||
const char *flattenSubname(const char *subname) const;
|
||||
void expandSubname(std::string &subname) const;
|
||||
|
||||
DocumentObject *getLink(int depth=0) const;
|
||||
|
||||
Base::Matrix4D getTransform(bool transform) const;
|
||||
Base::Vector3d getScaleVector() const;
|
||||
|
||||
App::GroupExtension *linkedPlainGroup() const;
|
||||
|
||||
bool linkTransform() const;
|
||||
|
||||
const char *getSubName() const {
|
||||
parseSubName();
|
||||
return mySubName.size()?mySubName.c_str():0;
|
||||
}
|
||||
const char *getSubElement() const {
|
||||
parseSubName();
|
||||
return mySubElement.size()?mySubElement.c_str():0;
|
||||
}
|
||||
|
||||
bool extensionGetSubObject(DocumentObject *&ret, const char *subname,
|
||||
PyObject **pyObj=0, Base::Matrix4D *mat=0, bool transform=false, int depth=0) const override;
|
||||
|
||||
bool extensionGetSubObjects(std::vector<std::string>&ret, int reason) const override;
|
||||
|
||||
bool extensionGetLinkedObject(DocumentObject *&ret,
|
||||
bool recurse, Base::Matrix4D *mat, bool transform, int depth) const override;
|
||||
|
||||
virtual App::DocumentObjectExecReturn *extensionExecute(void) override;
|
||||
virtual short extensionMustExecute(void) override;
|
||||
virtual void extensionOnChanged(const Property* p) override;
|
||||
virtual void onExtendedUnsetupObject () override;
|
||||
virtual void onExtendedDocumentRestored() override;
|
||||
|
||||
virtual int extensionSetElementVisible(const char *, bool) override;
|
||||
virtual int extensionIsElementVisible(const char *) override;
|
||||
virtual bool extensionHasChildElement() const override;
|
||||
|
||||
virtual PyObject* getExtensionPyObject(void) override;
|
||||
|
||||
virtual Property *extensionGetPropertyByName(const char* name) const override;
|
||||
|
||||
static int getArrayIndex(const char *subname, const char **psubname=0);
|
||||
int getElementIndex(const char *subname, const char **psubname=0) const;
|
||||
void elementNameFromIndex(int idx, std::ostream &ss) const;
|
||||
|
||||
DocumentObject *getContainer();
|
||||
const DocumentObject *getContainer() const;
|
||||
|
||||
void setLink(int index, DocumentObject *obj, const char *subname=0,
|
||||
const std::vector<std::string> &subs = std::vector<std::string>());
|
||||
|
||||
DocumentObject *getTrueLinkedObject(bool recurse,
|
||||
Base::Matrix4D *mat=0,int depth=0, bool noElement=false) const;
|
||||
|
||||
typedef std::map<const Property*,std::pair<LinkBaseExtension*,int> > LinkPropMap;
|
||||
|
||||
bool hasPlacement() const {
|
||||
return getLinkPlacementProperty() || getPlacementProperty();
|
||||
}
|
||||
|
||||
void cacheChildLabel(int enable=-1) const;
|
||||
|
||||
protected:
|
||||
void parseSubName() const;
|
||||
void update(App::DocumentObject *parent, const Property *prop);
|
||||
void syncElementList();
|
||||
void detachElement(App::DocumentObject *obj);
|
||||
void checkGeoElementMap(const App::DocumentObject *obj,
|
||||
const App::DocumentObject *linked, PyObject **pyObj, const char *postfix) const;
|
||||
void updateGroup();
|
||||
void slotChangedPlainGroup(const App::DocumentObject &, const App::Property &);
|
||||
|
||||
protected:
|
||||
std::vector<Property *> props;
|
||||
std::unordered_set<const App::DocumentObject*> myHiddenElements;
|
||||
mutable std::string mySubElement;
|
||||
mutable std::string mySubName;
|
||||
|
||||
std::unordered_map<const App::DocumentObject*,
|
||||
boost::signals2::scoped_connection> plainGroupConns;
|
||||
|
||||
mutable std::unordered_map<std::string,int> myLabelCache; // for label based subname lookup
|
||||
mutable bool enableLabelCache;
|
||||
|
||||
long myOwner;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef ExtensionPythonT<LinkBaseExtension> LinkBaseExtensionPython;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class AppExport LinkExtension : public LinkBaseExtension
|
||||
{
|
||||
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::LinkExtension);
|
||||
typedef LinkBaseExtension inherited;
|
||||
|
||||
public:
|
||||
LinkExtension();
|
||||
virtual ~LinkExtension();
|
||||
|
||||
/** \name Helpers for defining extended parameter
|
||||
*
|
||||
* extended parameter definition
|
||||
* (Name, Type, Property_Type, Default, Document, Property_Name,
|
||||
* Derived_Property_Type, App_Property_Type, Group)
|
||||
*
|
||||
* This helper simply reuses Name as Property_Name, Property_Type as
|
||||
* Derived_Property_type, Prop_None as App_Propert_Type
|
||||
*
|
||||
* Note: Because PropertyView will merge linked object's properties into
|
||||
* ours, we set the default group name as ' Link' with a leading space to
|
||||
* try to make our group before others
|
||||
*/
|
||||
//@{
|
||||
|
||||
#define LINK_ENAME(_param) BOOST_PP_TUPLE_ELEM(5,_param)
|
||||
#define LINK_ETYPE(_param) BOOST_PP_TUPLE_ELEM(6,_param)
|
||||
#define LINK_EPTYPE(_param) BOOST_PP_TUPLE_ELEM(7,_param)
|
||||
#define LINK_EGROUP(_param) BOOST_PP_TUPLE_ELEM(8,_param)
|
||||
|
||||
#define _LINK_PROP_ADD(_add_property, _param) \
|
||||
_add_property(BOOST_PP_STRINGIZE(LINK_ENAME(_param)),LINK_ENAME(_param),\
|
||||
(LINK_PDEF(_param)),LINK_EGROUP(_param),LINK_EPTYPE(_param),LINK_PDOC(_param));\
|
||||
setProperty(LINK_PINDEX(_param),&LINK_ENAME(_param));
|
||||
|
||||
#define LINK_PROP_ADD(_1,_2,_param) \
|
||||
_LINK_PROP_ADD(_ADD_PROPERTY_TYPE,_param);
|
||||
|
||||
#define LINK_PROP_ADD_EXTENSION(_1,_2,_param) \
|
||||
_LINK_PROP_ADD(_EXTENSION_ADD_PROPERTY_TYPE,_param);
|
||||
|
||||
#define LINK_PROPS_ADD(_seq) \
|
||||
BOOST_PP_SEQ_FOR_EACH(LINK_PROP_ADD,_,_seq)
|
||||
|
||||
#define LINK_PROPS_ADD_EXTENSION(_seq) \
|
||||
BOOST_PP_SEQ_FOR_EACH(LINK_PROP_ADD_EXTENSION,_,_seq)
|
||||
|
||||
#define _LINK_PROP_SET(_1,_2,_param) \
|
||||
setProperty(LINK_PINDEX(_param),&LINK_ENAME(_param));
|
||||
|
||||
#define LINK_PROPS_SET(_seq) BOOST_PP_SEQ_FOR_EACH(_LINK_PROP_SET,_,_seq)
|
||||
|
||||
/// Helper for defining default extended parameter
|
||||
#define _LINK_PARAM_EXT(_name,_type,_ptype,_def,_doc,...) \
|
||||
((_name,_type,_ptype,_def,_doc,_name,_ptype,App::Prop_None," Link"))
|
||||
|
||||
/** Define default extended parameter
|
||||
* It simply reuses Name as Property_Name, Property_Type as
|
||||
* Derived_Property_Type, and App::Prop_None as App::PropertyType
|
||||
*/
|
||||
#define LINK_PARAM_EXT(_param) BOOST_PP_EXPAND(_LINK_PARAM_EXT LINK_PARAM_##_param())
|
||||
|
||||
/// Helper for extended parameter with app property type
|
||||
#define _LINK_PARAM_EXT_ATYPE(_name,_type,_ptype,_def,_doc,_atype) \
|
||||
((_name,_type,_ptype,_def,_doc,_name,_ptype,_atype," Link"))
|
||||
|
||||
/// Define extended parameter with app property type
|
||||
#define LINK_PARAM_EXT_ATYPE(_param,_atype) \
|
||||
BOOST_PP_EXPAND(_LINK_PARAM_EXT_ATYPE LINK_PARAM_##_param(_atype))
|
||||
|
||||
/// Helper for extended parameter with derived property type
|
||||
#define _LINK_PARAM_EXT_TYPE(_name,_type,_ptype,_def,_doc,_dtype) \
|
||||
((_name,_type,_ptype,_def,_doc,_name,_dtype,App::Prop_None," Link"))
|
||||
|
||||
/// Define extended parameter with derived property type
|
||||
#define LINK_PARAM_EXT_TYPE(_param,_dtype) \
|
||||
BOOST_PP_EXPAND(_LINK_PARAM_EXT_TYPE LINK_PARAM_##_param(_dtype))
|
||||
|
||||
/// Helper for extended parameter with a different property name
|
||||
#define _LINK_PARAM_EXT_NAME(_name,_type,_ptype,_def,_doc,_pname) \
|
||||
((_name,_type,_ptype,_def,_doc,_pname,_ptype,App::Prop_None," Link"))
|
||||
|
||||
/// Define extended parameter with a different property name
|
||||
#define LINK_PARAM_EXT_NAME(_param,_pname) BOOST_PP_EXPAND(_LINK_PARAM_EXT_NAME LINK_PARAM_##_param(_pname))
|
||||
//@}
|
||||
|
||||
#define LINK_PARAMS_EXT \
|
||||
LINK_PARAM_EXT(SCALE)\
|
||||
LINK_PARAM_EXT(SCALES)\
|
||||
LINK_PARAM_EXT(VISIBILITIES)\
|
||||
LINK_PARAM_EXT(PLACEMENTS)\
|
||||
LINK_PARAM_EXT(ELEMENTS)
|
||||
|
||||
#define LINK_PROP_DEFINE(_1,_2,_param) LINK_ETYPE(_param) LINK_ENAME(_param);
|
||||
#define LINK_PROPS_DEFINE(_seq) BOOST_PP_SEQ_FOR_EACH(LINK_PROP_DEFINE,_,_seq)
|
||||
|
||||
// defines the actual properties
|
||||
LINK_PROPS_DEFINE(LINK_PARAMS_EXT)
|
||||
|
||||
void onExtendedDocumentRestored() override {
|
||||
LINK_PROPS_SET(LINK_PARAMS_EXT);
|
||||
inherited::onExtendedDocumentRestored();
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef ExtensionPythonT<LinkExtension> LinkExtensionPython;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class AppExport Link : public App::DocumentObject, public App::LinkExtension
|
||||
{
|
||||
PROPERTY_HEADER_WITH_EXTENSIONS(App::Link);
|
||||
typedef App::DocumentObject inherited;
|
||||
public:
|
||||
|
||||
#define LINK_PARAMS_LINK \
|
||||
LINK_PARAM_EXT_TYPE(OBJECT, App::PropertyXLink)\
|
||||
LINK_PARAM_EXT(TRANSFORM)\
|
||||
LINK_PARAM_EXT(LINK_PLACEMENT)\
|
||||
LINK_PARAM_EXT(PLACEMENT)\
|
||||
LINK_PARAM_EXT(SUB_ELEMENT)\
|
||||
LINK_PARAM_EXT(SHOW_ELEMENT)\
|
||||
LINK_PARAM_EXT_TYPE(COUNT,App::PropertyIntegerConstraint)\
|
||||
LINK_PARAM_EXT_ATYPE(COLORED_ELEMENTS,App::Prop_Hidden)
|
||||
|
||||
LINK_PROPS_DEFINE(LINK_PARAMS_LINK)
|
||||
|
||||
Link(void);
|
||||
|
||||
const char* getViewProviderName(void) const override{
|
||||
return "Gui::ViewProviderLink";
|
||||
}
|
||||
|
||||
void onDocumentRestored() override {
|
||||
LINK_PROPS_SET(LINK_PARAMS_LINK);
|
||||
inherited::onDocumentRestored();
|
||||
}
|
||||
|
||||
bool canLinkProperties() const override;
|
||||
};
|
||||
|
||||
typedef App::FeaturePythonT<Link> LinkPython;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class AppExport LinkElement : public App::DocumentObject, public App::LinkBaseExtension {
|
||||
PROPERTY_HEADER_WITH_EXTENSIONS(App::LinkElement);
|
||||
typedef App::DocumentObject inherited;
|
||||
public:
|
||||
|
||||
#define LINK_PARAMS_ELEMENT \
|
||||
LINK_PARAM_EXT(SCALE)\
|
||||
LINK_PARAM_EXT_TYPE(OBJECT, App::PropertyXLink)\
|
||||
LINK_PARAM_EXT(TRANSFORM) \
|
||||
LINK_PARAM_EXT(LINK_PLACEMENT)\
|
||||
LINK_PARAM_EXT(PLACEMENT)\
|
||||
LINK_PARAM_EXT(SUB_ELEMENT)
|
||||
|
||||
// defines the actual properties
|
||||
LINK_PROPS_DEFINE(LINK_PARAMS_ELEMENT)
|
||||
|
||||
LinkElement();
|
||||
const char* getViewProviderName(void) const override{
|
||||
return "Gui::ViewProviderLink";
|
||||
}
|
||||
|
||||
void onDocumentRestored() override {
|
||||
LINK_PROPS_SET(LINK_PARAMS_ELEMENT);
|
||||
inherited::onDocumentRestored();
|
||||
}
|
||||
|
||||
bool canDelete() const;
|
||||
};
|
||||
|
||||
typedef App::FeaturePythonT<LinkElement> LinkElementPython;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class AppExport LinkGroup : public App::DocumentObject, public App::LinkBaseExtension {
|
||||
PROPERTY_HEADER_WITH_EXTENSIONS(App::LinkGroup);
|
||||
typedef App::DocumentObject inherited;
|
||||
public:
|
||||
|
||||
#define LINK_PARAMS_GROUP \
|
||||
LINK_PARAM_EXT(ELEMENTS)\
|
||||
LINK_PARAM_EXT(PLACEMENT)\
|
||||
LINK_PARAM_EXT(VISIBILITIES)\
|
||||
LINK_PARAM_EXT(MODE)\
|
||||
LINK_PARAM_EXT_ATYPE(COLORED_ELEMENTS,App::Prop_Hidden)
|
||||
|
||||
// defines the actual properties
|
||||
LINK_PROPS_DEFINE(LINK_PARAMS_GROUP)
|
||||
|
||||
LinkGroup();
|
||||
|
||||
const char* getViewProviderName(void) const override{
|
||||
return "Gui::ViewProviderLink";
|
||||
}
|
||||
|
||||
void onDocumentRestored() override {
|
||||
LINK_PROPS_SET(LINK_PARAMS_GROUP);
|
||||
inherited::onDocumentRestored();
|
||||
}
|
||||
};
|
||||
|
||||
typedef App::FeaturePythonT<LinkGroup> LinkGroupPython;
|
||||
|
||||
} //namespace App
|
||||
|
||||
|
||||
#endif // APP_LINK_H
|
||||
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
|
||||
<PythonExport
|
||||
Father="DocumentObjectExtensionPy"
|
||||
Name="LinkBaseExtensionPy"
|
||||
TwinPointer="LinkBaseExtension"
|
||||
Twin="LinkBaseExtension"
|
||||
Include="App/Link.h"
|
||||
Namespace="App"
|
||||
FatherInclude="App/DocumentObjectExtensionPy.h"
|
||||
FatherNamespace="App">
|
||||
<Documentation>
|
||||
<Author Licence="LGPL" Name="Zheng, Lei" EMail="[email protected]" />
|
||||
<UserDocu>Link extension base class</UserDocu>
|
||||
</Documentation>
|
||||
<Methode Name="configLinkProperty" Keyword="true">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
configLinkProperty(key=val,...): property configuration
|
||||
configLinkProperty(key,...): property configuration with default name
|
||||
|
||||
This methode is here to impelement what I called Property Design
|
||||
Pattern. The extension operates on a predefined set of properties,
|
||||
but it relies on the extended object to supply the actual property by
|
||||
calling this methode. You can choose a sub set of functionality of
|
||||
this extension by supplying only some of the supported properties.
|
||||
|
||||
The 'key' are names used to refer to properties supported by this
|
||||
extension, and 'val' is the actual name of the property of your
|
||||
object. You can obtain the key names and expected types using
|
||||
getLinkPropertyInfo(). You can use property of derived type when
|
||||
calling configLinkProperty(). Other types will cause exception to
|
||||
ben thrown. The actual properties supported may be different
|
||||
depending on the actual extension object underlying this python
|
||||
object.
|
||||
|
||||
If 'val' is omitted, i.e. calling configLinkProperty(key,...), then
|
||||
it is assumed the the actually property name is the same as 'key'
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getLinkExtProperty">
|
||||
<Documentation>
|
||||
<UserDocu>getLinkExtProperty(name): return the property value by its predefined name </UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getLinkExtPropertyName">
|
||||
<Documentation>
|
||||
<UserDocu>getLinkExtPropertyName(name): lookup the property name by its predefined name </UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getLinkPropertyInfo">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
getLinkPropertyInfo(): return a tuple of (name,type,doc) for all supported properties.
|
||||
|
||||
getLinkPropertyInfo(index): return (name,type,doc) of a specific property
|
||||
|
||||
getLinkPropertyInfo(name): return (type,doc) of a specific property
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="setLink">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
setLink(obj,subName=None,subElements=None): Set link object.
|
||||
|
||||
setLink([obj,...]),
|
||||
setLink([(obj,subName,subElements),...]),
|
||||
setLink({index:obj,...}),
|
||||
setLink({index:(obj,subName,subElements),...}): set link element of a link group.
|
||||
|
||||
obj (DocumentObject): the object to link to. If this is None, then the link is cleared
|
||||
|
||||
subName (String): Dot separated object path.
|
||||
|
||||
subElements (String|tuple(String)): non-object sub-elements, e.g. Face1, Edge2.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="cacheChildLabel">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
cacheChildLabel(enable=True): enable/disable child label cache
|
||||
|
||||
The cache is not updated on child label change for performance reason. You must
|
||||
call this function on any child label change
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="flattenSubname">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
flattenSubname(subname) -> string
|
||||
|
||||
Return a flattened subname in case it references an object inside a linked plain group
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="expandSubname">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
expandSubname(subname) -> string
|
||||
|
||||
Return an expanded subname in case it references an object inside a linked plain group
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<CustomAttributes />
|
||||
<Attribute Name="LinkedChildren" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Return a flattened (in case grouped by plain group) list of linked children</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="LinkedChildren" Type="List"/>
|
||||
</Attribute>
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
@@ -0,0 +1,316 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <sstream>
|
||||
#endif
|
||||
|
||||
#include "DocumentObjectPy.h"
|
||||
#include "LinkBaseExtensionPy.h"
|
||||
#include "LinkBaseExtensionPy.cpp"
|
||||
|
||||
using namespace App;
|
||||
|
||||
// returns a string which represent the object e.g. when printed in python
|
||||
std::string LinkBaseExtensionPy::representation(void) const
|
||||
{
|
||||
std::ostringstream str;
|
||||
str << "<" << getLinkBaseExtensionPtr()->getExtensionClassTypeId().getName() << ">";
|
||||
return str.str();
|
||||
}
|
||||
|
||||
typedef std::map<std::string, std::pair<int,Property*> > PropTmpMap;
|
||||
typedef std::map<std::string, Property*> PropMap;
|
||||
|
||||
static bool getProperty(PropTmpMap &props, const LinkBaseExtension::PropInfoMap &infoMap,
|
||||
const PropMap &propMap, PyObject *key, PyObject *value)
|
||||
{
|
||||
std::ostringstream str;
|
||||
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if(!PyString_Check(key)) {
|
||||
PyErr_SetString(PyExc_TypeError, "key must be string");
|
||||
return false;
|
||||
}
|
||||
const char *keyStr = PyString_AsString(key);
|
||||
#else
|
||||
if(!PyUnicode_Check(key)) {
|
||||
PyErr_SetString(PyExc_TypeError, "key must be a unicode string");
|
||||
return false;
|
||||
}
|
||||
const char *keyStr = PyUnicode_AsUTF8(key);
|
||||
#endif
|
||||
auto it = infoMap.find(keyStr);
|
||||
if(it == infoMap.end()){
|
||||
str << "unknown key '" << keyStr << "'";
|
||||
PyErr_SetString(PyExc_KeyError, str.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *valStr = 0;
|
||||
if(key == value)
|
||||
valStr = keyStr;
|
||||
else if (value!=Py_None) {
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if(!PyString_Check(value)) {
|
||||
PyErr_SetString(PyExc_TypeError, "value must be string");
|
||||
return false;
|
||||
}
|
||||
valStr = PyString_AsString(value);
|
||||
#else
|
||||
if(!PyUnicode_Check(value)) {
|
||||
PyErr_SetString(PyExc_TypeError, "value must be unicode string");
|
||||
return false;
|
||||
}
|
||||
valStr = PyUnicode_AsUTF8(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
App::Property *prop = 0;
|
||||
auto &info = it->second;
|
||||
if(valStr) {
|
||||
auto pIt = propMap.find(valStr);
|
||||
if(pIt == propMap.end()) {
|
||||
str << "cannot find property '" << valStr << "'";
|
||||
PyErr_SetString(PyExc_ValueError, str.str().c_str());
|
||||
return false;
|
||||
}
|
||||
prop = pIt->second;
|
||||
if(!prop->isDerivedFrom(info.type)) {
|
||||
str << "expect property '" << keyStr << "(" << valStr
|
||||
<< ") to be derived from '" << info.type.getName()
|
||||
<< "', instead of '" << prop->getTypeId().getName() << "'";
|
||||
PyErr_SetString(PyExc_TypeError, str.str().c_str());
|
||||
}
|
||||
}
|
||||
props[keyStr] = std::make_pair(info.index,prop);
|
||||
return true;
|
||||
}
|
||||
|
||||
PyObject* LinkBaseExtensionPy::configLinkProperty(PyObject *args, PyObject *keywds) {
|
||||
auto ext = getLinkBaseExtensionPtr();
|
||||
const auto &info = ext->getPropertyInfoMap();
|
||||
|
||||
PropMap propMap;
|
||||
ext->getExtendedContainer()->getPropertyMap(propMap);
|
||||
|
||||
PropTmpMap props;
|
||||
|
||||
if(args && PyTuple_Check(args)) {
|
||||
for(Py_ssize_t pos=0;pos<PyTuple_GET_SIZE(args);++pos) {
|
||||
auto key = PyTuple_GET_ITEM(args,pos);
|
||||
if(!getProperty(props,info,propMap,key,key))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if(keywds && PyDict_Check(keywds)) {
|
||||
PyObject *key, *value;
|
||||
Py_ssize_t pos = 0;
|
||||
while (PyDict_Next(keywds, &pos, &key, &value)) {
|
||||
if(!getProperty(props,info,propMap,key,value))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
for(auto &v : props)
|
||||
ext->setProperty(v.second.first,v.second.second);
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* LinkBaseExtensionPy::getLinkExtProperty(PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
if(!PyArg_ParseTuple(args,"s",&name))
|
||||
return 0;
|
||||
auto prop = getLinkBaseExtensionPtr()->getProperty(name);
|
||||
if(!prop) {
|
||||
PyErr_SetString(PyExc_AttributeError, "unknown property name");
|
||||
return 0;
|
||||
}
|
||||
return prop->getPyObject();
|
||||
}
|
||||
|
||||
PyObject* LinkBaseExtensionPy::getLinkExtPropertyName(PyObject *args) {
|
||||
const char *name;
|
||||
if(!PyArg_ParseTuple(args,"s",&name))
|
||||
return 0;
|
||||
auto prop = getLinkBaseExtensionPtr()->getProperty(name);
|
||||
if(!prop) {
|
||||
PyErr_SetString(PyExc_AttributeError, "unknown property name");
|
||||
return 0;
|
||||
}
|
||||
auto container = getLinkBaseExtensionPtr()->getExtendedContainer();
|
||||
if(!container) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "no extended container");
|
||||
return 0;
|
||||
}
|
||||
name = container->getPropertyName(prop);
|
||||
if(!name) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "cannot find property name");
|
||||
return 0;
|
||||
}
|
||||
return Py::new_reference_to(Py::String(name));
|
||||
}
|
||||
|
||||
PyObject* LinkBaseExtensionPy::getLinkPropertyInfo(PyObject *args)
|
||||
{
|
||||
auto ext = getLinkBaseExtensionPtr();
|
||||
|
||||
const auto &infos = ext->getPropertyInfo();
|
||||
|
||||
if(PyArg_ParseTuple(args,"")) {
|
||||
Py::Tuple ret(infos.size());
|
||||
int i=0;
|
||||
for(const auto &info : infos) {
|
||||
ret.setItem(i++,Py::TupleN(Py::String(info.name),
|
||||
Py::String(info.type.getName()),Py::String(info.doc)));
|
||||
}
|
||||
return Py::new_reference_to(ret);
|
||||
}
|
||||
|
||||
short index = 0;
|
||||
if(PyArg_ParseTuple(args,"h",&index)) {
|
||||
if(index<0 || index>=(int)infos.size()) {
|
||||
PyErr_SetString(PyExc_ValueError, "index out of range");
|
||||
return 0;
|
||||
}
|
||||
Py::TupleN ret(Py::String(infos[index].name),
|
||||
Py::String(infos[index].type.getName()),Py::String(infos[index].doc));
|
||||
return Py::new_reference_to(ret);
|
||||
}
|
||||
|
||||
char *name;
|
||||
if(PyArg_ParseTuple(args,"s",&name)) {
|
||||
for(int i=0;i<(int)infos.size();++i) {
|
||||
if(strcmp(infos[i].name,name)==0) {
|
||||
Py::TupleN ret(Py::String(infos[i].type.getName()),
|
||||
Py::String(infos[i].doc));
|
||||
return Py::new_reference_to(ret);
|
||||
}
|
||||
}
|
||||
PyErr_SetString(PyExc_ValueError, "unknown property name");
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyErr_SetString(PyExc_ValueError, "invalid arguments");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void parseLink(LinkBaseExtension *ext, int index, PyObject *value) {
|
||||
App::DocumentObject *obj = 0;
|
||||
PropertyStringList subs;
|
||||
PropertyString sub;
|
||||
if(value!=Py_None) {
|
||||
if(PyObject_TypeCheck(value,&DocumentObjectPy::Type)) {
|
||||
obj = static_cast<DocumentObjectPy*>(value)->getDocumentObjectPtr();
|
||||
}else if(!PySequence_Check(value))
|
||||
throw Base::TypeError("Expects type of DocumentObject or sequence");
|
||||
else{
|
||||
Py::Sequence seq(value);
|
||||
if(seq[0].ptr() != Py_None) {
|
||||
if(!PyObject_TypeCheck(seq[0].ptr(),&DocumentObjectPy::Type))
|
||||
throw Base::TypeError("Expects the first argument to be DocumentObject in sequence");
|
||||
obj = static_cast<DocumentObjectPy*>(seq[0].ptr())->getDocumentObjectPtr();
|
||||
if(seq.size()>1) {
|
||||
sub.setPyObject(seq[1].ptr());
|
||||
if(seq.size()>2)
|
||||
subs.setPyObject(seq[2].ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ext->setLink(index,obj,sub.getValue(),subs.getValue());
|
||||
}
|
||||
|
||||
PyObject* LinkBaseExtensionPy::setLink(PyObject *_args)
|
||||
{
|
||||
Py::Sequence args(_args);
|
||||
PY_TRY {
|
||||
auto ext = getLinkBaseExtensionPtr();
|
||||
PyObject *pcObj = args.size()?args[0].ptr():Py_None;
|
||||
if(pcObj == Py_None) {
|
||||
ext->setLink(-1,0);
|
||||
}else if(PyDict_Check(pcObj)) {
|
||||
PyObject *key, *value;
|
||||
Py_ssize_t pos = 0;
|
||||
while(PyDict_Next(pcObj, &pos, &key, &value))
|
||||
parseLink(ext,Py::Int(key),value);
|
||||
}else if(PySequence_Check(pcObj)) {
|
||||
ext->setLink(-1,0);
|
||||
Py::Sequence seq(pcObj);
|
||||
for(size_t i=0;i<seq.size();++i)
|
||||
parseLink(ext,i,seq[i].ptr());
|
||||
}else
|
||||
parseLink(ext,-1,_args);
|
||||
|
||||
Py_Return;
|
||||
}PY_CATCH
|
||||
}
|
||||
|
||||
PyObject* LinkBaseExtensionPy::cacheChildLabel(PyObject *args) {
|
||||
PyObject *enable = Py_True;
|
||||
if(!PyArg_ParseTuple(args,"|O",&enable))
|
||||
return 0;
|
||||
PY_TRY {
|
||||
getLinkBaseExtensionPtr()->cacheChildLabel(PyObject_IsTrue(enable)?-1:0);
|
||||
Py_Return;
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject* LinkBaseExtensionPy::flattenSubname(PyObject *args) {
|
||||
const char *subname;
|
||||
if(!PyArg_ParseTuple(args,"s",&subname))
|
||||
return 0;
|
||||
PY_TRY {
|
||||
return Py::new_reference_to(Py::String(
|
||||
getLinkBaseExtensionPtr()->flattenSubname(subname)));
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject* LinkBaseExtensionPy::expandSubname(PyObject *args) {
|
||||
const char *subname;
|
||||
if(!PyArg_ParseTuple(args,"s",&subname))
|
||||
return 0;
|
||||
PY_TRY {
|
||||
std::string sub(subname);
|
||||
getLinkBaseExtensionPtr()->expandSubname(sub);
|
||||
return Py::new_reference_to(Py::String(sub));
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
Py::List LinkBaseExtensionPy::getLinkedChildren() const {
|
||||
Py::List ret;
|
||||
for(auto o : getLinkBaseExtensionPtr()->getLinkedChildren(true))
|
||||
ret.append(Py::asObject(o->getPyObject()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyObject *LinkBaseExtensionPy::getCustomAttributes(const char* /*attr*/) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LinkBaseExtensionPy::setCustomAttributes(const char* /*attr*/, PyObject * /*obj*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -282,6 +282,18 @@ public:
|
||||
float transparency;
|
||||
//@}
|
||||
|
||||
bool operator==(const Material& m) const
|
||||
{
|
||||
return _matType!=m._matType || shininess!=m.shininess ||
|
||||
transparency!=m.transparency || ambientColor!=m.ambientColor ||
|
||||
diffuseColor!=m.diffuseColor || specularColor!=m.specularColor ||
|
||||
emissiveColor!=m.emissiveColor;
|
||||
}
|
||||
bool operator!=(const Material& m) const
|
||||
{
|
||||
return !operator==(m);
|
||||
}
|
||||
|
||||
private:
|
||||
MaterialType _matType;
|
||||
};
|
||||
|
||||
@@ -33,7 +33,7 @@ using namespace App;
|
||||
|
||||
PyObject *MaterialPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper
|
||||
{
|
||||
// create a new instance of MaterialPy and the Twin object
|
||||
// create a new instance of MaterialPy and the Twin object
|
||||
return new MaterialPy(new Material);
|
||||
}
|
||||
|
||||
@@ -45,11 +45,11 @@ int MaterialPy::PyInit(PyObject* args, PyObject* kwds)
|
||||
PyObject* specular = 0;
|
||||
PyObject* emissive = 0;
|
||||
PyObject* shininess = 0;
|
||||
PyObject* transpareny = 0;
|
||||
PyObject* transparency = 0;
|
||||
static char* kwds_colors[] = { "DiffuseColor", "AmbientColor", "SpecularColor", "EmissiveColor", "Shininess", "Transparency", NULL };
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOOO", kwds_colors,
|
||||
&diffuse, &ambient, &specular, &emissive, &shininess, &transpareny))
|
||||
&diffuse, &ambient, &specular, &emissive, &shininess, &transparency))
|
||||
return -1;
|
||||
|
||||
if (diffuse) {
|
||||
@@ -72,8 +72,8 @@ int MaterialPy::PyInit(PyObject* args, PyObject* kwds)
|
||||
setShininess(Py::Float(shininess));
|
||||
}
|
||||
|
||||
if (transpareny) {
|
||||
setTransparency(Py::Float(transpareny));
|
||||
if (transparency) {
|
||||
setTransparency(Py::Float(transparency));
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -88,8 +88,8 @@ std::string MaterialPy::representation(void) const
|
||||
PyObject* MaterialPy::set(PyObject * args)
|
||||
{
|
||||
char *pstr;
|
||||
if (!PyArg_ParseTuple(args, "s", &pstr)) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
if (!PyArg_ParseTuple(args, "s", &pstr)) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
|
||||
getMaterialPtr()->set(pstr);
|
||||
|
||||
@@ -207,5 +207,5 @@ PyObject *MaterialPy::getCustomAttributes(const char* /*attr*/) const
|
||||
|
||||
int MaterialPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
|
||||
{
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,14 @@ public:
|
||||
return true;
|
||||
}
|
||||
protected:
|
||||
|
||||
// It is not safe to change potential object name reference at this level.
|
||||
// For example, a LinkSub with sub element name Face1 may also be some
|
||||
// object's name that may potentially be mapped. In addition, with the
|
||||
// introduction of full quanlified SubName reference, the Sub value inside
|
||||
// LinkSub may require customized mapping. So we move the mapping logic to
|
||||
// various link property's Restore() function.
|
||||
#if 0
|
||||
void startElement(const XMLCh* const uri, const XMLCh* const localname,
|
||||
const XMLCh* const qname,
|
||||
const XERCES_CPP_NAMESPACE_QUALIFIER Attributes& attrs)
|
||||
@@ -107,6 +115,7 @@ protected:
|
||||
if (LocalName == "Property")
|
||||
propertyStack.pop();
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
std::map<std::string, std::string>& nameMap;
|
||||
|
||||
@@ -50,6 +50,8 @@ public:
|
||||
void SaveDocFile (Base::Writer & w) const;
|
||||
void RestoreDocFile(Base::Reader & r);
|
||||
|
||||
const std::map<std::string, std::string> &getNameMap() const {return nameMap;}
|
||||
|
||||
private:
|
||||
bool guiup;
|
||||
bool verbose;
|
||||
|
||||
+1173
-428
File diff suppressed because it is too large
Load Diff
+246
-44
@@ -24,39 +24,86 @@
|
||||
#ifndef APP_PATH_H
|
||||
#define APP_PATH_H
|
||||
|
||||
#include <climits>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <bitset>
|
||||
#ifndef BOOST_105400
|
||||
#include <boost/any.hpp>
|
||||
#else
|
||||
#include <boost_any_1_55.hpp>
|
||||
#endif
|
||||
#include <CXX/Objects.hxx>
|
||||
|
||||
namespace App
|
||||
{
|
||||
|
||||
using any = boost::any;
|
||||
|
||||
template<class T>
|
||||
inline const T &any_cast(const boost::any &value) {
|
||||
return boost::any_cast<const T&>(value);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T &any_cast(boost::any &value) {
|
||||
return boost::any_cast<T&>(value);
|
||||
}
|
||||
|
||||
class Property;
|
||||
class Document;
|
||||
class PropertyContainer;
|
||||
class DocumentObject;
|
||||
class ExpressionVisitor;
|
||||
|
||||
AppExport std::string quote(const std::string &input);
|
||||
AppExport std::string quote(const std::string &input, bool toPython=false);
|
||||
|
||||
// Unfortunately VS2013 does not support default move constructor, so we have
|
||||
// to implement them manually
|
||||
#define FC_DEFAULT_CTORS(_t) \
|
||||
_t(const _t &) = default;\
|
||||
_t &operator=(const _t &) = default;\
|
||||
_t(_t &&other) { *this = std::move(other); }\
|
||||
_t &operator=(_t &&other)
|
||||
|
||||
class AppExport ObjectIdentifier {
|
||||
|
||||
public:
|
||||
|
||||
class AppExport DocumentMapper {
|
||||
public:
|
||||
DocumentMapper(const std::map<std::string,std::string> &);
|
||||
~DocumentMapper();
|
||||
};
|
||||
|
||||
class String {
|
||||
friend class ObjectIdentifier;
|
||||
|
||||
public:
|
||||
|
||||
// Constructor
|
||||
String(const std::string & s = "", bool _isRealString = false, bool _forceIdentifier = false) : str(s), isString(_isRealString), forceIdentifier(_forceIdentifier) { }
|
||||
String(const std::string & s = "", bool _isRealString = false, bool _forceIdentifier = false)
|
||||
: str(s), isString(_isRealString), forceIdentifier(_forceIdentifier)
|
||||
{ }
|
||||
|
||||
String(std::string &&s, bool _isRealString = false, bool _forceIdentifier = false)
|
||||
: str(std::move(s)), isString(_isRealString), forceIdentifier(_forceIdentifier)
|
||||
{ }
|
||||
|
||||
FC_DEFAULT_CTORS(String) {
|
||||
str = std::move(other.str);
|
||||
isString = other.isString;
|
||||
forceIdentifier = other.forceIdentifier;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
/** Returns the string */
|
||||
std::string getString() const { return str; }
|
||||
const std::string &getString() const { return str; }
|
||||
|
||||
/** Return true is string need to be quoted */
|
||||
bool isRealString() const { return isString; }
|
||||
@@ -64,7 +111,7 @@ public:
|
||||
bool isForceIdentifier() const { return forceIdentifier; }
|
||||
|
||||
/** Returns a possibly quoted string */
|
||||
std::string toString() const;
|
||||
std::string toString(bool toPython=false) const;
|
||||
|
||||
// Operators
|
||||
|
||||
@@ -82,6 +129,8 @@ public:
|
||||
|
||||
bool operator>(const String & other) const { return str > other.str; }
|
||||
|
||||
void checkImport(const App::DocumentObject *owner,
|
||||
const App::DocumentObject *obj=0, String *objName=0);
|
||||
private:
|
||||
|
||||
std::string str;
|
||||
@@ -103,22 +152,38 @@ public:
|
||||
enum typeEnum {
|
||||
SIMPLE,
|
||||
MAP,
|
||||
ARRAY
|
||||
ARRAY,
|
||||
RANGE,
|
||||
} ;
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
FC_DEFAULT_CTORS(Component) {
|
||||
name = std::move(other.name);
|
||||
type = other.type;
|
||||
begin = other.begin;
|
||||
end = other.end;
|
||||
step = other.step;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Component(const String &_component, typeEnum _type = SIMPLE, int _index = -1, String _key = String());
|
||||
Component(const String &_name = String(), typeEnum _type=SIMPLE,
|
||||
int begin=INT_MAX, int end=INT_MAX, int step=1);
|
||||
Component(String &&_name, typeEnum _type=SIMPLE,
|
||||
int begin=INT_MAX, int end=INT_MAX, int step=1);
|
||||
|
||||
static Component SimpleComponent(const char * _component);
|
||||
|
||||
static Component SimpleComponent(const String & _component);
|
||||
static Component SimpleComponent(String &&_component);
|
||||
|
||||
static Component ArrayComponent(const String &_component, int _index);
|
||||
static Component ArrayComponent(int _index);
|
||||
|
||||
static Component MapComponent(const String &_component, const String &_key);
|
||||
static Component RangeComponent(int _begin, int _end = INT_MAX, int _step=1);
|
||||
|
||||
static Component MapComponent(const String &_key);
|
||||
static Component MapComponent(String &&_key);
|
||||
|
||||
// Type queries
|
||||
|
||||
@@ -128,93 +193,175 @@ public:
|
||||
|
||||
bool isArray() const { return type == ARRAY; }
|
||||
|
||||
bool isRange() const { return type == RANGE; }
|
||||
|
||||
// Accessors
|
||||
|
||||
std::string toString() const;
|
||||
void toString(std::ostream &ss, bool toPython=false) const;
|
||||
|
||||
std::string getName() const { return name.getString(); }
|
||||
const std::string &getName() const { return name.getString(); }
|
||||
|
||||
std::size_t getIndex() const { return static_cast<std::size_t>(index); }
|
||||
int getIndex() const {return begin;}
|
||||
size_t getIndex(size_t count) const;
|
||||
|
||||
String getKey() const { return key; }
|
||||
|
||||
bool getKeyIsString() const { return keyIsString; }
|
||||
int getBegin() const { return begin; }
|
||||
int getEnd() const { return end; }
|
||||
int getStep() const { return step; }
|
||||
|
||||
// Operators
|
||||
|
||||
bool operator==(const Component & other) const;
|
||||
bool operator<(const Component & other) const;
|
||||
|
||||
Py::Object get(const Py::Object &pyobj) const;
|
||||
void set(Py::Object &pyobj, const Py::Object &value) const;
|
||||
void del(Py::Object &pyobj) const;
|
||||
|
||||
private:
|
||||
|
||||
String name;
|
||||
typeEnum type;
|
||||
int index;
|
||||
String key;
|
||||
bool keyIsString;
|
||||
|
||||
int begin;
|
||||
int end;
|
||||
int step;
|
||||
friend class ObjectIdentifier;
|
||||
|
||||
};
|
||||
|
||||
ObjectIdentifier(const App::PropertyContainer * _owner = 0, const std::string & property = std::string());
|
||||
static Component SimpleComponent(const char * _component)
|
||||
{return Component::SimpleComponent(_component);}
|
||||
|
||||
ObjectIdentifier(const App::Property & prop);
|
||||
static Component SimpleComponent(const String & _component)
|
||||
{return Component::SimpleComponent(_component);}
|
||||
|
||||
static Component SimpleComponent(String &&_component)
|
||||
{return Component::SimpleComponent(std::move(_component));}
|
||||
|
||||
static Component ArrayComponent(int _index)
|
||||
{return Component::ArrayComponent(_index); }
|
||||
|
||||
static Component RangeComponent(int _begin, int _end = INT_MAX, int _step=1)
|
||||
{return Component::RangeComponent(_begin,_end,_step);}
|
||||
|
||||
static Component MapComponent(const String &_key)
|
||||
{return Component::MapComponent(_key);}
|
||||
|
||||
static Component MapComponent(String &&_key)
|
||||
{return Component::MapComponent(_key);}
|
||||
|
||||
ObjectIdentifier(const App::PropertyContainer * _owner = 0,
|
||||
const std::string & property = std::string(), int index=INT_MAX);
|
||||
|
||||
ObjectIdentifier(const App::PropertyContainer * _owner, bool localProperty);
|
||||
|
||||
ObjectIdentifier(const App::Property & prop, int index=INT_MAX);
|
||||
|
||||
FC_DEFAULT_CTORS(ObjectIdentifier) {
|
||||
owner = other.owner;
|
||||
documentName = std::move(other.documentName);
|
||||
documentObjectName = std::move(other.documentObjectName);
|
||||
subObjectName = std::move(other.subObjectName);
|
||||
shadowSub = std::move(other.shadowSub);
|
||||
components = std::move(other.components);
|
||||
documentNameSet = other.documentNameSet;
|
||||
documentObjectNameSet = other.documentObjectNameSet;
|
||||
localProperty = other.localProperty;
|
||||
_cache = std::move(other._cache);
|
||||
_hash = std::move(other._hash);
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ~ObjectIdentifier() {}
|
||||
|
||||
App::DocumentObject *getOwner() const { return owner; }
|
||||
|
||||
// Components
|
||||
void addComponent(const Component &c) { components.push_back(c); }
|
||||
void addComponent(const Component &c) {
|
||||
components.push_back(c);
|
||||
_cache.clear();
|
||||
}
|
||||
|
||||
// Components
|
||||
void addComponent(Component &&c) {
|
||||
components.push_back(std::move(c));
|
||||
_cache.clear();
|
||||
}
|
||||
|
||||
std::string getPropertyName() const;
|
||||
|
||||
template<typename C>
|
||||
void addComponents(const C &cs) { components.insert(components.end(), cs.begin(), cs.end()); }
|
||||
|
||||
const std::string getPropertyName() const;
|
||||
|
||||
const Component & getPropertyComponent(int i) const;
|
||||
|
||||
std::string getSubPathStr() const;
|
||||
Component & getPropertyComponent(int i);
|
||||
|
||||
std::vector<Component> getPropertyComponents() const;
|
||||
const std::vector<Component> &getComponents() const { return components; }
|
||||
|
||||
std::string getSubPathStr(bool toPython=false) const;
|
||||
|
||||
int numComponents() const;
|
||||
|
||||
int numSubComponents() const;
|
||||
|
||||
virtual std::string toString() const;
|
||||
const std::string &toString() const;
|
||||
|
||||
std::string toPersistentString() const;
|
||||
|
||||
std::string toEscapedString() const;
|
||||
|
||||
App::Property *getProperty() const;
|
||||
bool isTouched() const;
|
||||
|
||||
App::Property *getProperty(int *ptype=0) const;
|
||||
|
||||
App::ObjectIdentifier canonicalPath() const;
|
||||
|
||||
// Document-centric functions
|
||||
|
||||
void setDocumentName(const String & name, bool force = false);
|
||||
void setDocumentName(String &&name, bool force = false);
|
||||
|
||||
const String getDocumentName() const;
|
||||
String getDocumentName() const;
|
||||
|
||||
void setDocumentObjectName(const String & name, bool force = false);
|
||||
void setDocumentObjectName(String &&name, bool force = false,
|
||||
String &&subname = String(), bool checkImport=false);
|
||||
|
||||
const String getDocumentObjectName() const;
|
||||
void setDocumentObjectName(const App::DocumentObject *obj, bool force = false,
|
||||
String &&subname = String(), bool checkImport=false);
|
||||
|
||||
bool validDocumentObjectRename(const std::string &oldName, const std::string &newName);
|
||||
bool hasDocumentObjectName(bool forced=false) const;
|
||||
|
||||
bool renameDocumentObject(const std::string & oldName, const std::string & newName);
|
||||
bool isLocalProperty() const { return localProperty; }
|
||||
|
||||
bool validDocumentRename(const std::string &oldName, const std::string &newName);
|
||||
String getDocumentObjectName() const;
|
||||
|
||||
bool renameDocument(const std::string &oldName, const std::string &newName);
|
||||
const std::string &getSubObjectName(bool newStyle) const;
|
||||
const std::string &getSubObjectName() const;
|
||||
|
||||
App::Document *getDocument(String name = String()) const;
|
||||
typedef std::map<std::pair<App::DocumentObject*,std::string>,std::string> SubNameMap;
|
||||
void importSubNames(const SubNameMap &subNameMap);
|
||||
|
||||
bool updateLabelReference(App::DocumentObject *, const std::string &, const char *);
|
||||
|
||||
bool relabeledDocument(ExpressionVisitor &v, const std::string &oldLabel, const std::string &newLabel);
|
||||
|
||||
std::pair<App::DocumentObject*,std::string> getDep(std::vector<std::string> *labels=0) const;
|
||||
|
||||
App::Document *getDocument(String name = String(), bool *ambiguous=0) const;
|
||||
|
||||
App::DocumentObject *getDocumentObject() const;
|
||||
|
||||
|
||||
std::vector<std::string> getStringList() const;
|
||||
|
||||
App::ObjectIdentifier relativeTo(const App::ObjectIdentifier & other) const;
|
||||
|
||||
bool replaceObject(ObjectIdentifier &res, const App::DocumentObject *parent,
|
||||
App::DocumentObject *oldObj, App::DocumentObject *newObj) const;
|
||||
|
||||
// Operators
|
||||
|
||||
App::ObjectIdentifier & operator<<(const Component & value);
|
||||
App::ObjectIdentifier & operator<<(Component &&value);
|
||||
|
||||
bool operator==(const ObjectIdentifier & other) const;
|
||||
|
||||
@@ -224,12 +371,14 @@ public:
|
||||
|
||||
// Getter
|
||||
|
||||
boost::any getValue() const;
|
||||
App::any getValue(bool pathValue=false, bool *isPseudoProperty=0) const;
|
||||
|
||||
Py::Object getPyValue(bool pathValue=false, bool *isPseudoProperty=0) const;
|
||||
|
||||
// Setter; is const because it does not alter the object state,
|
||||
// but does have a aide effect.
|
||||
|
||||
void setValue(const boost::any & value) const;
|
||||
void setValue(const App::any & value) const;
|
||||
|
||||
// Static functions
|
||||
|
||||
@@ -237,6 +386,16 @@ public:
|
||||
|
||||
std::string resolveErrorString() const;
|
||||
|
||||
bool adjustLinks(ExpressionVisitor &v, const std::set<App::DocumentObject *> &inList);
|
||||
|
||||
bool updateElementReference(ExpressionVisitor &v, App::DocumentObject *feature=0, bool reverse=false);
|
||||
|
||||
void resolveAmbiguity();
|
||||
|
||||
bool verify(const App::Property &prop, bool silent=false) const;
|
||||
|
||||
std::size_t hash() const;
|
||||
|
||||
protected:
|
||||
|
||||
struct ResolveResults {
|
||||
@@ -248,29 +407,72 @@ protected:
|
||||
String resolvedDocumentName;
|
||||
App::DocumentObject * resolvedDocumentObject;
|
||||
String resolvedDocumentObjectName;
|
||||
String subObjectName;
|
||||
App::DocumentObject * resolvedSubObject;
|
||||
App::Property * resolvedProperty;
|
||||
std::string propertyName;
|
||||
int propertyType;
|
||||
std::bitset<32> flags;
|
||||
|
||||
std::string resolveErrorString() const;
|
||||
void getProperty(const ObjectIdentifier &oi);
|
||||
};
|
||||
|
||||
std::string getPythonAccessor() const;
|
||||
friend struct ResolveResults;
|
||||
|
||||
App::Property *resolveProperty(const App::DocumentObject *obj,
|
||||
const char *propertyName, App::DocumentObject *&sobj,int &ptype) const;
|
||||
|
||||
void getSubPathStr(std::ostream &ss, const ResolveResults &result, bool toPython=false) const;
|
||||
|
||||
Py::Object access(const ResolveResults &rs, Py::Object *value=0) const;
|
||||
|
||||
void resolve(ResolveResults & results) const;
|
||||
void resolveAmbiguity(ResolveResults &results);
|
||||
|
||||
App::DocumentObject *getDocumentObject(const App::Document *doc, const String &name, bool &byIdentifier) const;
|
||||
static App::DocumentObject *getDocumentObject(
|
||||
const App::Document *doc, const String &name, std::bitset<32> &flags);
|
||||
|
||||
const App::PropertyContainer * owner;
|
||||
App::DocumentObject * owner;
|
||||
String documentName;
|
||||
bool documentNameSet;
|
||||
String documentObjectName;
|
||||
bool documentObjectNameSet;
|
||||
String subObjectName;
|
||||
std::pair<std::string,std::string> shadowSub;
|
||||
std::vector<Component> components;
|
||||
bool documentNameSet;
|
||||
bool documentObjectNameSet;
|
||||
bool localProperty;
|
||||
|
||||
private:
|
||||
std::string _cache; // Cached string represstation of this identifier
|
||||
std::size_t _hash; // Cached hash of this string
|
||||
};
|
||||
|
||||
std::size_t AppExport hash_value(const App::ObjectIdentifier & path);
|
||||
inline std::size_t hash_value(const App::ObjectIdentifier & path) {
|
||||
return path.hash();
|
||||
}
|
||||
|
||||
/** Helper function to convert Python object to/from App::any
|
||||
*
|
||||
* WARNING! Must hold Python global interpreter lock before calling these
|
||||
* functions
|
||||
*/
|
||||
//@{
|
||||
App::any AppExport pyObjectToAny(Py::Object pyobj, bool check=true);
|
||||
Py::Object AppExport pyObjectFromAny(const App::any &value);
|
||||
//@}
|
||||
}
|
||||
|
||||
namespace std {
|
||||
|
||||
template<>
|
||||
struct hash<App::ObjectIdentifier> {
|
||||
typedef App::ObjectIdentifier argument_type;
|
||||
typedef std::size_t result_type;
|
||||
inline result_type operator()(argument_type const& s) const {
|
||||
return s.hash();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+3
-3
@@ -69,7 +69,7 @@ App::OriginFeature *Origin::getOriginFeature( const char *role) const {
|
||||
} else {
|
||||
|
||||
std::stringstream err;
|
||||
err << "Origin \"" << getNameInDocument () << "\" doesn't contain feature with role \""
|
||||
err << "Origin \"" << getFullName () << "\" doesn't contain feature with role \""
|
||||
<< role << '"';
|
||||
throw Base::RuntimeError ( err.str().c_str () );
|
||||
}
|
||||
@@ -81,7 +81,7 @@ App::Line *Origin::getAxis( const char *role ) const {
|
||||
return static_cast<App::Line *> (feat);
|
||||
} else {
|
||||
std::stringstream err;
|
||||
err << "Origin \"" << getNameInDocument () << "\" contains bad Axis object for role \""
|
||||
err << "Origin \"" << getFullName () << "\" contains bad Axis object for role \""
|
||||
<< role << '"';
|
||||
throw Base::RuntimeError ( err.str().c_str () );
|
||||
}
|
||||
@@ -93,7 +93,7 @@ App::Plane *Origin::getPlane( const char *role ) const {
|
||||
return static_cast<App::Plane *> (feat);
|
||||
} else {
|
||||
std::stringstream err;
|
||||
err << "Origin \"" << getNameInDocument () << "\" comtains bad Plane object for role \""
|
||||
err << "Origin \"" << getFullName () << "\" comtains bad Plane object for role \""
|
||||
<< role << '"';
|
||||
throw Base::RuntimeError ( err.str().c_str () );
|
||||
}
|
||||
|
||||
@@ -53,19 +53,42 @@ App::Origin *OriginGroupExtension::getOrigin () const {
|
||||
|
||||
if ( !originObj ) {
|
||||
std::stringstream err;
|
||||
err << "Can't find Origin for \"" << getExtendedObject()->getNameInDocument () << "\"";
|
||||
err << "Can't find Origin for \"" << getExtendedObject()->getFullName () << "\"";
|
||||
throw Base::RuntimeError ( err.str().c_str () );
|
||||
|
||||
} else if (! originObj->isDerivedFrom ( App::Origin::getClassTypeId() ) ) {
|
||||
std::stringstream err;
|
||||
err << "Bad object \"" << originObj->getNameInDocument () << "\"(" << originObj->getTypeId().getName()
|
||||
<< ") linked to the Origin of \"" << getExtendedObject()->getNameInDocument () << "\"";
|
||||
err << "Bad object \"" << originObj->getFullName () << "\"(" << originObj->getTypeId().getName()
|
||||
<< ") linked to the Origin of \"" << getExtendedObject()->getFullName () << "\"";
|
||||
throw Base::RuntimeError ( err.str().c_str () );
|
||||
} else {
|
||||
return static_cast<App::Origin *> ( originObj );
|
||||
}
|
||||
}
|
||||
|
||||
bool OriginGroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
|
||||
{
|
||||
App::DocumentObject *originObj = Origin.getValue ();
|
||||
const char *dot;
|
||||
if(originObj && originObj->getNameInDocument() &&
|
||||
subname && (dot=strchr(subname,'.')))
|
||||
{
|
||||
bool found;
|
||||
if(subname[0] == '$')
|
||||
found = std::string(subname+1,dot)==originObj->Label.getValue();
|
||||
else
|
||||
found = std::string(subname,dot)==originObj->getNameInDocument();
|
||||
if(found) {
|
||||
if(mat && transform)
|
||||
*mat *= const_cast<OriginGroupExtension*>(this)->placement().getValue().toMatrix();
|
||||
ret = originObj->getSubObject(dot+1,pyObj,mat,true,depth+1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return GeoFeatureGroupExtension::extensionGetSubObject(ret,subname,pyObj,mat,transform,depth);
|
||||
}
|
||||
|
||||
App::DocumentObject *OriginGroupExtension::getGroupOfObject (const DocumentObject* obj) {
|
||||
|
||||
if(!obj)
|
||||
|
||||
@@ -67,6 +67,9 @@ public:
|
||||
virtual std::vector<DocumentObject*> addObjects(std::vector<DocumentObject*> obj) override;
|
||||
virtual bool hasObject(const DocumentObject* obj, bool recursive = false) const override;
|
||||
|
||||
virtual bool extensionGetSubObject(DocumentObject *&ret, const char *subname, PyObject **pyObj,
|
||||
Base::Matrix4D *mat, bool transform, int depth) const override;
|
||||
|
||||
protected:
|
||||
/// Checks integrity of the Origin
|
||||
virtual App::DocumentObjectExecReturn *extensionExecute () override;
|
||||
|
||||
+2
-2
@@ -80,7 +80,7 @@ public:
|
||||
virtual ~Part();
|
||||
|
||||
/// returns the type name of the ViewProvider
|
||||
virtual const char* getViewProviderName(void) const {
|
||||
virtual const char* getViewProviderName(void) const override {
|
||||
return "Gui::ViewProviderPart";
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
*/
|
||||
static App::Part* getPartOfObject (const DocumentObject* obj);
|
||||
|
||||
virtual PyObject *getPyObject(void);
|
||||
virtual PyObject *getPyObject(void) override;
|
||||
};
|
||||
|
||||
//typedef App::FeaturePythonT<Part> PartPython;
|
||||
|
||||
@@ -51,6 +51,14 @@ Placement::~Placement(void)
|
||||
|
||||
|
||||
|
||||
// Python feature ---------------------------------------------------------
|
||||
namespace App {
|
||||
PROPERTY_SOURCE_TEMPLATE(App::PlacementPython, App::Placement)
|
||||
template<> const char* App::PlacementPython::getViewProviderName(void) const {
|
||||
return "Gui::ViewProviderPlacementPython";
|
||||
}
|
||||
template class AppExport FeaturePythonT<App::Placement>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include <Base/Placement.h>
|
||||
|
||||
#include "FeaturePython.h"
|
||||
#include "GeoFeature.h"
|
||||
#include "PropertyGeo.h"
|
||||
|
||||
@@ -66,6 +67,7 @@ public:
|
||||
|
||||
|
||||
};
|
||||
typedef App::FeaturePythonT<App::Placement> PlacementPython;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
#include <bitset>
|
||||
#include <exception>
|
||||
#include <random>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
// Boost
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
+124
-13
@@ -33,6 +33,7 @@
|
||||
#include "PropertyContainer.h"
|
||||
#include <Base/Exception.h>
|
||||
#include "Application.h"
|
||||
#include "DocumentObject.h"
|
||||
|
||||
using namespace App;
|
||||
|
||||
@@ -49,7 +50,7 @@ TYPESYSTEM_SOURCE_ABSTRACT(App::Property , Base::Persistence);
|
||||
|
||||
// Here is the implementation! Description should take place in the header file!
|
||||
Property::Property()
|
||||
:father(0)
|
||||
:father(0), myName(0)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -61,12 +62,45 @@ Property::~Property()
|
||||
|
||||
const char* Property::getName(void) const
|
||||
{
|
||||
return father->getPropertyName(this);
|
||||
return myName;
|
||||
}
|
||||
|
||||
std::string Property::getFullName() const {
|
||||
std::string name;
|
||||
if(myName) {
|
||||
if(father)
|
||||
name = father->getFullName() + ".";
|
||||
name += myName;
|
||||
}else
|
||||
return "?";
|
||||
return name;
|
||||
}
|
||||
|
||||
short Property::getType(void) const
|
||||
{
|
||||
return father->getPropertyType(this);
|
||||
short type = 0;
|
||||
#define GET_PTYPE(_name) do {\
|
||||
if(testStatus(App::Property::Prop##_name)) type|=Prop_##_name;\
|
||||
}while(0)
|
||||
GET_PTYPE(ReadOnly);
|
||||
GET_PTYPE(Hidden);
|
||||
GET_PTYPE(Output);
|
||||
GET_PTYPE(Transient);
|
||||
GET_PTYPE(NoRecompute);
|
||||
GET_PTYPE(NoPersist);
|
||||
return type;
|
||||
}
|
||||
|
||||
void Property::syncType(unsigned type) {
|
||||
#define SYNC_PTYPE(_name) do{\
|
||||
if(type & Prop_##_name) StatusBits.set((size_t)Prop##_name);\
|
||||
}while(0)
|
||||
SYNC_PTYPE(ReadOnly);
|
||||
SYNC_PTYPE(Transient);
|
||||
SYNC_PTYPE(Hidden);
|
||||
SYNC_PTYPE(Output);
|
||||
SYNC_PTYPE(NoRecompute);
|
||||
SYNC_PTYPE(NoPersist);
|
||||
}
|
||||
|
||||
const char* Property::getGroup(void) const
|
||||
@@ -99,7 +133,7 @@ void Property::getPaths(std::vector<ObjectIdentifier> &paths) const
|
||||
paths.push_back(App::ObjectIdentifier(getContainer(), getName()));
|
||||
}
|
||||
|
||||
const ObjectIdentifier Property::canonicalPath(const ObjectIdentifier &p) const
|
||||
ObjectIdentifier Property::canonicalPath(const ObjectIdentifier &p) const
|
||||
{
|
||||
return p;
|
||||
}
|
||||
@@ -113,10 +147,7 @@ void Property::touch()
|
||||
|
||||
void Property::setReadOnly(bool readOnly)
|
||||
{
|
||||
unsigned long status = this->getStatus();
|
||||
this->setStatus(App::Property::ReadOnly, readOnly);
|
||||
if (status != this->getStatus())
|
||||
App::GetApplication().signalChangePropertyEditor(*this);
|
||||
}
|
||||
|
||||
void Property::hasSetValue(void)
|
||||
@@ -134,12 +165,7 @@ void Property::aboutToSetValue(void)
|
||||
|
||||
void Property::verifyPath(const ObjectIdentifier &p) const
|
||||
{
|
||||
if (p.numSubComponents() != 1)
|
||||
throw Base::ValueError("Invalid property path: single component expected");
|
||||
if (!p.getPropertyComponent(0).isSimple())
|
||||
throw Base::ValueError("Invalid property path: simple component expected");
|
||||
if (p.getPropertyComponent(0).getName() != getName())
|
||||
throw Base::ValueError("Invalid property path: name mismatch");
|
||||
p.verify(*this);
|
||||
}
|
||||
|
||||
Property *Property::Copy(void) const
|
||||
@@ -155,6 +181,91 @@ void Property::Paste(const Property& /*from*/)
|
||||
assert(0);
|
||||
}
|
||||
|
||||
void Property::setStatusValue(unsigned long status) {
|
||||
static const unsigned long mask =
|
||||
(1<<PropDynamic)
|
||||
|(1<<PropNoRecompute)
|
||||
|(1<<PropReadOnly)
|
||||
|(1<<PropTransient)
|
||||
|(1<<PropOutput)
|
||||
|(1<<PropHidden);
|
||||
|
||||
status &= ~mask;
|
||||
status |= StatusBits.to_ulong() & mask;
|
||||
unsigned long oldStatus = StatusBits.to_ulong();
|
||||
StatusBits = decltype(StatusBits)(status);
|
||||
|
||||
if(father) {
|
||||
static unsigned long _signalMask = (1<<ReadOnly) | (1<<Hidden);
|
||||
if((status & _signalMask) != (oldStatus & _signalMask))
|
||||
father->onPropertyStatusChanged(*this,oldStatus);
|
||||
}
|
||||
}
|
||||
|
||||
void Property::setStatus(Status pos, bool on) {
|
||||
auto bits = StatusBits;
|
||||
bits.set(pos,on);
|
||||
setStatusValue(bits.to_ulong());
|
||||
}
|
||||
//**************************************************************************
|
||||
//**************************************************************************
|
||||
// PropertyListsBase
|
||||
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
void PropertyListsBase::_setPyObject(PyObject *value) {
|
||||
std::vector<int> indices;
|
||||
std::vector<PyObject *> vals;
|
||||
Py::Object pySeq;
|
||||
|
||||
if (PyDict_Check(value)) {
|
||||
Py::Dict dict(value);
|
||||
auto size = dict.size();
|
||||
vals.reserve(size);
|
||||
indices.reserve(size);
|
||||
int listSize = getSize();
|
||||
for(auto it=dict.begin();it!=dict.end();++it) {
|
||||
const auto &item = *it;
|
||||
PyObject *key = item.first.ptr();
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if(!PyInt_Check(key))
|
||||
#else
|
||||
if(!PyLong_Check(key))
|
||||
#endif
|
||||
throw Base::TypeError("expect key type to be interger");
|
||||
long idx = PyLong_AsLong(key);
|
||||
if(idx<-1 || idx>listSize)
|
||||
throw Base::ValueError("index out of bound");
|
||||
if(idx==-1 || idx==listSize) {
|
||||
idx = listSize;
|
||||
++listSize;
|
||||
}
|
||||
indices.push_back(idx);
|
||||
vals.push_back(item.second.ptr());
|
||||
}
|
||||
} else {
|
||||
if (PySequence_Check(value))
|
||||
pySeq = value;
|
||||
else {
|
||||
PyObject *iter = PyObject_GetIter(value);
|
||||
if(iter) {
|
||||
Py::Object pyIter(iter,true);
|
||||
pySeq = Py::asObject(PySequence_Fast(iter,""));
|
||||
} else {
|
||||
PyErr_Clear();
|
||||
vals.push_back(value);
|
||||
}
|
||||
}
|
||||
if(!pySeq.isNone()) {
|
||||
Py::Sequence seq(pySeq);
|
||||
vals.reserve(seq.size());
|
||||
for(auto it=seq.begin();it!=seq.end();++it)
|
||||
vals.push_back((*it).ptr());
|
||||
}
|
||||
}
|
||||
setPyValues(vals,indices);
|
||||
}
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
//**************************************************************************
|
||||
// PropertyLists
|
||||
|
||||
+318
-54
@@ -26,6 +26,7 @@
|
||||
|
||||
// Std. configurations
|
||||
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Persistence.h>
|
||||
#ifndef BOOST_105400
|
||||
#include <boost/any.hpp>
|
||||
@@ -35,6 +36,9 @@
|
||||
#include <string>
|
||||
#include <bitset>
|
||||
|
||||
namespace Py {
|
||||
class Object;
|
||||
}
|
||||
|
||||
namespace App
|
||||
{
|
||||
@@ -53,7 +57,7 @@ class ObjectIdentifier;
|
||||
*/
|
||||
class AppExport Property : public Base::Persistence
|
||||
{
|
||||
TYPESYSTEM_HEADER();
|
||||
TYPESYSTEM_HEADER_WITH_OVERRIDE();
|
||||
|
||||
public:
|
||||
enum Status
|
||||
@@ -62,8 +66,35 @@ public:
|
||||
Immutable = 1, // can't modify property
|
||||
ReadOnly = 2, // for property editor
|
||||
Hidden = 3, // for property editor
|
||||
Single = 4, // for save/load of floating point numbers
|
||||
Ordered = 5, // for PropertyLists whether the order of the elements is relevant for the container using it
|
||||
Transient = 4, // for property container save
|
||||
MaterialEdit = 5, // to turn ON PropertyMaterial edit
|
||||
NoMaterialListEdit = 6, // to turn OFF PropertyMaterialList edit
|
||||
Output = 7, // same effect as Prop_Output
|
||||
LockDynamic = 8, // prevent being removed from dynamic property
|
||||
NoModify = 9, // prevent causing Gui::Document::setModified()
|
||||
PartialTrigger = 10, // allow change in partial doc
|
||||
NoRecompute = 11, // touch owner for recompute on property change
|
||||
Single = 12, // for save/load of floating point numbers
|
||||
Ordered = 13, // for PropertyLists whether the order of the elements is
|
||||
// relevant for the container using it
|
||||
EvalOnRestore = 14, // In case of expression binding, evaluate the
|
||||
// expression on restore and touch the object on value change.
|
||||
|
||||
// The following bits are corresponding to PropertyType set when the
|
||||
// property added. These types are meant to be static, and cannot be
|
||||
// changed in runtime. It is mirrored here to save the linear search
|
||||
// required in PropertyContainer::getPropertyType()
|
||||
//
|
||||
PropStaticBegin = 21,
|
||||
PropDynamic = 21, // indicating the property is dynamically added
|
||||
PropNoPersist = 22, // corresponding to Prop_NoPersist
|
||||
PropNoRecompute = 23, // corresponding to Prop_NoRecompute
|
||||
PropReadOnly = 24, // corresponding to Prop_ReadOnly
|
||||
PropTransient= 25, // corresponding to Prop_Transient
|
||||
PropHidden = 26, // corresponding to Prop_Hidden
|
||||
PropOutput = 27, // corresponding to Prop_Output
|
||||
PropStaticEnd = 28,
|
||||
|
||||
User1 = 28, // user-defined status
|
||||
User2 = 29, // user-defined status
|
||||
User3 = 30, // user-defined status
|
||||
@@ -79,7 +110,7 @@ public:
|
||||
* This method is defined in Base::Persistence
|
||||
* @see Base::Persistence
|
||||
*/
|
||||
virtual unsigned int getMemSize (void) const {
|
||||
virtual unsigned int getMemSize (void) const override {
|
||||
// you have to implement this method in all property classes!
|
||||
return sizeof(father) + sizeof(StatusBits);
|
||||
}
|
||||
@@ -87,6 +118,8 @@ public:
|
||||
/// get the name of this property in the belonging container
|
||||
const char* getName(void) const;
|
||||
|
||||
std::string getFullName() const;
|
||||
|
||||
/// Get the class name of the associated property editor item
|
||||
virtual const char* getEditorName(void) const { return ""; }
|
||||
|
||||
@@ -111,12 +144,48 @@ public:
|
||||
/// Get value of property
|
||||
virtual const boost::any getPathValue(const App::ObjectIdentifier & path) const;
|
||||
|
||||
/// Get Python value of property
|
||||
virtual bool getPyPathValue(const App::ObjectIdentifier &, Py::Object &) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Convert p to a canonical representation of it
|
||||
virtual const App::ObjectIdentifier canonicalPath(const App::ObjectIdentifier & p) const;
|
||||
virtual App::ObjectIdentifier canonicalPath(const App::ObjectIdentifier & p) const;
|
||||
|
||||
/// Get valid paths for this property; used by auto completer
|
||||
virtual void getPaths(std::vector<App::ObjectIdentifier> & paths) const;
|
||||
|
||||
/** Called at the beginning of Document::afterRestore()
|
||||
*
|
||||
* This function is called without dependency sorting, because some
|
||||
* types of link property can only reconstructs the linking information
|
||||
* inside this function.
|
||||
*
|
||||
* One example use case of this function is PropertyLinkSub that uses
|
||||
* afterRestore() to parse and restore subname references, which may
|
||||
* contain sub-object reference from external document, and there will be
|
||||
* special mapping required during object import.
|
||||
*
|
||||
* Another example is PropertyExpressionEngine which only parse the
|
||||
* restored expression in afterRestore(). The reason, in addition to
|
||||
* subname mapping like PropertyLinkSub, is that it can handle document
|
||||
* name adjustment as well. It internally relies on PropertyXLink to store
|
||||
* the external document path for external linking. When the external
|
||||
* document is restored, its internal name may change due to name conflict
|
||||
* with existing documents. PropertyExpressionEngine can now auto adjust
|
||||
* external references without any problem.
|
||||
*/
|
||||
virtual void afterRestore() {}
|
||||
|
||||
/** Called before calling DocumentObject::onDocumentRestored()
|
||||
*
|
||||
* This function is called after finished calling Property::afterRestore()
|
||||
* of all properties of objects. By then, the object dependency information
|
||||
* is assumed ready. So, unlike Property::afterRestore(), this function is
|
||||
* called on objects with dependency order.
|
||||
*/
|
||||
virtual void onContainerRestored() {}
|
||||
|
||||
/** Property status handling
|
||||
*/
|
||||
//@{
|
||||
@@ -137,9 +206,8 @@ public:
|
||||
inline bool testStatus(Status pos) const {
|
||||
return StatusBits.test(static_cast<size_t>(pos));
|
||||
}
|
||||
inline void setStatus(Status pos, bool on) {
|
||||
StatusBits.set(static_cast<size_t>(pos), on);
|
||||
}
|
||||
void setStatus(Status pos, bool on);
|
||||
void setStatusValue(unsigned long status);
|
||||
///Sets property editable/grayed out in property editor
|
||||
void setReadOnly(bool readOnly);
|
||||
inline bool isReadOnly() const {
|
||||
@@ -161,8 +229,14 @@ public:
|
||||
/// Paste the value from the property (mainly for Undo/Redo and transactions)
|
||||
virtual void Paste(const Property &from) = 0;
|
||||
|
||||
/// Called when a child property has changed value
|
||||
virtual void hasSetChildValue(Property &) {}
|
||||
/// Called before a child property changing value
|
||||
virtual void aboutToSetChildValue(Property &) {}
|
||||
|
||||
friend class PropertyContainer;
|
||||
friend struct PropertyData;
|
||||
friend class DynamicProperty;
|
||||
|
||||
protected:
|
||||
/** Status bits of the property
|
||||
@@ -178,9 +252,9 @@ protected:
|
||||
|
||||
protected:
|
||||
/// Gets called by all setValue() methods after the value has changed
|
||||
void hasSetValue(void);
|
||||
virtual void hasSetValue(void);
|
||||
/// Gets called by all setValue() methods before the value has changed
|
||||
void aboutToSetValue(void);
|
||||
virtual void aboutToSetValue(void);
|
||||
|
||||
/// Verify a path for the current property
|
||||
virtual void verifyPath(const App::ObjectIdentifier & p) const;
|
||||
@@ -190,37 +264,19 @@ private:
|
||||
Property(const Property&);
|
||||
Property& operator = (const Property&);
|
||||
|
||||
// Sync status with Property_Type
|
||||
void syncType(unsigned type);
|
||||
|
||||
private:
|
||||
PropertyContainer *father;
|
||||
const char *myName;
|
||||
};
|
||||
|
||||
|
||||
/** Base class of all property lists.
|
||||
* The PropertyLists class is the base class for properties which can contain
|
||||
* multiple values, not only a single value.
|
||||
* All property types which may contain more than one value inherits this class.
|
||||
*/
|
||||
class AppExport PropertyLists : public Property
|
||||
{
|
||||
TYPESYSTEM_HEADER();
|
||||
|
||||
public:
|
||||
PropertyLists() {};
|
||||
|
||||
virtual void setSize(int newSize)=0;
|
||||
virtual int getSize(void) const =0;
|
||||
|
||||
// if the order of the elements in the list relevant?
|
||||
// if yes, certain operations, like restoring must make sure that the
|
||||
// order is kept despite errors.
|
||||
inline void setOrderRelevant(bool on) { this->setStatus(Status::Ordered,on); };
|
||||
inline bool isOrderRelevant() const { return this->testStatus(Status::Ordered);}
|
||||
};
|
||||
|
||||
/** A template class that is used to inhibit multiple nested calls to aboutToSetValue/hasSetValue for properties.
|
||||
*
|
||||
* A template class that is used to inhibit multiple nested calls to
|
||||
* aboutToSetValue/hasSetValue for properties, and only invoke it the first and
|
||||
* aboutToSetValue/hasSetValue for properties, and only invoke it on change and
|
||||
* last time it is needed. This is useful in cases where you want to change multiple
|
||||
* values in a property "atomically", using possibly multiple primitive functions
|
||||
* that normally would trigger aboutToSetValue/hasSetValue calls on their own.
|
||||
@@ -228,48 +284,256 @@ public:
|
||||
* To use, inherit privately from the AtomicPropertyChangeInterface class, using
|
||||
* your class name as the template argument. In all cases where you normally would
|
||||
* call aboutToSetValue/hasSetValue before and after a change, create an
|
||||
* AtomicPropertyChange object before you do the change. Depending on a counter
|
||||
* in the main property, the constructor might invoke aboutToSetValue. When the
|
||||
* AtomicPropertyChange object is destructed, it might call hasSetValue if it is
|
||||
* found necessary to do (i.e last item on the AtomicPropertyChange stack).
|
||||
* This makes it easy to match the calls, and it is also exception safe in the
|
||||
* sense that the destructors are guaranteed to be called during unwinding and
|
||||
* exception handling, making the calls to boutToSetValue and hasSetValue balanced.
|
||||
* AtomicPropertyChange object. The default constructor assume you are about to
|
||||
* change the property and will call property's aboutToSetValue() if the
|
||||
* property has not been marked as changed before by any other
|
||||
* AtomicPropertyChange instances in current call stack. You can pass 'false'
|
||||
* as the a second argument to the constructor, and manually call
|
||||
* AtomicPropertyChange::aboutToChange() before actual change, this enables you
|
||||
* to prevent unnecessary property copy for undo/redo where there is actual
|
||||
* changes. AtomicPropertyChange will guaranetee calling hasSetValue() when the
|
||||
* last instance in the current call stack is destroyed.
|
||||
*
|
||||
* One thing to take note is that, because C++ does not allow throwing
|
||||
* exception in destructor, any exception thrown when calling property's
|
||||
* hasSetValue() will be caught and swallowed. To allow exception propagation,
|
||||
* you can manually call AtomicPropertyChange::tryInvoke(). If the condition is
|
||||
* satisfied, it will call hasSetValue() that allows exception propagation.
|
||||
*/
|
||||
|
||||
template<class P> class AtomicPropertyChangeInterface {
|
||||
protected:
|
||||
AtomicPropertyChangeInterface() : signalCounter(0) { }
|
||||
AtomicPropertyChangeInterface() : signalCounter(0), hasChanged(false) { }
|
||||
|
||||
public:
|
||||
class AtomicPropertyChange {
|
||||
public:
|
||||
AtomicPropertyChange(P & prop) : mProp(prop) {
|
||||
// Signal counter == 0? Then we need to invoke the aboutToSetValue in the property.
|
||||
if (mProp.signalCounter == 0)
|
||||
mProp.aboutToSetValue();
|
||||
|
||||
/** Constructor
|
||||
*
|
||||
* @param prop: the property
|
||||
* @param markChange: If true, marks the property as changed if it
|
||||
* hasn't been marked before, and calls its
|
||||
* aboutToSetValue().
|
||||
*/
|
||||
AtomicPropertyChange(P & prop, bool markChange=true) : mProp(prop) {
|
||||
mProp.signalCounter++;
|
||||
if (markChange)
|
||||
aboutToChange();
|
||||
}
|
||||
|
||||
~AtomicPropertyChange() {
|
||||
mProp.signalCounter--;
|
||||
/** Mark the property as changed
|
||||
*
|
||||
* It will mark the property as changed only if it has been marked
|
||||
* before, and only then will it call the property's aboutToSetValue().
|
||||
*/
|
||||
void aboutToChange() {
|
||||
if(!mProp.hasChanged) {
|
||||
mProp.hasChanged = true;
|
||||
mProp.aboutToSetValue();
|
||||
}
|
||||
}
|
||||
|
||||
// Signal counter == 0? Then we need to invoke the hasSetValue in the property.
|
||||
if (mProp.signalCounter == 0)
|
||||
mProp.hasSetValue();
|
||||
/** Destructor
|
||||
*
|
||||
* If the property is marked as changed, and this is the last instance
|
||||
* of the class in current call stack, it will call property's
|
||||
* hasSetValue()
|
||||
*/
|
||||
~AtomicPropertyChange() {
|
||||
// Signal counter == 1? meaning we are the last one. Invoke
|
||||
// hasSetValue() before decrease counter to prevent recursive call
|
||||
// triggered by another AtomicPropertyChange created inside
|
||||
// hasSetValue(), as it has now been changed to a virtual function.
|
||||
if (mProp.signalCounter == 1 && mProp.hasChanged) {
|
||||
// Must make sure to not throw in a destructor
|
||||
try {
|
||||
mProp.hasSetValue();
|
||||
}catch(Base::Exception &e) {
|
||||
e.ReportException();
|
||||
}catch(...) {}
|
||||
mProp.hasChanged = false;
|
||||
}
|
||||
if(mProp.signalCounter>0)
|
||||
mProp.signalCounter--;
|
||||
}
|
||||
|
||||
/** Check and invoke property's hasSetValue()
|
||||
*
|
||||
* Check if this is the last instance and the property has been marked
|
||||
* as changed. If so, invoke property's hasSetValue().
|
||||
*/
|
||||
// Destructor cannot throw. So we provide this function to allow error
|
||||
// propagation.
|
||||
void tryInvoke() {
|
||||
if(mProp.signalCounter==1 && mProp.hasChanged) {
|
||||
mProp.hasSetValue();
|
||||
if(mProp.signalCounter>0)
|
||||
--mProp.signalCounter;
|
||||
mProp.hasChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
P & mProp; /**< Referenced to property we work on */
|
||||
};
|
||||
|
||||
static AtomicPropertyChange * getAtomicPropertyChange(P & prop) { return new AtomicPropertyChange(prop); }
|
||||
|
||||
private:
|
||||
|
||||
int signalCounter; /**< Counter for invoking transaction start/stop */
|
||||
bool hasChanged;
|
||||
};
|
||||
|
||||
|
||||
/** Helper class to construct list like properties
|
||||
*
|
||||
* This class is not derived from Property so that we can have more that one
|
||||
* base class for list like properties, e.g. see PropertyList, and
|
||||
* PropertyLinkListBase
|
||||
*/
|
||||
class AppExport PropertyListsBase
|
||||
{
|
||||
public:
|
||||
virtual void setSize(int newSize)=0;
|
||||
virtual int getSize(void) const =0;
|
||||
|
||||
const std::set<int> &getTouchList() const {
|
||||
return _touchList;
|
||||
}
|
||||
|
||||
void clearTouchList() {
|
||||
_touchList.clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void setPyValues(const std::vector<PyObject*> &vals, const std::vector<int> &indices) {
|
||||
(void)vals;
|
||||
(void)indices;
|
||||
throw Base::NotImplementedError("not implemented");
|
||||
}
|
||||
|
||||
void _setPyObject(PyObject *);
|
||||
|
||||
protected:
|
||||
std::set<int> _touchList;
|
||||
};
|
||||
|
||||
/** Base class of all property lists.
|
||||
* The PropertyLists class is the base class for properties which can contain
|
||||
* multiple values, not only a single value.
|
||||
* All property types which may contain more than one value inherits this class.
|
||||
*/
|
||||
class AppExport PropertyLists : public Property, public PropertyListsBase
|
||||
|
||||
{
|
||||
TYPESYSTEM_HEADER_WITH_OVERRIDE();
|
||||
public:
|
||||
virtual void setPyObject(PyObject *obj) override {
|
||||
_setPyObject(obj);
|
||||
}
|
||||
|
||||
// if the order of the elements in the list relevant?
|
||||
// if yes, certain operations, like restoring must make sure that the
|
||||
// order is kept despite errors.
|
||||
inline void setOrderRelevant(bool on) { this->setStatus(Status::Ordered,on); };
|
||||
inline bool isOrderRelevant() const { return this->testStatus(Status::Ordered);}
|
||||
|
||||
};
|
||||
|
||||
/** Helper class to implement PropertyLists */
|
||||
template<class T, class ListT = std::vector<T>, class ParentT = PropertyLists >
|
||||
class PropertyListsT: public ParentT
|
||||
, public AtomicPropertyChangeInterface<PropertyListsT<T,ListT,ParentT> >
|
||||
{
|
||||
public:
|
||||
typedef typename ListT::const_reference const_reference;
|
||||
typedef ListT list_type;
|
||||
typedef ParentT parent_type;
|
||||
typedef typename AtomicPropertyChangeInterface<
|
||||
PropertyListsT<T,ListT,ParentT> >::AtomicPropertyChange atomic_change;
|
||||
|
||||
friend atomic_change;
|
||||
|
||||
virtual void setSize(int newSize, const_reference def) {
|
||||
_lValueList.resize(newSize,def);
|
||||
}
|
||||
|
||||
virtual void setSize(int newSize) override {
|
||||
_lValueList.resize(newSize);
|
||||
}
|
||||
|
||||
virtual int getSize(void) const override {
|
||||
return static_cast<int>(_lValueList.size());
|
||||
}
|
||||
|
||||
void setValue(const_reference value) {
|
||||
ListT vals;
|
||||
vals.resize(1,value);
|
||||
setValues(vals);
|
||||
}
|
||||
|
||||
virtual void setValues(const ListT &newValues = ListT()) {
|
||||
atomic_change guard(*this);
|
||||
this->_touchList.clear();
|
||||
this->_lValueList = newValues;
|
||||
guard.tryInvoke();
|
||||
}
|
||||
|
||||
void setValue(const ListT &newValues = ListT()) {
|
||||
setValues(newValues);
|
||||
}
|
||||
|
||||
const ListT &getValues(void) const{return _lValueList;}
|
||||
|
||||
// alias to getValues
|
||||
const ListT &getValue(void) const{return getValues();}
|
||||
|
||||
const_reference operator[] (int idx) const {return _lValueList[idx];}
|
||||
|
||||
virtual void setPyObject(PyObject *value) override {
|
||||
try {
|
||||
setValue(getPyValue(value));
|
||||
return;
|
||||
}catch(...){}
|
||||
parent_type::setPyObject(value);
|
||||
}
|
||||
|
||||
virtual void set1Value(int index, const_reference value) {
|
||||
int size = getSize();
|
||||
if(index<-1 || index>size)
|
||||
throw Base::RuntimeError("index out of bound");
|
||||
|
||||
atomic_change guard(*this);
|
||||
if(index==-1 || index == size) {
|
||||
index = size;
|
||||
setSize(index+1,value);
|
||||
}else
|
||||
_lValueList[index] = value;
|
||||
this->_touchList.insert(index);
|
||||
guard.tryInvoke();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void setPyValues(const std::vector<PyObject*> &vals, const std::vector<int> &indices) override
|
||||
{
|
||||
if(indices.empty()) {
|
||||
ListT values;
|
||||
values.resize(vals.size());
|
||||
for(std::size_t i=0,count=vals.size();i<count;++i)
|
||||
values[i] = getPyValue(vals[i]);
|
||||
setValues(std::move(values));
|
||||
return;
|
||||
}
|
||||
assert(vals.size()==indices.size());
|
||||
atomic_change guard(*this);
|
||||
for(int i=0,count=indices.size();i<count;++i)
|
||||
set1Value(indices[i],getPyValue(vals[i]));
|
||||
guard.tryInvoke();
|
||||
}
|
||||
|
||||
virtual T getPyValue(PyObject *item) const = 0;
|
||||
|
||||
protected:
|
||||
ListT _lValueList;
|
||||
};
|
||||
|
||||
} // namespace App
|
||||
|
||||
+221
-170
@@ -35,10 +35,13 @@
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Exception.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Property.h"
|
||||
#include "PropertyContainer.h"
|
||||
#include "PropertyLinks.h"
|
||||
|
||||
FC_LOG_LEVEL_INIT("App",true,true)
|
||||
|
||||
using namespace App;
|
||||
using namespace Base;
|
||||
using namespace std;
|
||||
@@ -71,18 +74,30 @@ unsigned int PropertyContainer::getMemSize (void) const
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
App::Property* PropertyContainer::addDynamicProperty(
|
||||
const char* type, const char* name, const char* group, const char* doc,
|
||||
short attr, bool ro, bool hidden)
|
||||
{
|
||||
return dynamicProps.addDynamicProperty(*this,type,name,group,doc,attr,ro,hidden);
|
||||
}
|
||||
|
||||
Property *PropertyContainer::getPropertyByName(const char* name) const
|
||||
{
|
||||
auto prop = dynamicProps.getDynamicPropertyByName(name);
|
||||
if(prop) return prop;
|
||||
return getPropertyData().getPropertyByName(this,name);
|
||||
}
|
||||
|
||||
void PropertyContainer::getPropertyMap(std::map<std::string,Property*> &Map) const
|
||||
{
|
||||
dynamicProps.getPropertyMap(Map);
|
||||
getPropertyData().getPropertyMap(this,Map);
|
||||
}
|
||||
|
||||
void PropertyContainer::getPropertyList(std::vector<Property*> &List) const
|
||||
{
|
||||
dynamicProps.getPropertyList(List);
|
||||
getPropertyData().getPropertyList(this,List);
|
||||
}
|
||||
|
||||
@@ -96,31 +111,39 @@ void PropertyContainer::setPropertyStatus(unsigned char bit,bool value)
|
||||
|
||||
short PropertyContainer::getPropertyType(const Property* prop) const
|
||||
{
|
||||
return getPropertyData().getType(this,prop);
|
||||
return prop?prop->getType():0;
|
||||
}
|
||||
|
||||
short PropertyContainer::getPropertyType(const char *name) const
|
||||
{
|
||||
return getPropertyData().getType(this,name);
|
||||
return getPropertyType(getPropertyByName(name));
|
||||
}
|
||||
|
||||
const char* PropertyContainer::getPropertyGroup(const Property* prop) const
|
||||
{
|
||||
auto group = dynamicProps.getPropertyGroup(prop);
|
||||
if(group) return group;
|
||||
return getPropertyData().getGroup(this,prop);
|
||||
}
|
||||
|
||||
const char* PropertyContainer::getPropertyGroup(const char *name) const
|
||||
{
|
||||
auto group = dynamicProps.getPropertyGroup(name);
|
||||
if(group) return group;
|
||||
return getPropertyData().getGroup(this,name);
|
||||
}
|
||||
|
||||
const char* PropertyContainer::getPropertyDocumentation(const Property* prop) const
|
||||
{
|
||||
auto doc = dynamicProps.getPropertyDocumentation(prop);
|
||||
if(doc) return doc;
|
||||
return getPropertyData().getDocumentation(this,prop);
|
||||
}
|
||||
|
||||
const char* PropertyContainer::getPropertyDocumentation(const char *name) const
|
||||
{
|
||||
auto doc = dynamicProps.getPropertyDocumentation(name);
|
||||
if(doc) return doc;
|
||||
return getPropertyData().getDocumentation(this,name);
|
||||
}
|
||||
|
||||
@@ -146,10 +169,12 @@ bool PropertyContainer::isHidden(const char *name) const
|
||||
|
||||
const char* PropertyContainer::getPropertyName(const Property* prop)const
|
||||
{
|
||||
return getPropertyData().getName(this,prop);
|
||||
auto res = dynamicProps.getPropertyName(prop);
|
||||
if(!res)
|
||||
res = getPropertyData().getName(this,prop);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
const PropertyData * PropertyContainer::getPropertyDataPtr(void){return &propertyData;}
|
||||
const PropertyData & PropertyContainer::getPropertyData(void) const{return propertyData;}
|
||||
|
||||
@@ -193,66 +218,92 @@ void PropertyContainer::handleChangedPropertyType(XMLReader &reader, const char
|
||||
|
||||
PropertyData PropertyContainer::propertyData;
|
||||
|
||||
/**
|
||||
* Binary function to query the flags for use with generic STL functions.
|
||||
*/
|
||||
template <class TCLASS>
|
||||
class PropertyAttribute : public std::binary_function<TCLASS, typename App::PropertyType, bool>
|
||||
{
|
||||
public:
|
||||
PropertyAttribute(const PropertyContainer* c) : cont(c) {}
|
||||
bool operator () (const TCLASS& prop, typename App::PropertyType attr) const
|
||||
{ return (cont->getPropertyType(prop.second) & attr) == attr; }
|
||||
private:
|
||||
const PropertyContainer* cont;
|
||||
};
|
||||
|
||||
void PropertyContainer::Save (Base::Writer &writer) const
|
||||
void PropertyContainer::Save (Base::Writer &writer) const
|
||||
{
|
||||
std::map<std::string,Property*> Map;
|
||||
getPropertyMap(Map);
|
||||
|
||||
// ignore the properties we won't store
|
||||
size_t ct = std::count_if(Map.begin(), Map.end(), std::bind2nd(PropertyAttribute
|
||||
<std::pair<std::string,Property*> >(this), Prop_Transient));
|
||||
size_t size = Map.size() - ct;
|
||||
std::vector<Property*> transients;
|
||||
for(auto it=Map.begin();it!=Map.end();) {
|
||||
auto prop = it->second;
|
||||
if(prop->testStatus(Property::PropNoPersist)) {
|
||||
it = Map.erase(it);
|
||||
continue;
|
||||
}
|
||||
if(!prop->testStatus(Property::PropDynamic)
|
||||
&& (prop->testStatus(Property::Transient) ||
|
||||
getPropertyType(prop) & Prop_Transient))
|
||||
{
|
||||
transients.push_back(prop);
|
||||
it = Map.erase(it);
|
||||
}else
|
||||
++it;
|
||||
}
|
||||
|
||||
writer.incInd(); // indentation for 'Properties Count'
|
||||
writer.Stream() << writer.ind() << "<Properties Count=\"" << size << "\">" << endl;
|
||||
std::map<std::string,Property*>::iterator it;
|
||||
for (it = Map.begin(); it != Map.end(); ++it)
|
||||
writer.Stream() << writer.ind() << "<Properties Count=\"" << Map.size()
|
||||
<< "\" TransientCount=\"" << transients.size() << "\">" << endl;
|
||||
|
||||
// First store transient properties to persisit their status value. We use
|
||||
// a new element named "_Property" so that the save file can be opened by
|
||||
// older version FC.
|
||||
writer.incInd();
|
||||
for(auto prop : transients) {
|
||||
writer.Stream() << writer.ind() << "<_Property name=\"" << prop->getName()
|
||||
<< "\" type=\"" << prop->getTypeId().getName()
|
||||
<< "\" status=\"" << prop->getStatus() << "\"/>" << std::endl;
|
||||
}
|
||||
writer.decInd();
|
||||
|
||||
// Now store normal properties
|
||||
for (auto it = Map.begin(); it != Map.end(); ++it)
|
||||
{
|
||||
// Don't write transient properties
|
||||
if (!(getPropertyType(it->second) & Prop_Transient))
|
||||
writer.incInd(); // indentation for 'Property name'
|
||||
writer.Stream() << writer.ind() << "<Property name=\"" << it->first << "\" type=\""
|
||||
<< it->second->getTypeId().getName();
|
||||
|
||||
dynamicProps.save(it->second,writer);
|
||||
|
||||
auto status = it->second->getStatus();
|
||||
if(status)
|
||||
writer.Stream() << "\" status=\"" << status;
|
||||
writer.Stream() << "\">";
|
||||
|
||||
if(it->second->testStatus(Property::Transient)
|
||||
|| it->second->getType() & Prop_Transient)
|
||||
{
|
||||
writer.incInd(); // indentation for 'Property name'
|
||||
writer.Stream() << writer.ind() << "<Property name=\"" << it->first << "\" type=\""
|
||||
<< it->second->getTypeId().getName() << "\">" << endl;;
|
||||
writer.incInd(); // indentation for the actual property
|
||||
try {
|
||||
// We must make sure to handle all exceptions accordingly so that
|
||||
// the project file doesn't get invalidated. In the error case this
|
||||
// means to proceed instead of aborting the write operation.
|
||||
it->second->Save(writer);
|
||||
}
|
||||
catch (const Base::Exception &e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
}
|
||||
catch (const char* e) {
|
||||
Base::Console().Error("%s\n", e);
|
||||
}
|
||||
#ifndef FC_DEBUG
|
||||
catch (...) {
|
||||
Base::Console().Error("PropertyContainer::Save: Unknown C++ exception thrown. Try to continue...\n");
|
||||
}
|
||||
#endif
|
||||
writer.decInd(); // indentation for the actual property
|
||||
writer.Stream() << writer.ind() << "</Property>" << endl;
|
||||
writer.decInd(); // indentation for 'Property name'
|
||||
writer.decInd();
|
||||
writer.Stream() << "</Property>" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
writer.Stream() << std::endl;
|
||||
|
||||
writer.incInd(); // indentation for the actual property
|
||||
|
||||
try {
|
||||
// We must make sure to handle all exceptions accordingly so that
|
||||
// the project file doesn't get invalidated. In the error case this
|
||||
// means to proceed instead of aborting the write operation.
|
||||
it->second->Save(writer);
|
||||
}
|
||||
catch (const Base::Exception &e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
}
|
||||
catch (const char* e) {
|
||||
Base::Console().Error("%s\n", e);
|
||||
}
|
||||
#ifndef FC_DEBUG
|
||||
catch (...) {
|
||||
Base::Console().Error("PropertyContainer::Save: Unknown C++ exception thrown. Try to continue...\n");
|
||||
}
|
||||
#endif
|
||||
writer.decInd(); // indentation for the actual property
|
||||
writer.Stream() << writer.ind() << "</Property>" << endl;
|
||||
writer.decInd(); // indentation for 'Property name'
|
||||
}
|
||||
writer.Stream() << writer.ind() << "</Properties>" << endl;
|
||||
writer.decInd(); // indentation for 'Properties Count'
|
||||
@@ -264,11 +315,33 @@ void PropertyContainer::Restore(Base::XMLReader &reader)
|
||||
reader.readElement("Properties");
|
||||
int Cnt = reader.getAttributeAsInteger("Count");
|
||||
|
||||
int transientCount = 0;
|
||||
if(reader.hasAttribute("TransientCount"))
|
||||
transientCount = reader.getAttributeAsUnsigned("TransientCount");
|
||||
|
||||
for (int i=0;i<transientCount; ++i) {
|
||||
reader.readElement("_Property");
|
||||
Property* prop = getPropertyByName(reader.getAttribute("name"));
|
||||
if(prop)
|
||||
FC_TRACE("restore transient '" << prop->getName() << "'");
|
||||
if(prop && reader.hasAttribute("status"))
|
||||
prop->setStatusValue(reader.getAttributeAsUnsigned("status"));
|
||||
}
|
||||
|
||||
for (int i=0 ;i<Cnt ;i++) {
|
||||
reader.readElement("Property");
|
||||
std::string PropName = reader.getAttribute("name");
|
||||
std::string TypeName = reader.getAttribute("type");
|
||||
Property* prop = getPropertyByName(PropName.c_str());
|
||||
auto prop = dynamicProps.restore(*this,PropName.c_str(),TypeName.c_str(),reader);
|
||||
if(!prop)
|
||||
prop = getPropertyByName(PropName.c_str());
|
||||
|
||||
decltype(Property::StatusBits) status;
|
||||
if(reader.hasAttribute("status")) {
|
||||
status = decltype(status)(reader.getAttributeAsUnsigned("status"));
|
||||
if(prop)
|
||||
prop->setStatusValue(status.to_ulong());
|
||||
}
|
||||
// NOTE: We must also check the type of the current property because a
|
||||
// subclass of PropertyContainer might change the type of a property but
|
||||
// not its name. In this case we would force to read-in a wrong property
|
||||
@@ -276,7 +349,15 @@ void PropertyContainer::Restore(Base::XMLReader &reader)
|
||||
try {
|
||||
// name and type match
|
||||
if (prop && strcmp(prop->getTypeId().getName(), TypeName.c_str()) == 0) {
|
||||
prop->Restore(reader);
|
||||
if (!prop->testStatus(Property::Transient)
|
||||
&& !status.test(Property::Transient)
|
||||
&& !status.test(Property::PropTransient)
|
||||
&& !(getPropertyType(prop) & Prop_Transient))
|
||||
{
|
||||
FC_TRACE("restore property '" << prop->getName() << "'");
|
||||
prop->Restore(reader);
|
||||
}else
|
||||
FC_TRACE("skip transient '" << prop->getName() << "'");
|
||||
}
|
||||
// name matches but not the type
|
||||
else if (prop) {
|
||||
@@ -315,58 +396,98 @@ void PropertyContainer::Restore(Base::XMLReader &reader)
|
||||
Base::Console().Error("PropertyContainer::Restore: Unknown C++ exception thrown\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
reader.readEndElement("Property");
|
||||
}
|
||||
reader.readEndElement("Properties");
|
||||
}
|
||||
|
||||
void PropertyContainer::onPropertyStatusChanged(const Property &prop, unsigned long oldStatus)
|
||||
{
|
||||
(void)prop;
|
||||
(void)oldStatus;
|
||||
}
|
||||
|
||||
void PropertyData::addProperty(OffsetBase offsetBase,const char* PropName, Property *Prop, const char* PropertyGroup , PropertyType Type, const char* PropertyDocu)
|
||||
{
|
||||
bool IsIn = false;
|
||||
for (vector<PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
|
||||
if(strcmp(It->Name,PropName)==0)
|
||||
IsIn = true;
|
||||
#ifdef FC_DEBUG
|
||||
if(!parentMerged)
|
||||
#endif
|
||||
{
|
||||
short offset = offsetBase.getOffsetTo(Prop);
|
||||
if(offset < 0)
|
||||
throw Base::RuntimeError("Invalid static property");
|
||||
auto &index = propertyData.get<1>();
|
||||
auto it = index.find(PropName);
|
||||
if(it == index.end()) {
|
||||
if(parentMerged)
|
||||
throw Base::RuntimeError("Cannot add static property");
|
||||
index.emplace(PropName, PropertyGroup, PropertyDocu, offset, Type);
|
||||
} else{
|
||||
#ifdef FC_DEBUG
|
||||
if(it->Offset != offset) {
|
||||
FC_ERR("Duplicate property '" << PropName << "'");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if( !IsIn )
|
||||
{
|
||||
PropertySpec temp;
|
||||
temp.Name = PropName;
|
||||
temp.Offset = offsetBase.getOffsetTo(Prop);
|
||||
assert(temp.Offset>=0);
|
||||
temp.Group = PropertyGroup;
|
||||
temp.Type = Type;
|
||||
temp.Docu = PropertyDocu;
|
||||
propertyData.push_back(temp);
|
||||
}
|
||||
Prop->syncType(Type);
|
||||
Prop->myName = PropName;
|
||||
}
|
||||
|
||||
void PropertyData::merge(PropertyData *other) const {
|
||||
if(!other)
|
||||
other = const_cast<PropertyData*>(parentPropertyData);
|
||||
if(other == parentPropertyData) {
|
||||
if(parentMerged)
|
||||
return;
|
||||
parentMerged = true;
|
||||
}
|
||||
if(other) {
|
||||
other->merge();
|
||||
auto &index = propertyData.get<0>();
|
||||
for(const auto &spec : other->propertyData.get<0>())
|
||||
index.push_back(spec);
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyData::split(PropertyData *other) {
|
||||
if(other == parentPropertyData) {
|
||||
if(!parentMerged)
|
||||
return;
|
||||
parentMerged = false;
|
||||
}
|
||||
if(other) {
|
||||
auto &index = propertyData.get<2>();
|
||||
for(const auto &spec : other->propertyData.get<0>())
|
||||
index.erase(spec.Offset);
|
||||
}
|
||||
}
|
||||
|
||||
const PropertyData::PropertySpec *PropertyData::findProperty(OffsetBase offsetBase,const char* PropName) const
|
||||
{
|
||||
for (vector<PropertyData::PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
|
||||
if(strcmp(It->Name,PropName)==0)
|
||||
return &(*It);
|
||||
|
||||
if(parentPropertyData)
|
||||
return parentPropertyData->findProperty(offsetBase,PropName);
|
||||
|
||||
return 0;
|
||||
(void)offsetBase;
|
||||
merge();
|
||||
auto &index = propertyData.get<1>();
|
||||
auto it = index.find(PropName);
|
||||
if(it != index.end())
|
||||
return &(*it);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const PropertyData::PropertySpec *PropertyData::findProperty(OffsetBase offsetBase,const Property* prop) const
|
||||
{
|
||||
const int diff = offsetBase.getOffsetTo(prop);
|
||||
if(diff<0)
|
||||
return 0;
|
||||
merge();
|
||||
int diff = offsetBase.getOffsetTo(prop);
|
||||
if(diff<0)
|
||||
return 0;
|
||||
|
||||
for (vector<PropertyData::PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
|
||||
if(diff == It->Offset)
|
||||
return &(*It);
|
||||
|
||||
if(parentPropertyData)
|
||||
return parentPropertyData->findProperty(offsetBase,prop);
|
||||
|
||||
return 0;
|
||||
auto &index = propertyData.get<2>();
|
||||
auto it = index.find(diff);
|
||||
if(it!=index.end())
|
||||
return &(*it);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* PropertyData::getName(OffsetBase offsetBase,const Property* prop) const
|
||||
@@ -377,17 +498,6 @@ const char* PropertyData::getName(OffsetBase offsetBase,const Property* prop) co
|
||||
return Spec->Name;
|
||||
else
|
||||
return 0;
|
||||
/*
|
||||
|
||||
for(std::map<std::string,PropertySpec>::const_iterator pos = propertyData.begin();pos != propertyData.end();++pos)
|
||||
if(pos->second.Offset == diff)
|
||||
return pos->first.c_str();
|
||||
|
||||
if(parentPropertyData)
|
||||
return parentPropertyData->getName(container,prop);
|
||||
|
||||
return 0;
|
||||
*/
|
||||
}
|
||||
|
||||
short PropertyData::getType(OffsetBase offsetBase,const Property* prop) const
|
||||
@@ -398,19 +508,6 @@ short PropertyData::getType(OffsetBase offsetBase,const Property* prop) const
|
||||
return Spec->Type;
|
||||
else
|
||||
return 0;
|
||||
|
||||
/*
|
||||
const int diff = (int) ((char*)prop - (char*)container);
|
||||
|
||||
for(std::map<std::string,PropertySpec>::const_iterator pos = propertyData.begin();pos != propertyData.end();++pos)
|
||||
if(pos->second.Offset == diff)
|
||||
return pos->second.Type;
|
||||
|
||||
if(parentPropertyData)
|
||||
return parentPropertyData->getType(container,prop);
|
||||
|
||||
return 0;
|
||||
*/
|
||||
}
|
||||
|
||||
short PropertyData::getType(OffsetBase offsetBase,const char* name) const
|
||||
@@ -431,19 +528,6 @@ const char* PropertyData::getGroup(OffsetBase offsetBase,const Property* prop) c
|
||||
return Spec->Group;
|
||||
else
|
||||
return 0;
|
||||
|
||||
/*
|
||||
const int diff = (int) ((char*)prop - (char*)container);
|
||||
|
||||
for(std::map<std::string,PropertySpec>::const_iterator pos = propertyData.begin();pos != propertyData.end();++pos)
|
||||
if(pos->second.Offset == diff)
|
||||
return pos->second.Group;
|
||||
|
||||
if(parentPropertyData)
|
||||
return parentPropertyData->getGroup(container,prop);
|
||||
|
||||
return 0;
|
||||
*/
|
||||
}
|
||||
|
||||
const char* PropertyData::getGroup(OffsetBase offsetBase,const char* name) const
|
||||
@@ -476,8 +560,6 @@ const char* PropertyData::getDocumentation(OffsetBase offsetBase,const char* nam
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Property *PropertyData::getPropertyByName(OffsetBase offsetBase,const char* name) const
|
||||
{
|
||||
const PropertyData::PropertySpec* Spec = findProperty(offsetBase,name);
|
||||
@@ -486,53 +568,22 @@ Property *PropertyData::getPropertyByName(OffsetBase offsetBase,const char* name
|
||||
return (Property *) (Spec->Offset + offsetBase.getOffset());
|
||||
else
|
||||
return 0;
|
||||
/*
|
||||
std::map<std::string,PropertySpec>::const_iterator pos = propertyData.find(name);
|
||||
|
||||
if(pos != propertyData.end())
|
||||
{
|
||||
// calculate propterty by offset
|
||||
return (Property *) (pos->second.Offset + (char *)container);
|
||||
}else{
|
||||
if(parentPropertyData)
|
||||
return parentPropertyData->getPropertyByName(container,name);
|
||||
else
|
||||
return 0;
|
||||
}*/
|
||||
}
|
||||
|
||||
void PropertyData::getPropertyMap(OffsetBase offsetBase,std::map<std::string,Property*> &Map) const
|
||||
{
|
||||
for (vector<PropertyData::PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
|
||||
Map[It->Name] = (Property *) (It->Offset + offsetBase.getOffset());
|
||||
/*
|
||||
std::map<std::string,PropertySpec>::const_iterator pos;
|
||||
|
||||
for(pos = propertyData.begin();pos != propertyData.end();++pos)
|
||||
{
|
||||
Map[pos->first] = (Property *) (pos->second.Offset + (char *)container);
|
||||
}
|
||||
*/
|
||||
|
||||
if(parentPropertyData)
|
||||
parentPropertyData->getPropertyMap(offsetBase,Map);
|
||||
|
||||
merge();
|
||||
for(auto &spec : propertyData.get<0>())
|
||||
Map[spec.Name] = (Property *) (spec.Offset + offsetBase.getOffset());
|
||||
}
|
||||
|
||||
void PropertyData::getPropertyList(OffsetBase offsetBase,std::vector<Property*> &List) const
|
||||
{
|
||||
for (vector<PropertyData::PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
|
||||
List.push_back((Property *) (It->Offset + offsetBase.getOffset()) );
|
||||
|
||||
/* std::map<std::string,PropertySpec>::const_iterator pos;
|
||||
|
||||
for(pos = propertyData.begin();pos != propertyData.end();++pos)
|
||||
{
|
||||
List.push_back((Property *) (pos->second.Offset + (char *)container) );
|
||||
}*/
|
||||
if(parentPropertyData)
|
||||
parentPropertyData->getPropertyList(offsetBase,List);
|
||||
|
||||
merge();
|
||||
size_t base = List.size();
|
||||
List.reserve(base+propertyData.size());
|
||||
for (auto &spec : propertyData.get<0>())
|
||||
List.push_back((Property *) (spec.Offset + offsetBase.getOffset()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+75
-33
@@ -26,8 +26,11 @@
|
||||
|
||||
#include <map>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <Base/Persistence.h>
|
||||
|
||||
#include "DynamicProperty.h"
|
||||
|
||||
namespace Base {
|
||||
class Writer;
|
||||
}
|
||||
@@ -37,7 +40,6 @@ namespace App
|
||||
{
|
||||
class Property;
|
||||
class PropertyContainer;
|
||||
class DynamicProperty;
|
||||
class DocumentObject;
|
||||
class Extension;
|
||||
|
||||
@@ -45,20 +47,25 @@ enum PropertyType
|
||||
{
|
||||
Prop_None = 0, /*!< No special property type */
|
||||
Prop_ReadOnly = 1, /*!< Property is read-only in the editor */
|
||||
Prop_Transient = 2, /*!< Property won't be saved to file */
|
||||
Prop_Transient = 2, /*!< Property content won't be saved to file, but still saves name, type and status */
|
||||
Prop_Hidden = 4, /*!< Property won't appear in the editor */
|
||||
Prop_Output = 8, /*!< Modified property doesn't touch its parent container */
|
||||
Prop_NoRecompute = 16 /*!< Modified property doesn't touch its container for recompute */
|
||||
Prop_NoRecompute = 16,/*!< Modified property doesn't touch its container for recompute */
|
||||
Prop_NoPersist = 32,/*!< Property won't be saved to file at all */
|
||||
};
|
||||
|
||||
struct AppExport PropertyData
|
||||
{
|
||||
struct PropertySpec
|
||||
{
|
||||
const char* Name;
|
||||
const char * Name;
|
||||
const char * Group;
|
||||
const char * Docu;
|
||||
short Offset,Type;
|
||||
short Offset, Type;
|
||||
|
||||
inline PropertySpec(const char *name, const char *group, const char *doc, short offset, short type)
|
||||
:Name(name),Group(group),Docu(doc),Offset(offset),Type(type)
|
||||
{}
|
||||
};
|
||||
|
||||
//purpose of this struct is to be constructible from all acceptable container types and to
|
||||
@@ -82,10 +89,30 @@ struct AppExport PropertyData
|
||||
private:
|
||||
const void* m_container;
|
||||
};
|
||||
|
||||
// vector of all properties
|
||||
std::vector<PropertySpec> propertyData;
|
||||
const PropertyData* parentPropertyData;
|
||||
|
||||
// A multi index container for holding the property spec, with the following
|
||||
// index,
|
||||
// * a sequence, to preserve creation order
|
||||
// * hash index on property name
|
||||
// * hash index on property pointer offset
|
||||
mutable bmi::multi_index_container<
|
||||
PropertySpec,
|
||||
bmi::indexed_by<
|
||||
bmi::sequenced<>,
|
||||
bmi::hashed_unique<
|
||||
bmi::member<PropertySpec, const char*, &PropertySpec::Name>,
|
||||
CStringHasher,
|
||||
CStringHasher
|
||||
>,
|
||||
bmi::hashed_unique<
|
||||
bmi::member<PropertySpec, short, &PropertySpec::Offset>
|
||||
>
|
||||
>
|
||||
> propertyData;
|
||||
|
||||
mutable bool parentMerged = false;
|
||||
|
||||
const PropertyData* parentPropertyData;
|
||||
|
||||
void addProperty(OffsetBase offsetBase,const char* PropName, Property *Prop, const char* PropertyGroup= 0, PropertyType = Prop_None, const char* PropertyDocu= 0 );
|
||||
|
||||
@@ -103,6 +130,9 @@ struct AppExport PropertyData
|
||||
Property *getPropertyByName(OffsetBase offsetBase,const char* name) const;
|
||||
void getPropertyMap(OffsetBase offsetBase,std::map<std::string,Property*> &Map) const;
|
||||
void getPropertyList(OffsetBase offsetBase,std::vector<Property*> &List) const;
|
||||
|
||||
void merge(PropertyData *other=0) const;
|
||||
void split(PropertyData *other);
|
||||
};
|
||||
|
||||
|
||||
@@ -128,6 +158,8 @@ public:
|
||||
|
||||
virtual unsigned int getMemSize (void) const;
|
||||
|
||||
virtual std::string getFullName() const {return std::string();}
|
||||
|
||||
/// find a property by its name
|
||||
virtual Property *getPropertyByName(const char* name) const;
|
||||
/// get the name of a property
|
||||
@@ -162,33 +194,34 @@ public:
|
||||
virtual App::Property* addDynamicProperty(
|
||||
const char* type, const char* name=0,
|
||||
const char* group=0, const char* doc=0,
|
||||
short attr=0, bool ro=false, bool hidden=false){
|
||||
(void)type;
|
||||
(void)name;
|
||||
(void)group;
|
||||
(void)doc;
|
||||
(void)attr;
|
||||
(void)ro;
|
||||
(void)hidden;
|
||||
return 0;
|
||||
short attr=0, bool ro=false, bool hidden=false);
|
||||
|
||||
DynamicProperty::PropData getDynamicPropertyData(const Property* prop) const {
|
||||
return dynamicProps.getDynamicPropertyData(prop);
|
||||
}
|
||||
|
||||
virtual bool removeDynamicProperty(const char* name) {
|
||||
(void)name;
|
||||
return false;
|
||||
return dynamicProps.removeDynamicProperty(name);
|
||||
}
|
||||
virtual std::vector<std::string> getDynamicPropertyNames() const {
|
||||
return std::vector<std::string>();
|
||||
return dynamicProps.getDynamicPropertyNames();
|
||||
}
|
||||
virtual App::Property *getDynamicPropertyByName(const char* name) const {
|
||||
(void)name;
|
||||
return 0;
|
||||
}
|
||||
virtual void addDynamicProperties(const PropertyContainer*) {
|
||||
return dynamicProps.getDynamicPropertyByName(name);
|
||||
}
|
||||
|
||||
virtual void onPropertyStatusChanged(const Property &prop, unsigned long oldStatus);
|
||||
|
||||
virtual void Save (Base::Writer &writer) const;
|
||||
virtual void Restore(Base::XMLReader &reader);
|
||||
|
||||
const char *getPropertyPrefix() const {
|
||||
return _propertyPrefix.c_str();
|
||||
}
|
||||
|
||||
void setPropertyPrefix(const char *prefix) {
|
||||
_propertyPrefix = prefix;
|
||||
}
|
||||
|
||||
friend class Property;
|
||||
friend class DynamicProperty;
|
||||
@@ -212,25 +245,34 @@ private:
|
||||
PropertyContainer(const PropertyContainer&);
|
||||
PropertyContainer& operator = (const PropertyContainer&);
|
||||
|
||||
protected:
|
||||
DynamicProperty dynamicProps;
|
||||
|
||||
private:
|
||||
std::string _propertyPrefix;
|
||||
static PropertyData propertyData;
|
||||
};
|
||||
|
||||
/// Property define
|
||||
#define ADD_PROPERTY(_prop_, _defaultval_) \
|
||||
#define _ADD_PROPERTY(_name,_prop_, _defaultval_) \
|
||||
do { \
|
||||
this->_prop_.setValue _defaultval_;\
|
||||
this->_prop_.setContainer(this); \
|
||||
propertyData.addProperty(static_cast<App::PropertyContainer*>(this), #_prop_, &this->_prop_); \
|
||||
propertyData.addProperty(static_cast<App::PropertyContainer*>(this), _name, &this->_prop_); \
|
||||
} while (0)
|
||||
|
||||
#define ADD_PROPERTY(_prop_, _defaultval_) \
|
||||
_ADD_PROPERTY(#_prop_, _prop_, _defaultval_)
|
||||
|
||||
#define _ADD_PROPERTY_TYPE(_name,_prop_, _defaultval_, _group_,_type_,_Docu_) \
|
||||
do { \
|
||||
this->_prop_.setValue _defaultval_;\
|
||||
this->_prop_.setContainer(this); \
|
||||
propertyData.addProperty(static_cast<App::PropertyContainer*>(this), _name, &this->_prop_, (_group_),(_type_),(_Docu_)); \
|
||||
} while (0)
|
||||
|
||||
#define ADD_PROPERTY_TYPE(_prop_, _defaultval_, _group_,_type_,_Docu_) \
|
||||
do { \
|
||||
this->_prop_.setValue _defaultval_;\
|
||||
this->_prop_.setContainer(this); \
|
||||
propertyData.addProperty(static_cast<App::PropertyContainer*>(this), #_prop_, &this->_prop_, (_group_),(_type_),(_Docu_)); \
|
||||
} while (0)
|
||||
|
||||
_ADD_PROPERTY_TYPE(#_prop_,_prop_,_defaultval_,_group_,_type_,_Docu_)
|
||||
|
||||
|
||||
#define PROPERTY_HEADER(_class_) \
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user