Compare commits
102 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9fc7709969 | |||
| 12cced51f1 | |||
| 439c50c5c4 | |||
| 87a3bcf9db | |||
| 8910345149 | |||
| af4e54a6b7 | |||
| 90a0132533 | |||
| fe425c3f57 | |||
| 8098d72bb2 | |||
| ebde1e3d9a | |||
| d9b4006aa1 | |||
| 0b78b5ade8 | |||
| 4750f06058 | |||
| 0cb8f7e240 | |||
| 49d7d77274 | |||
| d443d5b065 | |||
| 013a1d5f3a | |||
| 180c39709a | |||
| 601bcd5e3d | |||
| dcb380e1ca | |||
| cfe641fb7d | |||
| d384c9d8cb | |||
| 3620b68b21 | |||
| 20e70f84e1 | |||
| e28b1ce1ad | |||
| c8efc26982 | |||
| 9abe7b4af1 | |||
| 27f077c62b | |||
| eabd052c17 | |||
| 706b01f607 | |||
| 8a2f665430 | |||
| 9fe630e0c3 | |||
| 91f015b132 | |||
| ad630cbb9c | |||
| 51ac15b375 | |||
| db1469f219 | |||
| 1665dec4e4 | |||
| 99e414efab | |||
| 7cb3815fb1 | |||
| 5c6bf5461d | |||
| 3076ce66be | |||
| d59471b6bd | |||
| 4ba5504e7a | |||
| 7db3f901fd | |||
| f4665aa7b5 | |||
| 6d8b98d471 | |||
| 03f506c5a5 | |||
| d90c88f0fc | |||
| df8c6b5db9 | |||
| 5455306a4a | |||
| d1476c819c | |||
| 88a74cfa3a | |||
| 5e818e961c | |||
| fdef6c35e7 | |||
| 4ac848aab0 | |||
| e1fdc27e7c | |||
| 0caccde398 | |||
| 244dffcfb7 | |||
| 5328e74e3d | |||
| 3c48b4a950 | |||
| a78c31be69 | |||
| c2495411b7 | |||
| 1a8e2fb333 | |||
| 73c3c78474 | |||
| 509c881ea0 | |||
| d690dcb1d3 | |||
| 493829b376 | |||
| 0819d776d7 | |||
| 79aca8950f | |||
| 8efe9964e7 | |||
| eda87a7efe | |||
| e1815f10ff | |||
| 7d39a9d59a | |||
| baa47bd606 | |||
| 51b37da53e | |||
| 8ce3c4a1cc | |||
| 037ce3bffd | |||
| b9306c6f63 | |||
| 7b2b972efa | |||
| 6d66721582 | |||
| 448235cd2b | |||
| a729bb4bc1 | |||
| 03fc08d02e | |||
| e44c1ce75a | |||
| 4083c7c616 | |||
| 76a84f63ab | |||
| 065315d7a4 | |||
| 6206ed2c51 | |||
| 451072f0d7 | |||
| 0b232ac1ff | |||
| 737cab44f2 | |||
| a06be5de68 | |||
| 0ea238f90c | |||
| 1182be91bc | |||
| e561950ff8 | |||
| bf98221885 | |||
| f7939beee0 | |||
| 0702616895 | |||
| eb0d7ba6a5 | |||
| eaf76e4d0a | |||
| ba24ab8a49 | |||
| b0e11e286b |
@@ -58,7 +58,7 @@ jobs:
|
||||
logdir: /tmp/log/
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ permissions:
|
||||
contents: write
|
||||
actions: write
|
||||
|
||||
concurrency:
|
||||
group: build-release-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
upload_src:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -17,11 +21,11 @@ jobs:
|
||||
build_tag: ${{ steps.get_tag.outputs.build_tag }}
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout Source
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
@@ -29,7 +33,7 @@ jobs:
|
||||
fetch-tags: true
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: get tag and create release if weekly
|
||||
- name: Get tag and create release if weekly
|
||||
id: get_tag
|
||||
shell: bash -l {0}
|
||||
env:
|
||||
@@ -39,7 +43,10 @@ jobs:
|
||||
export BUILD_TAG="${{ github.event.release.tag_name }}"
|
||||
else
|
||||
export BUILD_TAG=weekly-$(date "+%Y.%m.%d")
|
||||
gh release create ${BUILD_TAG} --title "Development Build ${BUILD_TAG}" -F .github/workflows/weekly-build-notes.md --prerelease || true
|
||||
gh release create ${BUILD_TAG} --title "Development Build ${BUILD_TAG}" \
|
||||
-F .github/workflows/weekly-build-notes.md \
|
||||
--prerelease || \
|
||||
gh release view ${BUILD_TAG} > /dev/null # fail if it doesn't exist
|
||||
fi
|
||||
echo "BUILD_TAG=${BUILD_TAG}" >> "$GITHUB_ENV"
|
||||
echo "build_tag=${BUILD_TAG}" >> "$GITHUB_OUTPUT"
|
||||
@@ -105,7 +112,7 @@ jobs:
|
||||
environment: weekly-build
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -118,7 +125,7 @@ jobs:
|
||||
remove-android: 'true' # (frees ~9 GB)
|
||||
remove-cached-tools: 'true' # (frees ~8.3 GB)
|
||||
|
||||
- name: Set Platform Environment Variables
|
||||
- name: Set platform environment variables
|
||||
shell: bash -l {0}
|
||||
env:
|
||||
OPERATING_SYSTEM: ${{ runner.os }}
|
||||
@@ -128,7 +135,7 @@ jobs:
|
||||
echo 'RATTLER_CACHE_DIR=D:\rattler' >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Checkout Source
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
@@ -142,7 +149,7 @@ jobs:
|
||||
cache: false
|
||||
|
||||
- name: Install the Apple certificate and provisioning profile
|
||||
id: get_cert
|
||||
id: macos_get_cert
|
||||
if: runner.os == 'macOS'
|
||||
env:
|
||||
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
|
||||
@@ -150,7 +157,6 @@ jobs:
|
||||
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
|
||||
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
|
||||
DEVELOPER_TEAM_ID: ${{ secrets.DEVELOPER_TEAM_ID }}
|
||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||||
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
|
||||
run: |
|
||||
if [ -z "$BUILD_CERTIFICATE_BASE64" ]; then
|
||||
@@ -186,23 +192,81 @@ jobs:
|
||||
|
||||
xcrun notarytool store-credentials "FreeCAD" --keychain "$KEYCHAIN_PATH" --apple-id "${APPLE_ID}" --password "${APP_SPECIFIC_PASSWORD}" --team-id "${DEVELOPER_TEAM_ID}"
|
||||
|
||||
- name: Build and Release Packages
|
||||
- name: Setup .NET 10 SDK
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5.1.0
|
||||
with:
|
||||
dotnet-version: '10.0.103'
|
||||
|
||||
- name: Install sign tool
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
# NOTE: The sign tool is ONLY available as a prerelease package, they never issue real releases.
|
||||
dotnet tool install --global --prerelease sign
|
||||
echo "$env:USERPROFILE\.dotnet\tools" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
dotnet --info
|
||||
sign --version
|
||||
|
||||
- name: Build packages
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
|
||||
SIGN_RELEASE: ${{ steps.get_cert.outputs.has_cert }}
|
||||
TARGET_PLATFORM: ${{ matrix.target }}
|
||||
MAKE_INSTALLER: "true"
|
||||
UPLOAD_RELEASE: "true"
|
||||
BUILD_TAG: ${{ needs.upload_src.outputs.build_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ "${{ runner.os }}" == "macOS" ]]; then
|
||||
export MACOS_DEPLOYMENT_TARGET="${{ matrix.deploy_target }}"
|
||||
fi
|
||||
python3 package/scripts/write_version_info.py ../freecad_version.txt
|
||||
cd package/rattler-build
|
||||
pixi install
|
||||
|
||||
- name: Azure login for Windows build code signing
|
||||
id: azure_login
|
||||
if: runner.os == 'Windows'
|
||||
continue-on-error: true
|
||||
uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0
|
||||
with:
|
||||
creds: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","clientSecret":"${{ secrets.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ secrets.AZURE_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}'
|
||||
|
||||
- name: Release packages with optional code-signing on Windows and macOS
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TARGET_PLATFORM: ${{ matrix.target }}
|
||||
MAKE_INSTALLER: "true"
|
||||
UPLOAD_RELEASE: "true"
|
||||
BUILD_TAG: ${{ needs.upload_src.outputs.build_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
export MACOS_SIGN_RELEASE=0
|
||||
export WINDOWS_SIGN_RELEASE=0
|
||||
|
||||
case "${RUNNER_OS}" in
|
||||
Windows)
|
||||
export WINDOWS_SIGN_RELEASE="${{ steps.azure_login.outcome }}"
|
||||
export WINDOWS_AZURE_ENDPOINT="${{ vars.AZURE_TRUSTED_SIGNING_ENDPOINT }}"
|
||||
export WINDOWS_AZURE_CERTIFICATE_PROFILE="${{ vars.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }}"
|
||||
export WINDOWS_AZURE_SIGNING_ACCOUNT="${{ vars.AZURE_TRUSTED_SIGNING_ACCOUNT }}"
|
||||
|
||||
# For good measure, normalize the azure_login result to 1 or 0
|
||||
if [[ "${WINDOWS_SIGN_RELEASE:-}" == "success" ]]; then
|
||||
export WINDOWS_SIGN_RELEASE=1
|
||||
else
|
||||
export WINDOWS_SIGN_RELEASE=0
|
||||
fi
|
||||
;;
|
||||
|
||||
macOS)
|
||||
export MACOS_SIGN_RELEASE="${{ steps.macos_get_cert.outputs.has_cert }}"
|
||||
export MACOS_SIGNING_KEY_ID="${{ secrets.SIGNING_KEY_ID }}"
|
||||
export MACOS_DEPLOYMENT_TARGET="${{ matrix.deploy_target }}"
|
||||
;;
|
||||
esac
|
||||
|
||||
cd package/rattler-build
|
||||
pixi run -e package create_bundle
|
||||
|
||||
## Needed if running on a self-hosted runner:
|
||||
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4
|
||||
uses: actions/dependency-review-action@05fe4576374b728f0c523d6a13d64c25081e0803 # v4
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
echo "last_month=$first_day..$last_day" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Run issue-metrics tool
|
||||
uses: github/issue-metrics@41a7961f701cc64490f32e143af8ef479b93e87d # v4.1.0
|
||||
uses: github/issue-metrics@6a35322ff89cee3e1a594d282c27eb34bffa9174 # v4.1.1
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SEARCH_QUERY: 'repo:FreeCAD/FreeCAD is:issue created:${{ env.last_month }}'
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
run: |
|
||||
mkdir -p ${{ env.artifactsDownloadDir }}
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
path: ${{ env.artifactsDownloadDir }}
|
||||
- name: Save input data to file
|
||||
@@ -119,7 +119,7 @@ jobs:
|
||||
cat report.md >> $GITHUB_STEP_SUMMARY
|
||||
- name: Delete used artifacts
|
||||
continue-on-error: true
|
||||
uses: geekyeggo/delete-artifact@f275313e70c08f6120db482d7a6b98377786765b # v5.1.0
|
||||
uses: geekyeggo/delete-artifact@176a747ab7e287e3ff4787bf8a148716375ca118 # v6.0.0
|
||||
with:
|
||||
name: |
|
||||
${{ env.usedArtifacts }}
|
||||
|
||||
@@ -76,9 +76,9 @@ if [ -d "${conda_env}/PlugIns" ]; then
|
||||
mv ${conda_env}/PlugIns ${conda_env}/..
|
||||
fi
|
||||
|
||||
if [[ "${SIGN_RELEASE}" == "true" ]]; then
|
||||
if [[ "${MACOS_SIGN_RELEASE}" == "true" ]]; then
|
||||
# create the signed dmg
|
||||
../../scripts/macos_sign_and_notarize.zsh -p "FreeCAD" -k ${SIGNING_KEY_ID} -o "${version_name}.dmg"
|
||||
../../scripts/macos_sign_and_notarize.zsh -p "FreeCAD" -k ${MACOS_SIGNING_KEY_ID} -o "${version_name}.dmg"
|
||||
else
|
||||
# Ad-hoc sign for local builds (required for QuickLook extensions to register)
|
||||
if [ -d "FreeCAD.app/Contents/PlugIns" ]; then
|
||||
|
||||
@@ -90,7 +90,7 @@ environments:
|
||||
- conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/linux-64/icu-75.1-he02047a_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/linux-64/ifcopenshell-0.8.4-py311hfa9dd64_2.conda
|
||||
- conda: https://prefix.dev/conda-forge/linux-64/ifcopenshell-0.8.2-py311hfea35e5_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/linux-64/imath-3.1.12-h7955e40_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/linux-64/jasper-4.2.8-he3c4edf_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/linux-64/jsoncpp-1.9.6-hf42df4d_1.conda
|
||||
@@ -422,7 +422,7 @@ environments:
|
||||
- conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/linux-aarch64/ifcopenshell-0.8.4-py311h9ffcc0b_2.conda
|
||||
- conda: https://prefix.dev/conda-forge/linux-aarch64/ifcopenshell-0.8.2-py311h9a0e4cb_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/linux-aarch64/imath-3.1.12-hf428078_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/linux-aarch64/jasper-4.2.8-h27a9ab5_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/linux-aarch64/jsoncpp-1.9.6-h34915d9_1.conda
|
||||
@@ -741,7 +741,7 @@ environments:
|
||||
- conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/osx-64/ifcopenshell-0.8.4-py311hbba92a7_2.conda
|
||||
- conda: https://prefix.dev/conda-forge/osx-64/ifcopenshell-0.8.2-py311h2fdbcd7_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/osx-64/imath-3.1.12-h2016aa1_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/osx-64/jasper-4.2.8-h9ce442b_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/osx-64/jsoncpp-1.9.6-h466cfd8_1.conda
|
||||
@@ -1011,7 +1011,7 @@ environments:
|
||||
- conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/osx-arm64/ifcopenshell-0.8.4-py311h5dd451c_2.conda
|
||||
- conda: https://prefix.dev/conda-forge/osx-arm64/ifcopenshell-0.8.2-py311h30f7335_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/osx-arm64/imath-3.1.12-h025cafa_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/osx-arm64/jasper-4.2.8-hc0e5025_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/osx-arm64/jsoncpp-1.9.6-h726d253_1.conda
|
||||
@@ -1274,7 +1274,7 @@ environments:
|
||||
- conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/win-64/icu-75.1-he0c23c2_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/win-64/ifcopenshell-0.8.4-py311h5252d0d_2.conda
|
||||
- conda: https://prefix.dev/conda-forge/win-64/ifcopenshell-0.8.2-py311ha1ff2f0_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/win-64/imath-3.1.12-hbb528cf_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/win-64/jasper-4.2.8-h8ad263b_0.conda
|
||||
- conda: https://prefix.dev/conda-forge/win-64/jsoncpp-1.9.6-hda1637e_1.conda
|
||||
@@ -6121,7 +6121,7 @@ packages:
|
||||
license_family: BSD
|
||||
size: 50721
|
||||
timestamp: 1760286526795
|
||||
- conda: https://prefix.dev/conda-forge/linux-64/ifcopenshell-0.8.4-py311hfa9dd64_2.conda
|
||||
- conda: https://prefix.dev/conda-forge/linux-64/ifcopenshell-0.8.2-py311hfea35e5_0.conda
|
||||
sha256: 45ce07c412cb1d4b8bfd0fde785e9f3751381b4e3d6a28686a174559f0628e69
|
||||
md5: 25bcf0826fc3b896d07afbe35982a500
|
||||
depends:
|
||||
@@ -6145,7 +6145,7 @@ packages:
|
||||
license_family: LGPL
|
||||
size: 43852878
|
||||
timestamp: 1745412691720
|
||||
- conda: https://prefix.dev/conda-forge/linux-aarch64/ifcopenshell-0.8.4-py311h9ffcc0b_2.conda
|
||||
- conda: https://prefix.dev/conda-forge/linux-aarch64/ifcopenshell-0.8.2-py311h9a0e4cb_0.conda
|
||||
sha256: 5b0d7760899ffa8ae91169bff49c9d6cd435b1cb8957e7622d9bd765a9c2d577
|
||||
md5: fc1ebe0deebc32d2d42b8dc257afb836
|
||||
depends:
|
||||
@@ -6168,7 +6168,7 @@ packages:
|
||||
license_family: LGPL
|
||||
size: 46059599
|
||||
timestamp: 1745412697613
|
||||
- conda: https://prefix.dev/conda-forge/osx-64/ifcopenshell-0.8.4-py311hbba92a7_2.conda
|
||||
- conda: https://prefix.dev/conda-forge/osx-64/ifcopenshell-0.8.2-py311h2fdbcd7_0.conda
|
||||
sha256: d52f4bd7cec3cf1cbcd7cf9c6a21ec6d2331425972c4cd7f88d3df64a4a3cd72
|
||||
md5: d0352d59f66ec49a974177e1efc749df
|
||||
depends:
|
||||
@@ -6190,7 +6190,7 @@ packages:
|
||||
license_family: LGPL
|
||||
size: 39452121
|
||||
timestamp: 1745412590610
|
||||
- conda: https://prefix.dev/conda-forge/osx-arm64/ifcopenshell-0.8.4-py311h5dd451c_2.conda
|
||||
- conda: https://prefix.dev/conda-forge/osx-arm64/ifcopenshell-0.8.2-py311h30f7335_0.conda
|
||||
sha256: 15d5d080aa54c1b5aeed0e4827d5a6b4521b8a2599e3bc06af5318575d2b7a8f
|
||||
md5: b550b7b3c4a8f543fb3c0c15f8ec5967
|
||||
depends:
|
||||
@@ -6213,7 +6213,7 @@ packages:
|
||||
license_family: LGPL
|
||||
size: 40077397
|
||||
timestamp: 1745412613454
|
||||
- conda: https://prefix.dev/conda-forge/win-64/ifcopenshell-0.8.4-py311h5252d0d_2.conda
|
||||
- conda: https://prefix.dev/conda-forge/win-64/ifcopenshell-0.8.2-py311ha1ff2f0_0.conda
|
||||
sha256: 4e3c4d0d3749b761267b9ac9f9c3262c2b690d1908a6a6db734dcd4eecd43b2e
|
||||
md5: e178c988f7034d08d27fe14a62bdbe12
|
||||
depends:
|
||||
|
||||
@@ -100,7 +100,7 @@ requirements:
|
||||
|
||||
host:
|
||||
- coin3d
|
||||
- eigen
|
||||
- eigen >=3.3,<5
|
||||
- fmt
|
||||
- freetype
|
||||
- hdf5
|
||||
|
||||
@@ -44,6 +44,9 @@ find ${copy_dir} -name \*arm\*.exe -delete # arm binaries that fail to extract u
|
||||
mv ${copy_dir}/bin/Lib/ssl.py .ssl-orig.py
|
||||
cp ssl-patch.py ${copy_dir}/bin/Lib/ssl.py
|
||||
|
||||
# Turn off the echo before we start actually calling "echo"
|
||||
set +x
|
||||
|
||||
echo '[Paths]' >> ${copy_dir}/bin/qt6.conf
|
||||
echo 'Prefix = ../lib/qt6' >> ${copy_dir}/bin/qt6.conf
|
||||
|
||||
@@ -67,6 +70,64 @@ sed -i '1s/.*/\nLIST OF PACKAGES:/' ${copy_dir}/packages.txt
|
||||
|
||||
mv ${copy_dir} ${version_name}
|
||||
|
||||
|
||||
# Sign the EXE, DLL, and PYD files (if we can access the Azure account for signing):
|
||||
set -euo pipefail
|
||||
SIGN_DIR="${version_name}"
|
||||
|
||||
|
||||
if [[ "${WINDOWS_SIGN_RELEASE:-0}" == "1" ]]; then
|
||||
TENANT="$(az account show --query tenantId -o tsv)"
|
||||
export AZURE_IDENTITY_DISABLE_WORKLOAD_IDENTITY=true
|
||||
export AZURE_IDENTITY_DISABLE_MANAGED_IDENTITY=true
|
||||
unset AZURE_IDENTITY_LOGGING_ENABLED
|
||||
|
||||
if az account get-access-token \
|
||||
--tenant "$TENANT" \
|
||||
--scope "https://codesigning.azure.net/.default" \
|
||||
>/dev/null 2>&1;
|
||||
then
|
||||
echo "Azure Artifact Signing access confirmed. Beginning signing process..."
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
FILES=(
|
||||
"$SIGN_DIR"/*.exe
|
||||
"$SIGN_DIR"/bin/*.exe
|
||||
"$SIGN_DIR"/bin/*.dll
|
||||
"$SIGN_DIR"/bin/*.pyd
|
||||
)
|
||||
|
||||
count=0
|
||||
total=${#FILES[@]}
|
||||
echo "Signing $total files"
|
||||
for f in "${FILES[@]}"; do
|
||||
((count+=1))
|
||||
echo "Signing [$count/$total]: $f"
|
||||
sign code artifact-signing \
|
||||
--artifact-signing-endpoint "${WINDOWS_AZURE_ENDPOINT}" \
|
||||
--artifact-signing-certificate-profile "${WINDOWS_AZURE_CERTIFICATE_PROFILE}" \
|
||||
--artifact-signing-account "${WINDOWS_AZURE_SIGNING_ACCOUNT}" \
|
||||
--timestamp-url https://timestamp.acs.microsoft.com \
|
||||
--timestamp-digest sha256 \
|
||||
"$f" >/dev/null 2>&1
|
||||
|
||||
# Output was redirected to /dev/null because Azure authentication is absurdly noisy, with constant misleading
|
||||
# "failure" messages about Managed Identity authentication failing. We don't use, or want to use, that
|
||||
# authentication, and the fact that it fails is not a problem as long as the real authentication succeeds.
|
||||
done
|
||||
|
||||
# Manually check the important one!
|
||||
signtool verify -pa "$SIGN_DIR/bin/FreeCAD.exe"
|
||||
|
||||
echo "Signing completed."
|
||||
else
|
||||
echo "Signing requested, but no Azure Artifact Signing available -- skipping signing."
|
||||
fi
|
||||
else
|
||||
echo "Not logged into Azure -- skipping signing."
|
||||
fi
|
||||
|
||||
7z a -t7z -mx9 -mmt=${NUMBER_OF_PROCESSORS} ${version_name}.7z ${version_name} -bb
|
||||
# create hash
|
||||
sha256sum ${version_name}.7z > ${version_name}.7z-SHA256.txt
|
||||
@@ -88,6 +149,28 @@ if [ "${MAKE_INSTALLER}" == "true" ]; then
|
||||
-X'SetCompressor /FINAL lzma' \
|
||||
../../WindowsInstaller/FreeCAD-installer.nsi
|
||||
mv ../../WindowsInstaller/${version_name}-installer.exe .
|
||||
echo "Created installer ${version_name}-installer.exe"
|
||||
|
||||
# See if we can sign the installer exe as well:
|
||||
if [[ "${WINDOWS_SIGN_RELEASE:-0}" == "1" ]] && \
|
||||
az account get-access-token \
|
||||
--tenant "$TENANT" \
|
||||
--scope "https://codesigning.azure.net/.default" \
|
||||
>/dev/null 2>&1;
|
||||
then
|
||||
echo "Signing the installer..."
|
||||
sign code artifact-signing \
|
||||
--artifact-signing-endpoint "${WINDOWS_AZURE_ENDPOINT}" \
|
||||
--artifact-signing-certificate-profile "${WINDOWS_AZURE_CERTIFICATE_PROFILE}" \
|
||||
--artifact-signing-account "${WINDOWS_AZURE_SIGNING_ACCOUNT}" \
|
||||
--timestamp-url https://timestamp.acs.microsoft.com \
|
||||
--timestamp-digest sha256 \
|
||||
${version_name}-installer.exe >/dev/null 2>&1 \
|
||||
|| { echo "Signing the installer failed!"; exit 1; }
|
||||
else
|
||||
echo "No code signing available, leaving the installer unsigned"
|
||||
fi
|
||||
|
||||
sha256sum ${version_name}-installer.exe > ${version_name}-installer.exe-SHA256.txt
|
||||
else
|
||||
echo "Error: Failed to get NsProcess plugin. Aborting installer creation..."
|
||||
@@ -96,8 +179,10 @@ if [ "${MAKE_INSTALLER}" == "true" ]; then
|
||||
fi
|
||||
|
||||
if [ "${UPLOAD_RELEASE}" == "true" ]; then
|
||||
echo "Uploading the release..."
|
||||
gh release upload --clobber ${BUILD_TAG} "${version_name}.7z" "${version_name}.7z-SHA256.txt"
|
||||
if [ "${MAKE_INSTALLER}" == "true" ]; then
|
||||
gh release upload --clobber ${BUILD_TAG} "${version_name}-installer.exe" "${version_name}-installer.exe-SHA256.txt"
|
||||
fi
|
||||
echo "Done uploading"
|
||||
fi
|
||||
|
||||
@@ -19,11 +19,12 @@ debugpy = "*"
|
||||
defusedxml = "*"
|
||||
docutils = "*"
|
||||
doxygen = "*"
|
||||
eigen = "*"
|
||||
eigen = ">=3.3,<5"
|
||||
fmt = "*"
|
||||
freetype = "*"
|
||||
git = "*"
|
||||
graphviz = "*"
|
||||
harfbuzz = "*"
|
||||
hdf5 = "*"
|
||||
ifcopenshell = "*"
|
||||
lark = "*"
|
||||
|
||||
+12
-1
@@ -587,6 +587,15 @@ Document* Application::getDocument(const char *Name) const
|
||||
|
||||
return pos->second;
|
||||
}
|
||||
Document* Application::getDocumentOrActive(const char *Name) const
|
||||
{
|
||||
if (!Base::Tools::isNullOrEmpty(Name)) {
|
||||
return getDocument(Name);
|
||||
}
|
||||
else {
|
||||
return getActiveDocument();
|
||||
}
|
||||
}
|
||||
|
||||
const char * Application::getDocumentName(const Document* doc) const
|
||||
{
|
||||
@@ -2492,7 +2501,9 @@ void processProgramOptions(const boost::program_options::variables_map& vm, std:
|
||||
void Application::initConfig(int argc, char ** argv)
|
||||
{
|
||||
// find the home path....
|
||||
mConfig["AppHomePath"] = ApplicationDirectories::findHomePath(argv[0]).string();
|
||||
mConfig["AppHomePath"] = Base::FileInfo::pathToString(
|
||||
ApplicationDirectories::findHomePath(argv[0])
|
||||
);
|
||||
|
||||
// Version of the application extracted from SubWCRef into src/Build/Version.h
|
||||
// We only set these keys if not yet defined. Therefore it suffices to search
|
||||
|
||||
+52
-27
@@ -35,10 +35,12 @@
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
#include <Base/Observer.h>
|
||||
#include <Base/Parameter.h>
|
||||
#include <Base/ProgressIndicator.h>
|
||||
#include "TransactionDefs.h"
|
||||
|
||||
// forward declarations
|
||||
using PyObject = struct _object;
|
||||
@@ -203,6 +205,8 @@ public:
|
||||
*/
|
||||
App::Document* getDocument(const char *Name) const;
|
||||
|
||||
App::Document* getDocumentOrActive(const char *Name) const;
|
||||
|
||||
/// %Path matching modes for getDocumentByPath()
|
||||
enum class PathMatchMode
|
||||
{
|
||||
@@ -289,9 +293,11 @@ public:
|
||||
/**
|
||||
* @brief Setup a pending application-wide active transaction.
|
||||
*
|
||||
* 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
|
||||
* Call this function to setup a transaction in the currently active document
|
||||
* if no document is active, a global transaction is created. If the current
|
||||
* active document already has a transaction setup it will either commit the
|
||||
* current transaction or rename it, depending on the tmpName flag of the
|
||||
* currently setup transaction. 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.
|
||||
@@ -303,32 +309,45 @@ public:
|
||||
*
|
||||
* @return The new transaction ID.
|
||||
*/
|
||||
int setActiveTransaction(const char* name, bool persist = false);
|
||||
|
||||
/**
|
||||
* @brief Get the current active transaction name and ID.
|
||||
int setActiveTransaction(TransactionName name);
|
||||
|
||||
/// Return the current global transaction name and ID if such a global transaction is
|
||||
/// setup (uncommon)
|
||||
std::string getActiveTransaction(int *tid=nullptr) const;
|
||||
|
||||
int openGlobalTransaction(TransactionName name);
|
||||
int getGlobalTransaction() const;
|
||||
|
||||
bool transactionIsActive(int tid) const;
|
||||
std::string getTransactionName(int tid) const;
|
||||
bool transactionTmpName(int tid) const;
|
||||
Document* transactionInitiator(int tid) const;
|
||||
std::optional<TransactionDescription> transactionDescription(int tid) const;
|
||||
void setTransactionDescription(int tid, const TransactionDescription& desc);
|
||||
void setTransactionName(int tid, const TransactionName& name);
|
||||
|
||||
/** Commit/abort current active transactions
|
||||
*
|
||||
* If there is no active transaction, an empty string is returned.
|
||||
*
|
||||
* @param[out] tid If not `nullptr`, the current active transaction ID is
|
||||
* returned through this pointer.
|
||||
* @return The current active transaction name.
|
||||
*/
|
||||
const char* getActiveTransaction(int* tid = nullptr) const;
|
||||
|
||||
/**
|
||||
* @brief Commit/abort current active transactions.
|
||||
*
|
||||
* @param[in] abort: whether to abort or commit the transactions
|
||||
* @param[in] id: by default 0 meaning that the current active transaction ID is used.
|
||||
*
|
||||
* Besides 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.
|
||||
* @param[in] id: by default 0 meaning that the current global transaction ID is used.
|
||||
*
|
||||
* 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
|
||||
* returns true if it succeeded in closing the transaction
|
||||
*/
|
||||
void closeActiveTransaction(bool abort=false, int id=0);
|
||||
/// @}
|
||||
bool closeActiveTransaction(TransactionCloseMode mode = TransactionCloseMode::Commit, int id=0);
|
||||
|
||||
/// Internally call closeActiveTransaction(), but it makes the call site clearer
|
||||
bool commitTransaction(int tid);
|
||||
bool abortTransaction(int tid);
|
||||
|
||||
|
||||
//@}
|
||||
|
||||
// NOLINTBEGIN
|
||||
// clang-format off
|
||||
@@ -1040,10 +1059,16 @@ private:
|
||||
|
||||
friend class AutoTransaction;
|
||||
|
||||
std::string _activeTransactionName;
|
||||
int _activeTransactionID{0};
|
||||
int _activeTransactionGuard{0};
|
||||
bool _activeTransactionTmpName{false};
|
||||
std::map<int, TransactionDescription> _activeTransactionDescriptions; // Maps transaction ID to transaction name
|
||||
|
||||
int currentlyClosingID {0};
|
||||
|
||||
// This is the transaction ID for a global transaction
|
||||
// Documents will take this ID if it is non-zero
|
||||
// and generate their own otherwise
|
||||
int _globalTransactionID { 0 };
|
||||
bool _globalTransactionTmpName {false};
|
||||
std::string _globalTransactionName;
|
||||
|
||||
Base::ProgressIndicator _progressIndicator;
|
||||
|
||||
|
||||
@@ -80,12 +80,16 @@ const fs::path& ApplicationDirectories::getTempPath() const {
|
||||
return this->_temp;
|
||||
}
|
||||
|
||||
fs::path ApplicationDirectories::getTempFileName(const std::string & filename) const {
|
||||
fs::path ApplicationDirectories::getTempFileName(const std::string & filename) const
|
||||
{
|
||||
auto tempPath = Base::FileInfo::pathToString(getTempPath());
|
||||
if (filename.empty()) {
|
||||
return Base::FileInfo::getTempFileName(nullptr, tempPath.c_str());
|
||||
return Base::FileInfo::stringToPath(Base::FileInfo::getTempFileName(nullptr, tempPath.c_str())
|
||||
);
|
||||
}
|
||||
return Base::FileInfo::getTempFileName(filename.c_str(), tempPath.c_str());
|
||||
return Base::FileInfo::stringToPath(
|
||||
Base::FileInfo::getTempFileName(filename.c_str(), tempPath.c_str())
|
||||
);
|
||||
}
|
||||
|
||||
const fs::path& ApplicationDirectories::getUserCachePath() const
|
||||
@@ -182,7 +186,7 @@ void ApplicationDirectories::configurePaths(std::map<std::string,std::string>& m
|
||||
bool keepDeprecatedPaths = mConfig.contains("KeepDeprecatedPaths");
|
||||
|
||||
// std paths
|
||||
_home = fs::path(mConfig.at("AppHomePath"));
|
||||
_home = Base::FileInfo::stringToPath(mConfig.at("AppHomePath"));
|
||||
mConfig["BinPath"] = mConfig.at("AppHomePath") + "bin" + PATHSEP;
|
||||
mConfig["DocPath"] = mConfig.at("AppHomePath") + "doc" + PATHSEP;
|
||||
|
||||
@@ -298,7 +302,7 @@ void ApplicationDirectories::configureResourceDirectory(const std::map<std::stri
|
||||
_resource = Base::FileInfo::stringToPath(mConfig.at("AppHomePath")) / path;
|
||||
}
|
||||
#else
|
||||
_resource = fs::path(mConfig.at("AppHomePath"));
|
||||
_resource = Base::FileInfo::stringToPath(mConfig.at("AppHomePath"));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -242,9 +242,7 @@ PyMethodDef ApplicationPy::Methods[] = {
|
||||
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"
|
||||
"persist(False): This parameter has no effect and is kept for compatibility reasonss"
|
||||
"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."},
|
||||
@@ -1201,14 +1199,14 @@ PyObject* ApplicationPy::sGetDependentObjects(PyObject* /*self*/, PyObject* args
|
||||
PyObject* ApplicationPy::sSetActiveTransaction(PyObject* /*self*/, PyObject* args)
|
||||
{
|
||||
char* name {};
|
||||
PyObject* persist = Py_False;
|
||||
PyObject* persist = Py_False; // Not used
|
||||
if (!PyArg_ParseTuple(args, "s|O!", &name, &PyBool_Type, &persist)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PY_TRY
|
||||
{
|
||||
Py::Long ret(GetApplication().setActiveTransaction(name, Base::asBoolean(persist)));
|
||||
Py::Long ret(GetApplication().setActiveTransaction(TransactionName {.name=name, .temporary=false}));
|
||||
return Py::new_reference_to(ret);
|
||||
}
|
||||
PY_CATCH;
|
||||
@@ -1223,8 +1221,8 @@ PyObject* ApplicationPy::sGetActiveTransaction(PyObject* /*self*/, PyObject* arg
|
||||
PY_TRY
|
||||
{
|
||||
int id = 0;
|
||||
const char* name = GetApplication().getActiveTransaction(&id);
|
||||
if (!name || id <= 0) {
|
||||
std::string name = GetApplication().getActiveTransaction(&id);
|
||||
if (name.empty() || id <= 0) {
|
||||
Py_Return;
|
||||
}
|
||||
Py::Tuple ret(2);
|
||||
@@ -1245,7 +1243,10 @@ PyObject* ApplicationPy::sCloseActiveTransaction(PyObject* /*self*/, PyObject* a
|
||||
|
||||
PY_TRY
|
||||
{
|
||||
GetApplication().closeActiveTransaction(Base::asBoolean(abort), id);
|
||||
TransactionCloseMode mode = Base::asBoolean(abort)
|
||||
? TransactionCloseMode::Abort
|
||||
: TransactionCloseMode::Commit;
|
||||
GetApplication().closeActiveTransaction(mode, id);
|
||||
Py_Return;
|
||||
}
|
||||
PY_CATCH;
|
||||
|
||||
+167
-167
@@ -35,204 +35,217 @@ FC_LOG_LEVEL_INIT("App", true, true)
|
||||
|
||||
using namespace App;
|
||||
|
||||
static int _TransactionLock;
|
||||
static int _TransactionClosed;
|
||||
|
||||
AutoTransaction::AutoTransaction(const char* name, bool tmpName)
|
||||
AutoTransaction::AutoTransaction(int tid)
|
||||
: tid(tid)
|
||||
{
|
||||
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(Document* doc, const std::string& name)
|
||||
: AutoTransaction(doc->openTransaction(name))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
close(TransactionCloseMode::Commit);
|
||||
}
|
||||
|
||||
void AutoTransaction::close(bool abort)
|
||||
void AutoTransaction::close(TransactionCloseMode mode)
|
||||
{
|
||||
if (tid || abort) {
|
||||
GetApplication().closeActiveTransaction(abort, abort ? 0 : tid);
|
||||
if (tid != NullTransaction) {
|
||||
GetApplication().closeActiveTransaction(mode, tid);
|
||||
tid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void AutoTransaction::setEnable(bool enable)
|
||||
int Application::setActiveTransaction(TransactionName name)
|
||||
{
|
||||
auto& app = GetApplication();
|
||||
if (!app._activeTransactionGuard) {
|
||||
return;
|
||||
if (name.name.empty()) {
|
||||
name.name = "Command";
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
if (_pActiveDoc != nullptr) {
|
||||
return _pActiveDoc->setActiveTransaction(name);
|
||||
}
|
||||
return openGlobalTransaction(name);
|
||||
}
|
||||
|
||||
int Application::setActiveTransaction(const char* name, bool persist)
|
||||
std::string Application::getActiveTransaction(int* id) const
|
||||
{
|
||||
if (id != nullptr) {
|
||||
*id = _globalTransactionID;
|
||||
}
|
||||
return _globalTransactionID != 0 ? getTransactionName(_globalTransactionID) : "";
|
||||
}
|
||||
int Application::openGlobalTransaction(TransactionName name)
|
||||
{
|
||||
if (name.name.empty()) {
|
||||
name.name = "Command";
|
||||
}
|
||||
|
||||
if (!name || !name[0]) {
|
||||
name = "Command";
|
||||
}
|
||||
|
||||
this->signalBeforeOpenTransaction(name);
|
||||
if (_activeTransactionGuard > 0 && getActiveTransaction()) {
|
||||
if (_activeTransactionTmpName) {
|
||||
FC_LOG("transaction rename to '" << name << "'");
|
||||
for (auto& v : DocMap) {
|
||||
v.second->renameTransaction(name, _activeTransactionID);
|
||||
FC_WARN("Setting a global transaction with name='" << name.name);
|
||||
if (_globalTransactionID != 0 && transactionTmpName(_globalTransactionID)) {
|
||||
setTransactionName(_globalTransactionID, name);
|
||||
} else {
|
||||
FC_LOG("set global transaction '" << name.name << "'");
|
||||
|
||||
if (_globalTransactionID != 0 && !commitTransaction(_globalTransactionID)) {
|
||||
FC_WARN("could not close current global transaction");
|
||||
return _globalTransactionID;
|
||||
}
|
||||
|
||||
_globalTransactionID = Transaction::getNewID();
|
||||
setTransactionDescription(
|
||||
_globalTransactionID,
|
||||
TransactionDescription {
|
||||
.initiator = nullptr,
|
||||
.name = name
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (persist) {
|
||||
AutoTransaction::setEnable(false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
else if (_TransactionLock) {
|
||||
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
||||
FC_WARN("Transaction locked, ignore new transaction '" << name << "'");
|
||||
}
|
||||
return 0;
|
||||
|
||||
return _globalTransactionID;
|
||||
}
|
||||
int Application::getGlobalTransaction() const
|
||||
{
|
||||
return _globalTransactionID;
|
||||
}
|
||||
bool Application::transactionIsActive(int tid) const
|
||||
{
|
||||
return transactionDescription(tid) != std::nullopt;
|
||||
}
|
||||
std::string Application::getTransactionName(int tid) const
|
||||
{
|
||||
auto desc = transactionDescription(tid);
|
||||
return desc ? desc->name.name : "";
|
||||
}
|
||||
bool Application::transactionTmpName(int tid) const
|
||||
{
|
||||
auto desc = transactionDescription(tid);
|
||||
return desc ? desc->name.temporary : false;
|
||||
}
|
||||
Document* Application::transactionInitiator(int tid) const
|
||||
{
|
||||
auto desc = transactionDescription(tid);
|
||||
return desc ? desc->initiator : nullptr;
|
||||
}
|
||||
std::optional<TransactionDescription> Application::transactionDescription(int tid) const
|
||||
{
|
||||
if (tid == NullTransaction) {
|
||||
return std::nullopt;
|
||||
}
|
||||
else {
|
||||
FC_LOG("set active transaction '" << name << "'");
|
||||
_activeTransactionID = 0;
|
||||
auto found = _activeTransactionDescriptions.find(tid);
|
||||
if (found != _activeTransactionDescriptions.end()) {
|
||||
return std::optional<TransactionDescription>(found->second);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
void Application::setTransactionDescription(int tid, const TransactionDescription& desc)
|
||||
{
|
||||
if (tid == NullTransaction) {
|
||||
return;
|
||||
}
|
||||
auto found = _activeTransactionDescriptions.find(tid);
|
||||
bool wasPresent = (found != _activeTransactionDescriptions.end());
|
||||
|
||||
if (wasPresent && found->second.name.temporary) {
|
||||
for (auto& v : DocMap) {
|
||||
v.second->_commitTransaction();
|
||||
v.second->renameTransaction(desc.name.name, tid);
|
||||
}
|
||||
_activeTransactionID = Transaction::getNewID();
|
||||
}
|
||||
_activeTransactionTmpName = false;
|
||||
_activeTransactionName = name;
|
||||
if (persist) {
|
||||
AutoTransaction::setEnable(false);
|
||||
|
||||
if (!wasPresent || found->second.name.temporary) {
|
||||
_activeTransactionDescriptions[tid] = desc;
|
||||
FC_LOG("transaction rename to '" << desc.name.name << "'");
|
||||
}
|
||||
return _activeTransactionID;
|
||||
}
|
||||
void Application::setTransactionName(int tid, const TransactionName& name)
|
||||
{
|
||||
if (tid == NullTransaction || !transactionIsActive(tid)) {
|
||||
return;
|
||||
}
|
||||
auto found = _activeTransactionDescriptions.find(tid);
|
||||
if (found == _activeTransactionDescriptions.end() || found->second.name.temporary) {
|
||||
_activeTransactionDescriptions[tid].name = name;
|
||||
FC_LOG("transaction rename to '" << name.name << "'");
|
||||
for (auto& v : DocMap) {
|
||||
v.second->renameTransaction(name.name, tid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* Application::getActiveTransaction(int* id) const
|
||||
bool Application::closeActiveTransaction(TransactionCloseMode mode, int id)
|
||||
{
|
||||
int tid = 0;
|
||||
if (Transaction::getLastID() == _activeTransactionID) {
|
||||
tid = _activeTransactionID;
|
||||
}
|
||||
if (id) {
|
||||
*id = tid;
|
||||
}
|
||||
return tid ? _activeTransactionName.c_str() : nullptr;
|
||||
}
|
||||
|
||||
void Application::closeActiveTransaction(bool abort, int id)
|
||||
{
|
||||
if (!id) {
|
||||
id = _activeTransactionID;
|
||||
}
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_activeTransactionGuard > 0 && !abort) {
|
||||
FC_LOG("ignore close transaction");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_TransactionLock) {
|
||||
if (_TransactionClosed >= 0) {
|
||||
_TransactionLock = abort ? -1 : 1;
|
||||
bool abort = (mode == TransactionCloseMode::Abort);
|
||||
|
||||
if (id == NullTransaction) {
|
||||
if (_pActiveDoc != nullptr && _pActiveDoc->getBookedTransactionID() != NullTransaction) {
|
||||
id = _pActiveDoc->getBookedTransactionID();
|
||||
} else {
|
||||
id = _globalTransactionID;
|
||||
}
|
||||
FC_LOG("pending " << (abort ? "abort" : "close") << " transaction");
|
||||
return;
|
||||
}
|
||||
if (id == NullTransaction || id == currentlyClosingID) {
|
||||
return false;
|
||||
}
|
||||
currentlyClosingID = id;
|
||||
|
||||
FC_LOG("close transaction '" << _activeTransactionName << "' " << abort);
|
||||
_activeTransactionID = 0;
|
||||
|
||||
TransactionSignaller signaller(abort, false);
|
||||
for (auto& v : DocMap) {
|
||||
if (v.second->getTransactionID(true) != id) {
|
||||
std::vector<Document*> docsToPoke;
|
||||
for (auto& docNameAndDoc : DocMap) {
|
||||
if (docNameAndDoc.second->getBookedTransactionID() != id) {
|
||||
continue;
|
||||
}
|
||||
if(docNameAndDoc.second->isTransactionLocked() || docNameAndDoc.second->transacting()) {
|
||||
FC_LOG("pending " << (abort ? "abort" : "close") << " transaction");
|
||||
currentlyClosingID = 0;
|
||||
return false;
|
||||
}
|
||||
if(docNameAndDoc.second->transacting()) {
|
||||
FC_LOG("pending " << (abort ? "abort" : "close") << " transaction");
|
||||
currentlyClosingID = 0;
|
||||
return false;
|
||||
}
|
||||
docsToPoke.push_back(docNameAndDoc.second);
|
||||
}
|
||||
|
||||
FC_LOG("close transaction '" << _activeTransactionDescriptions[id].name.name << "' " << abort);
|
||||
_activeTransactionDescriptions.erase(id);
|
||||
if (id == _globalTransactionID) {
|
||||
_globalTransactionID = 0;
|
||||
}
|
||||
|
||||
TransactionSignaller signaller(abort, false);
|
||||
|
||||
for (auto& doc : docsToPoke) {
|
||||
if (abort) {
|
||||
v.second->_abortTransaction();
|
||||
doc->_abortTransaction();
|
||||
}
|
||||
else {
|
||||
v.second->_commitTransaction();
|
||||
doc->_commitTransaction();
|
||||
}
|
||||
}
|
||||
currentlyClosingID = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool Application::commitTransaction(int tid)
|
||||
{
|
||||
return closeActiveTransaction(TransactionCloseMode::Commit, tid);
|
||||
}
|
||||
bool Application::abortTransaction(int tid)
|
||||
{
|
||||
return closeActiveTransaction(TransactionCloseMode::Abort, tid);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TransactionLocker::TransactionLocker(bool lock)
|
||||
TransactionLocker::TransactionLocker(Document* doc, bool lock)
|
||||
: active(lock)
|
||||
, doc(doc)
|
||||
{
|
||||
if (lock) {
|
||||
++_TransactionLock;
|
||||
doc->lockTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,22 +280,9 @@ void TransactionLocker::activate(bool enable)
|
||||
|
||||
active = enable;
|
||||
if (active) {
|
||||
++_TransactionLock;
|
||||
doc->lockTransaction();
|
||||
return;
|
||||
}
|
||||
|
||||
if (--_TransactionLock != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_TransactionClosed) {
|
||||
bool abort = (_TransactionClosed < 0);
|
||||
_TransactionClosed = 0;
|
||||
GetApplication().closeActiveTransaction(abort);
|
||||
}
|
||||
}
|
||||
|
||||
bool TransactionLocker::isLocked()
|
||||
{
|
||||
return _TransactionLock > 0;
|
||||
doc->unlockTransaction();
|
||||
}
|
||||
|
||||
+22
-48
@@ -26,11 +26,15 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <FCGlobal.h>
|
||||
#include <string>
|
||||
|
||||
#include "TransactionDefs.h"
|
||||
|
||||
namespace App
|
||||
{
|
||||
|
||||
class Application;
|
||||
class Document;
|
||||
|
||||
/**
|
||||
* @brief A helper class to manage transactions (i.e. undo/redo).
|
||||
@@ -45,59 +49,31 @@ public:
|
||||
void* operator new(std::size_t) = delete;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct an auto transaction.
|
||||
*
|
||||
* @param[in] name: optional new transaction name on construction
|
||||
* @param[in] 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 transactions being setup. It also prevents to close
|
||||
* (i.e. commits) the current active transaction until it reaches zero. It
|
||||
* does not have any effect on aborting transactions though.
|
||||
/** Constructor
|
||||
*
|
||||
* @param tid the ID of the transaction to manage
|
||||
*
|
||||
* No action is done in the constructor
|
||||
*/
|
||||
AutoTransaction(const char* name = nullptr, bool tmpName = false);
|
||||
|
||||
/**
|
||||
* @brief Destruct an auto transaction.
|
||||
*
|
||||
* This destructor decrease an internal counter
|
||||
* (Application::_activeTransactionGuard), and will commit any current
|
||||
* active transaction when the counter reaches zero.
|
||||
explicit AutoTransaction(int tid);
|
||||
AutoTransaction(Document* doc, const std::string& name);
|
||||
|
||||
/** Destructor
|
||||
*
|
||||
* This destructor attempts to commit the transaction it manages
|
||||
*/
|
||||
~AutoTransaction();
|
||||
|
||||
/**
|
||||
* @brief 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.
|
||||
*
|
||||
* @param[in] abort: if true, abort the transaction; otherwise, commit it.
|
||||
* This function can be used to explicitly close (i.e. commit / abort) the
|
||||
* transaction,
|
||||
*/
|
||||
void close(bool abort = false);
|
||||
|
||||
/**
|
||||
* @brief Enable/Disable any AutoTransaction instance on the current stack.
|
||||
*
|
||||
* Once disabled, any empty temporary named transaction is closed. If there
|
||||
* are non-empty or non-temporary 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.
|
||||
*
|
||||
* @param[in] enable: if true, enable the AutoTransaction; otherwise, disable it.
|
||||
*/
|
||||
static void setEnable(bool enable);
|
||||
void close(TransactionCloseMode mode = TransactionCloseMode::Commit);
|
||||
|
||||
private:
|
||||
int tid = 0;
|
||||
int tid { 0 };
|
||||
};
|
||||
|
||||
|
||||
@@ -116,7 +92,7 @@ public:
|
||||
*
|
||||
* @param[in] lock: whether to activate the lock
|
||||
*/
|
||||
TransactionLocker(bool lock = true);
|
||||
TransactionLocker(Document* doc, bool lock = true);
|
||||
|
||||
/**
|
||||
* @brief Destruct a transaction locker.
|
||||
@@ -142,10 +118,7 @@ public:
|
||||
{
|
||||
return active;
|
||||
}
|
||||
|
||||
/// Check if transaction is being locked.
|
||||
static bool isLocked();
|
||||
|
||||
|
||||
friend class Application;
|
||||
|
||||
public:
|
||||
@@ -154,6 +127,7 @@ public:
|
||||
|
||||
private:
|
||||
bool active;
|
||||
Document* doc;
|
||||
};
|
||||
|
||||
} // namespace App
|
||||
+199
-101
@@ -35,6 +35,7 @@
|
||||
#include <list>
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/bimap.hpp>
|
||||
@@ -190,6 +191,7 @@ bool Document::undo(const int id)
|
||||
mRedoMap[d->activeUndoTransaction->getID()] = d->activeUndoTransaction;
|
||||
mRedoTransactions.push_back(d->activeUndoTransaction);
|
||||
d->activeUndoTransaction = nullptr;
|
||||
d->bookedTransaction = 0;
|
||||
|
||||
mUndoMap.erase(mUndoTransactions.back()->getID());
|
||||
delete mUndoTransactions.back();
|
||||
@@ -242,6 +244,7 @@ bool Document::redo(const int id)
|
||||
mUndoMap[d->activeUndoTransaction->getID()] = d->activeUndoTransaction;
|
||||
mUndoTransactions.push_back(d->activeUndoTransaction);
|
||||
d->activeUndoTransaction = nullptr;
|
||||
d->bookedTransaction = 0;
|
||||
|
||||
mRedoMap.erase(mRedoTransactions.back()->getID());
|
||||
delete mRedoTransactions.back();
|
||||
@@ -271,10 +274,10 @@ void Document::changePropertyOfObject(TransactionalObject* obj,
|
||||
}
|
||||
if ((d->iUndoMode != 0) && !isPerformingTransaction() && !d->activeUndoTransaction) {
|
||||
if (!testStatus(Restoring) || testStatus(Importing)) {
|
||||
int tid = 0;
|
||||
const char* name = GetApplication().getActiveTransaction(&tid);
|
||||
if (name && tid > 0) {
|
||||
_openTransaction(name, tid);
|
||||
if (d->bookedTransaction == NullTransaction) {
|
||||
d->bookedTransaction = GetApplication().getGlobalTransaction();
|
||||
} else {
|
||||
_openTransaction(GetApplication().getTransactionName(d->bookedTransaction), d->bookedTransaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -329,71 +332,92 @@ std::vector<std::string> Document::getAvailableRedoNames() const
|
||||
return vList;
|
||||
}
|
||||
|
||||
void Document::openTransaction(const char* name) // NOLINT
|
||||
int Document::openTransaction(TransactionName name, int tid) // NOLINT
|
||||
{
|
||||
if (isPerformingTransaction() || d->committing) {
|
||||
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
||||
FC_WARN("Cannot open transaction while transacting");
|
||||
}
|
||||
return;
|
||||
if (tid != NullTransaction && tid == d->bookedTransaction) {
|
||||
return tid; // Early exit without warning
|
||||
}
|
||||
if (isTransactionLocked()) {
|
||||
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
||||
FC_WARN("Transaction locked, ignore new transaction '" << name.name << "'");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
GetApplication().setActiveTransaction(name ? name : "<empty>");
|
||||
}
|
||||
|
||||
int Document::_openTransaction(const char* name, int id)
|
||||
{
|
||||
if (isPerformingTransaction() || d->committing) {
|
||||
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
||||
FC_WARN("Cannot open transaction while transacting");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (d->iUndoMode != 0) {
|
||||
// Avoid recursive calls that is possible while
|
||||
// clearing the redo transactions and will cause
|
||||
// a double deletion of some transaction and thus
|
||||
// a segmentation fault
|
||||
if (d->opentransaction) {
|
||||
return 0;
|
||||
}
|
||||
Base::FlagToggler<> flag(d->opentransaction);
|
||||
|
||||
if ((id != 0) && mUndoMap.find(id) != mUndoMap.end()) {
|
||||
throw Base::RuntimeError("invalid transaction id");
|
||||
}
|
||||
if (d->activeUndoTransaction) {
|
||||
_commitTransaction(true);
|
||||
}
|
||||
_clearRedos();
|
||||
|
||||
d->activeUndoTransaction = new Transaction(id);
|
||||
if (!name) {
|
||||
name = "<empty>";
|
||||
}
|
||||
d->activeUndoTransaction->Name = name;
|
||||
mUndoMap[d->activeUndoTransaction->getID()] = d->activeUndoTransaction;
|
||||
id = d->activeUndoTransaction->getID();
|
||||
|
||||
signalOpenTransaction(*this, name);
|
||||
|
||||
auto& app = GetApplication();
|
||||
auto activeDoc = app.getActiveDocument();
|
||||
if (activeDoc && activeDoc != this && !activeDoc->hasPendingTransaction()) {
|
||||
std::string aname("-> ");
|
||||
aname += d->activeUndoTransaction->Name;
|
||||
FC_LOG("auto transaction " << getName() << " -> " << activeDoc->getName());
|
||||
activeDoc->_openTransaction(aname.c_str(), id);
|
||||
}
|
||||
return id;
|
||||
if (name.name.empty()) {
|
||||
name.name = "<empty>";
|
||||
}
|
||||
return 0;
|
||||
return setActiveTransaction(name, tid);
|
||||
}
|
||||
int Document::openTransaction(std::string name, int tid)
|
||||
{
|
||||
return openTransaction(TransactionName {.name = name, .temporary = false}, tid);
|
||||
}
|
||||
|
||||
void Document::renameTransaction(const char* name, const int id) const
|
||||
int Document::_openTransaction(std::string name, int id)
|
||||
{
|
||||
if (name && d->activeUndoTransaction && d->activeUndoTransaction->getID() == id) {
|
||||
if (isTransactionLocked() && id != d->bookedTransaction) {
|
||||
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
||||
FC_WARN("Transaction locked, ignore new transaction '" << name << "'");
|
||||
}
|
||||
}
|
||||
if (isPerformingTransaction() || d->committing) {
|
||||
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
||||
FC_WARN("Cannot open transaction while transacting");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (d->iUndoMode == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Avoid recursive calls that is possible while
|
||||
// clearing the redo transactions and will cause
|
||||
// a double deletion of some transaction and thus
|
||||
// a segmentation fault
|
||||
if (d->opentransaction) {
|
||||
return 0;
|
||||
}
|
||||
Base::FlagToggler<> flag(d->opentransaction);
|
||||
|
||||
if ((id != 0) && mUndoMap.find(id) != mUndoMap.end()) {
|
||||
throw Base::RuntimeError("invalid transaction id");
|
||||
}
|
||||
if (d->activeUndoTransaction) {
|
||||
_commitTransaction(true);
|
||||
}
|
||||
_clearRedos();
|
||||
|
||||
// When id == 0, this creates a new id
|
||||
// for instance, when there is no global transaction
|
||||
// from the application to stick to
|
||||
d->activeUndoTransaction = new Transaction(id);
|
||||
if (name.empty()) {
|
||||
name = "<empty>";
|
||||
}
|
||||
d->activeUndoTransaction->Name = name;
|
||||
mUndoMap[d->activeUndoTransaction->getID()] = d->activeUndoTransaction;
|
||||
id = d->activeUndoTransaction->getID();
|
||||
|
||||
signalOpenTransaction(*this, name);
|
||||
|
||||
Document* transactionInitiator = GetApplication().transactionInitiator(id);
|
||||
if (transactionInitiator && transactionInitiator != this && !transactionInitiator->hasPendingTransaction()) {
|
||||
std::string aname = std::format("-> {}", d->activeUndoTransaction->Name);
|
||||
FC_LOG("auto transaction " << getName() << " -> " << transactionInitiator->getName());
|
||||
transactionInitiator->_openTransaction(aname, id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void Document::renameTransaction(const std::string& name, const int id) const
|
||||
{
|
||||
if (!name.empty() && d->activeUndoTransaction && d->activeUndoTransaction->getID() == id) {
|
||||
if (boost::starts_with(d->activeUndoTransaction->Name, "-> ")) {
|
||||
d->activeUndoTransaction->Name.resize(3);
|
||||
}
|
||||
@@ -403,48 +427,108 @@ void Document::renameTransaction(const char* name, const int id) const
|
||||
d->activeUndoTransaction->Name += name;
|
||||
}
|
||||
}
|
||||
int Document::setActiveTransaction(TransactionName name, int tid)
|
||||
{
|
||||
// Probably a group transaction situation
|
||||
if (tid != NullTransaction) {
|
||||
if (!GetApplication().transactionIsActive(tid)) {
|
||||
FC_LOG("Could not set active transaction to inactive ID");
|
||||
return NullTransaction;
|
||||
}
|
||||
if (d->bookedTransaction != NullTransaction && d->bookedTransaction != tid && !_commitTransaction(true)) {
|
||||
FC_LOG("Could not book transaction for document");
|
||||
return NullTransaction;
|
||||
}
|
||||
d->bookedTransaction = tid;
|
||||
|
||||
if (GetApplication().transactionTmpName(d->bookedTransaction)) {
|
||||
GetApplication().setTransactionName(d->bookedTransaction, name);
|
||||
}
|
||||
return d->bookedTransaction;
|
||||
}
|
||||
|
||||
// Rename the transaction if it had a tmp name
|
||||
if (d->bookedTransaction != NullTransaction && GetApplication().transactionTmpName(d->bookedTransaction)) {
|
||||
GetApplication().setTransactionName(d->bookedTransaction, name);
|
||||
return d->bookedTransaction;
|
||||
}
|
||||
if (d->bookedTransaction != NullTransaction && !_commitTransaction(true)) {
|
||||
FC_LOG("Could not book transaction for document");
|
||||
return NullTransaction;
|
||||
}
|
||||
d->bookedTransaction = Transaction::getNewID();
|
||||
|
||||
GetApplication().setTransactionDescription(d->bookedTransaction, TransactionDescription {.initiator = this, .name = name});
|
||||
return d->bookedTransaction;
|
||||
}
|
||||
|
||||
void Document::lockTransaction()
|
||||
{
|
||||
d->TransactionLock++;
|
||||
}
|
||||
void Document::unlockTransaction()
|
||||
{
|
||||
if (d->TransactionLock > 0) {
|
||||
d->TransactionLock--;
|
||||
}
|
||||
}
|
||||
bool Document::isTransactionLocked() const
|
||||
{
|
||||
return d->TransactionLock > 0;
|
||||
}
|
||||
bool Document::transacting() const
|
||||
{
|
||||
return isPerformingTransaction() || d->committing;
|
||||
}
|
||||
|
||||
void Document::_checkTransaction(DocumentObject* pcDelObj, const Property* What, int line)
|
||||
{
|
||||
// if the undo is active but no transaction open, open one!
|
||||
if ((d->iUndoMode != 0) && !isPerformingTransaction()) {
|
||||
if (!d->activeUndoTransaction) {
|
||||
if (!testStatus(Restoring) || testStatus(Importing)) {
|
||||
int tid = 0;
|
||||
const char* name = GetApplication().getActiveTransaction(&tid);
|
||||
if (name && tid > 0) {
|
||||
bool ignore = false;
|
||||
if (What && What->testStatus(Property::NoModify)) {
|
||||
ignore = true;
|
||||
}
|
||||
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
||||
if (What) {
|
||||
FC_LOG((ignore ? "ignore" : "auto")
|
||||
<< " transaction (" << line << ") '" << What->getFullName());
|
||||
}
|
||||
else {
|
||||
FC_LOG((ignore ? "ignore" : "auto") << " transaction (" << line << ") '"
|
||||
<< name << "' in " << getName());
|
||||
}
|
||||
}
|
||||
if (!ignore) {
|
||||
_openTransaction(name, tid);
|
||||
}
|
||||
return;
|
||||
if (d->iUndoMode == 0 || isPerformingTransaction() || d->activeUndoTransaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!testStatus(Restoring) || testStatus(Importing)) {
|
||||
|
||||
// Priority to a transaction that has been booked
|
||||
// explicitly for this document, it there are none
|
||||
// get a sticky transaction from application
|
||||
if (!d->bookedTransaction) {
|
||||
d->bookedTransaction = GetApplication().getGlobalTransaction();
|
||||
}
|
||||
|
||||
if (d->bookedTransaction != NullTransaction) {
|
||||
std::string name = GetApplication().getTransactionName(d->bookedTransaction);
|
||||
bool ignore = false;
|
||||
if (What && What->testStatus(Property::NoModify)) {
|
||||
ignore = true;
|
||||
}
|
||||
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
||||
if (What) {
|
||||
FC_LOG((ignore ? "ignore" : "auto")
|
||||
<< " transaction (" << line << ") '" << What->getFullName());
|
||||
}
|
||||
else {
|
||||
FC_LOG((ignore ? "ignore" : "auto") << " transaction (" << line << ") '"
|
||||
<< name << "' in " << getName());
|
||||
}
|
||||
}
|
||||
if (!pcDelObj) {
|
||||
return;
|
||||
}
|
||||
// When the object is going to be deleted we have to check if it has already been added
|
||||
// to the undo transactions
|
||||
std::list<Transaction*>::iterator it;
|
||||
for (it = mUndoTransactions.begin(); it != mUndoTransactions.end(); ++it) {
|
||||
if ((*it)->hasObject(pcDelObj)) {
|
||||
_openTransaction("Delete");
|
||||
break;
|
||||
}
|
||||
if (!ignore) {
|
||||
_openTransaction(name, d->bookedTransaction);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!pcDelObj) {
|
||||
return;
|
||||
}
|
||||
// When the object is going to be deleted we have to check if it has already been added
|
||||
// to the undo transactions
|
||||
std::list<Transaction*>::iterator it;
|
||||
for (it = mUndoTransactions.begin(); it != mUndoTransactions.end(); ++it) {
|
||||
if ((*it)->hasObject(pcDelObj)) {
|
||||
_openTransaction("Delete");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -473,29 +557,36 @@ void Document::commitTransaction() // NOLINT
|
||||
}
|
||||
|
||||
if (d->activeUndoTransaction) {
|
||||
GetApplication().closeActiveTransaction(false, d->activeUndoTransaction->getID());
|
||||
// This will iterate over all documents and ask them to
|
||||
// commit their transaction if their ID matches
|
||||
GetApplication().commitTransaction(d->activeUndoTransaction->getID());
|
||||
} else {
|
||||
d->bookedTransaction = 0; // Reset booked transaction even if it was not used
|
||||
}
|
||||
}
|
||||
|
||||
void Document::_commitTransaction(const bool notify)
|
||||
bool Document::_commitTransaction(const bool notify)
|
||||
{
|
||||
if (isPerformingTransaction()) {
|
||||
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
||||
FC_WARN("Cannot commit transaction while transacting");
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (d->committing) {
|
||||
// for a recursive call return without printing a warning
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
d->bookedTransaction = 0;
|
||||
if (d->activeUndoTransaction) {
|
||||
Base::FlagToggler<> flag(d->committing);
|
||||
Application::TransactionSignaller signaller(false, true);
|
||||
const int id = d->activeUndoTransaction->getID();
|
||||
|
||||
mUndoTransactions.push_back(d->activeUndoTransaction);
|
||||
d->activeUndoTransaction = nullptr;
|
||||
|
||||
// check the stack for the limits
|
||||
if (mUndoTransactions.size() > d->UndoMaxStackSize) {
|
||||
mUndoMap.erase(mUndoTransactions.front()->getID());
|
||||
@@ -504,11 +595,12 @@ void Document::_commitTransaction(const bool notify)
|
||||
}
|
||||
signalCommitTransaction(*this);
|
||||
|
||||
// closeActiveTransaction() may call again _commitTransaction()
|
||||
// commitTransaction() may call again _commitTransaction()
|
||||
if (notify) {
|
||||
GetApplication().closeActiveTransaction(false, id);
|
||||
GetApplication().commitTransaction(id);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Document::abortTransaction() const
|
||||
@@ -520,7 +612,9 @@ void Document::abortTransaction() const
|
||||
return;
|
||||
}
|
||||
if (d->activeUndoTransaction) {
|
||||
GetApplication().closeActiveTransaction(true, d->activeUndoTransaction->getID());
|
||||
GetApplication().abortTransaction(d->activeUndoTransaction->getID());
|
||||
} else {
|
||||
d->bookedTransaction = 0; // Reset booked transaction even if it was not used
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,6 +626,7 @@ void Document::_abortTransaction()
|
||||
}
|
||||
}
|
||||
|
||||
d->bookedTransaction = 0;
|
||||
if (d->activeUndoTransaction) {
|
||||
Base::FlagToggler<bool> flag(d->rollback);
|
||||
Application::TransactionSignaller signaller(true, true);
|
||||
@@ -575,7 +670,10 @@ int Document::getTransactionID(const bool undo, unsigned pos) const
|
||||
for (; pos != 0U; ++rit, --pos) {}
|
||||
return (*rit)->getID();
|
||||
}
|
||||
|
||||
int Document::getBookedTransactionID() const
|
||||
{
|
||||
return d->bookedTransaction;
|
||||
}
|
||||
bool Document::isTransactionEmpty() const
|
||||
{
|
||||
return !d->activeUndoTransaction;
|
||||
@@ -3312,7 +3410,7 @@ void Document::_removeObject(DocumentObject* pcObject, RemoveObjectOptions optio
|
||||
return;
|
||||
}
|
||||
|
||||
TransactionLocker tlock;
|
||||
TransactionLocker tlock(this);
|
||||
|
||||
_checkTransaction(pcObject, nullptr, __LINE__);
|
||||
|
||||
|
||||
+18
-7
@@ -35,6 +35,7 @@
|
||||
#include "PropertyLinks.h"
|
||||
#include "PropertyStandard.h"
|
||||
#include "ExportInfo.h"
|
||||
#include "TransactionDefs.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
@@ -865,7 +866,19 @@ public:
|
||||
* setup a potential transaction that will only be created if there are
|
||||
* actual changes.
|
||||
*/
|
||||
void openTransaction(const char* name = nullptr);
|
||||
int openTransaction(TransactionName name, int tid = 0);
|
||||
int openTransaction(std::string name, int tid = 0);
|
||||
|
||||
// If the tid != 0, it will take the transaction id if it exists
|
||||
int setActiveTransaction(TransactionName name, int tid = 0);
|
||||
|
||||
void lockTransaction();
|
||||
void unlockTransaction();
|
||||
bool isTransactionLocked() const;
|
||||
|
||||
bool transacting() const;
|
||||
|
||||
int getBookedTransactionID() const;
|
||||
|
||||
/**
|
||||
* @brief Rename the current transaction.
|
||||
@@ -875,7 +888,7 @@ public:
|
||||
* @param[in] name The new name of the transaction.
|
||||
* @param[in] id The transaction ID to match.
|
||||
*/
|
||||
void renameTransaction(const char* name, int id) const;
|
||||
void renameTransaction(const std::string& name, int id) const;
|
||||
|
||||
/**
|
||||
* @brief Commit the Command transaction.
|
||||
@@ -1244,7 +1257,7 @@ public:
|
||||
|
||||
/// Check if there is any document restoring/importing.
|
||||
static bool isAnyRestoring();
|
||||
|
||||
|
||||
/// Register a new label.
|
||||
void registerLabel(const std ::string& newLabel);
|
||||
/// Unregister a label.
|
||||
@@ -1409,8 +1422,7 @@ protected:
|
||||
* This function creates an actual transaction regardless of Application
|
||||
* AutoTransaction setting.
|
||||
*/
|
||||
int _openTransaction(const char* name = nullptr, int id = 0);
|
||||
|
||||
int _openTransaction(std::string name = "", int id = 0);
|
||||
/**
|
||||
* @brief Commit the Command transaction.
|
||||
*
|
||||
@@ -1419,8 +1431,7 @@ protected:
|
||||
*
|
||||
* @param notify If true, notify the application to close the transaction.
|
||||
*/
|
||||
void _commitTransaction(bool notify = false);
|
||||
|
||||
bool _commitTransaction(bool notify = false);
|
||||
/**
|
||||
* @brief Abort the running transaction.
|
||||
*
|
||||
|
||||
@@ -447,3 +447,12 @@ class Document(PropertyContainer):
|
||||
sort: whether to topologically sort the return list
|
||||
"""
|
||||
...
|
||||
|
||||
def getBookedTransactionID(self) -> int:
|
||||
"""
|
||||
getBookedTransactionID() -> int
|
||||
|
||||
Returns the currently booked transaction id, which is the id of the current transaction OR the id
|
||||
the next transaction will stick to if no change has occured yet
|
||||
"""
|
||||
...
|
||||
@@ -1175,6 +1175,15 @@ PyObject* DocumentPy::getDependentDocuments(PyObject* args)
|
||||
}
|
||||
PY_CATCH;
|
||||
}
|
||||
PyObject* DocumentPy::getBookedTransactionID(PyObject* args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
return nullptr;
|
||||
}
|
||||
int tid = getDocumentPtr()->getBookedTransactionID();
|
||||
return Py::new_reference_to(Py::Long(tid));
|
||||
}
|
||||
|
||||
|
||||
Py::Boolean DocumentPy::getRestoring() const
|
||||
{
|
||||
|
||||
@@ -1546,11 +1546,11 @@ void PropertyString::setValue(const char* newValue)
|
||||
// OnProposedLabelChange has changed the new value to what the current value is
|
||||
return;
|
||||
}
|
||||
if (!propChanges.empty() && !GetApplication().getActiveTransaction()) {
|
||||
if (!propChanges.empty() && obj->getDocument()->getBookedTransactionID() == 0) {
|
||||
commit = true;
|
||||
std::ostringstream str;
|
||||
str << "Change " << obj->getNameInDocument() << ".Label";
|
||||
GetApplication().setActiveTransaction(str.str().c_str());
|
||||
obj->getDocument()->openTransaction(str.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1563,7 +1563,7 @@ void PropertyString::setValue(const char* newValue)
|
||||
}
|
||||
|
||||
if (commit) {
|
||||
GetApplication().closeActiveTransaction();
|
||||
obj->getDocument()->commitTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ Man bør derefter klikke på Opdater knappen i hovedværktøjslinjen.
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="271"/>
|
||||
<source>Origin</source>
|
||||
<translation>Origo</translation>
|
||||
<translation>Nulpunkt</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<context>
|
||||
<name>LinkParams</name>
|
||||
<message>
|
||||
<location filename="../../Link.cpp" line="135"/>
|
||||
<location filename="../../Link.cpp" line="131"/>
|
||||
<source>Stores the last user choice of whether to apply CopyOnChange setup to all links
|
||||
that reference the same configurable object</source>
|
||||
<translation>Menyimpan pilihan terakhir pengguna sama ada untuk menerapkan tetapan CopyOnChange kepada semua pautan yang merujuk kepada objek boleh dikonfigurasikan yang sama</translation>
|
||||
@@ -13,7 +13,7 @@ that reference the same configurable object</source>
|
||||
<context>
|
||||
<name>QObject</name>
|
||||
<message>
|
||||
<location filename="../../Application.cpp" line="457"/>
|
||||
<location filename="../../Application.cpp" line="458"/>
|
||||
<source>Unnamed</source>
|
||||
<translation>Tidak bernama</translation>
|
||||
</message>
|
||||
@@ -21,7 +21,7 @@ that reference the same configurable object</source>
|
||||
<context>
|
||||
<name>App::OriginGroupExtension</name>
|
||||
<message>
|
||||
<location filename="../../OriginGroupExtension.cpp" line="162"/>
|
||||
<location filename="../../OriginGroupExtension.cpp" line="161"/>
|
||||
<source>Origin</source>
|
||||
<translation>Asal</translation>
|
||||
</message>
|
||||
@@ -29,7 +29,7 @@ that reference the same configurable object</source>
|
||||
<context>
|
||||
<name>Notifications</name>
|
||||
<message>
|
||||
<location filename="../../Document.cpp" line="1095"/>
|
||||
<location filename="../../Document.cpp" line="1117"/>
|
||||
<source>
|
||||
It is recommended that the user right-click the root of the document and select Mark to recompute.
|
||||
The user should then click the Refresh button in the main toolbar.
|
||||
@@ -41,37 +41,37 @@ Pengguna kemudiannya perlu mengklik butang Muat semula dalam bar alat utama.</tr
|
||||
<context>
|
||||
<name>App::LocalCoordinateSystem</name>
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="250"/>
|
||||
<location filename="../../Datums.cpp" line="265"/>
|
||||
<source>X-axis</source>
|
||||
<translation>Paksi-X</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="251"/>
|
||||
<location filename="../../Datums.cpp" line="266"/>
|
||||
<source>Y-axis</source>
|
||||
<translation>Paksi-Y</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="252"/>
|
||||
<location filename="../../Datums.cpp" line="267"/>
|
||||
<source>Z-axis</source>
|
||||
<translation>Paksi-Z</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="253"/>
|
||||
<location filename="../../Datums.cpp" line="268"/>
|
||||
<source>XY-plane</source>
|
||||
<translation>Pesawat-XY</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="254"/>
|
||||
<location filename="../../Datums.cpp" line="269"/>
|
||||
<source>XZ-plane</source>
|
||||
<translation>Pesawat-XZ</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="255"/>
|
||||
<location filename="../../Datums.cpp" line="270"/>
|
||||
<source>YZ-plane</source>
|
||||
<translation>Pesawat-YZ</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="256"/>
|
||||
<location filename="../../Datums.cpp" line="271"/>
|
||||
<source>Origin</source>
|
||||
<translation>Asal</translation>
|
||||
</message>
|
||||
|
||||
@@ -58,10 +58,10 @@ static void _replaceDirs()
|
||||
};
|
||||
|
||||
for (auto const d : dirs) {
|
||||
auto const path = temp_base + PATHSEP + d + PATHSEP;
|
||||
auto const qpath = QString::fromStdString(path);
|
||||
auto const path = temp_base + "/" + d + "/";
|
||||
auto const qpath = QDir::cleanPath(QString::fromStdString(path));
|
||||
QDir().mkpath(qpath);
|
||||
config[d] = path;
|
||||
config[d] = QDir::toNativeSeparators(qpath).toStdString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2026 Théo Veilleux-Trinh <theo.veilleux.trinh@proton.me>*
|
||||
* *
|
||||
* 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_TRANSACTIONDEFS_H_
|
||||
#define APP_TRANSACTIONDEFS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace App {
|
||||
|
||||
class Document;
|
||||
|
||||
constexpr int NullTransaction = 0;
|
||||
|
||||
struct TransactionName {
|
||||
// Name of the transaction as it will appear in the GUI
|
||||
std::string name;
|
||||
|
||||
// If true, the transaction is allowed to be renamed
|
||||
bool temporary { false };
|
||||
};
|
||||
|
||||
struct TransactionDescription {
|
||||
// Document on which the transaction was first created
|
||||
// useful for mass transaction (e.g. delete from an assembly)
|
||||
// where other documents may use the same transaction id
|
||||
Document* initiator { nullptr };
|
||||
TransactionName name;
|
||||
};
|
||||
|
||||
enum class TransactionCloseMode {
|
||||
Commit = 0,
|
||||
Abort = 1,
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -556,4 +556,4 @@ TransactionObject* TransactionFactory::createTransaction(const Base::Type& type)
|
||||
|
||||
Base::Console().log("Cannot create transaction object from %s\n", type.getName());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <Base/Factory.h>
|
||||
#include <Base/Persistence.h>
|
||||
#include <App/PropertyContainer.h>
|
||||
#include "TransactionDefs.h"
|
||||
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
@@ -61,6 +62,7 @@ class AppExport Transaction: public Base::Persistence
|
||||
{
|
||||
TYPESYSTEM_HEADER_WITH_OVERRIDE();
|
||||
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a transaction.
|
||||
@@ -71,7 +73,7 @@ public:
|
||||
* transactions from different document, so that they can be undone/redone
|
||||
* together.
|
||||
*/
|
||||
explicit Transaction(int id = 0);
|
||||
explicit Transaction(int id = NullTransaction);
|
||||
|
||||
~Transaction() override;
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <optional>
|
||||
|
||||
#include <boost/bimap.hpp>
|
||||
#include <boost/graph/adjacency_list.hpp>
|
||||
@@ -93,6 +94,11 @@ struct DocumentP
|
||||
int iUndoMode {0};
|
||||
unsigned int UndoMemSize {0};
|
||||
unsigned int UndoMaxStackSize {20};
|
||||
unsigned int TransactionLock {0};
|
||||
// Id and name that the next transaction will take
|
||||
// as soon as there is a change to the document
|
||||
int bookedTransaction { 0 };
|
||||
|
||||
std::string programVersion;
|
||||
mutable HasherMap hashers;
|
||||
std::multimap<const App::DocumentObject*, std::unique_ptr<App::DocumentObjectExecReturn>>
|
||||
|
||||
@@ -4,54 +4,54 @@
|
||||
<context>
|
||||
<name>UnitsApi</name>
|
||||
<message>
|
||||
<location filename="../../UnitsSchemasData.h" line="85"/>
|
||||
<location filename="../../UnitsSchemasData.h" line="90"/>
|
||||
<source>Standard (mm, kg, s, °)</source>
|
||||
<translation type="unfinished">Standard (mm, kg, s, °)</translation>
|
||||
<translation>Standard (mm, kg, s, °)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../UnitsSchemasData.h" line="315"/>
|
||||
<location filename="../../UnitsSchemasData.h" line="326"/>
|
||||
<source>MKS (m, kg, s, °)</source>
|
||||
<translation type="unfinished">MKS (m, kg, s, °)</translation>
|
||||
<translation>MKS (m, kg, s, °)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../UnitsSchemasData.h" line="579"/>
|
||||
<location filename="../../UnitsSchemasData.h" line="593"/>
|
||||
<source>US customary (in, lb)</source>
|
||||
<translation type="unfinished">US customary (in, lb)</translation>
|
||||
<translation>Adat AS (in, lb)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../UnitsSchemasData.h" line="70"/>
|
||||
<location filename="../../UnitsSchemasData.h" line="642"/>
|
||||
<source>Imperial for Civil Eng (ft, lb, mph)</source>
|
||||
<translation>Imperial Civil Eng (ft, lb, mph)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../UnitsSchemasData.h" line="75"/>
|
||||
<source>Imperial decimal (in, lb)</source>
|
||||
<translation type="unfinished">Imperial decimal (in, lb)</translation>
|
||||
<translation>Imperial decimal (in, lb)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../UnitsSchemasData.h" line="540"/>
|
||||
<location filename="../../UnitsSchemasData.h" line="554"/>
|
||||
<source>Building Euro (cm, m², m³)</source>
|
||||
<translation type="unfinished">Building Euro (cm, m², m³)</translation>
|
||||
<translation>Bagunan Euro (cm, m², m³)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../UnitsSchemasData.h" line="617"/>
|
||||
<location filename="../../UnitsSchemasData.h" line="631"/>
|
||||
<source>Building US (ft-in, sqft, cft)</source>
|
||||
<translation type="unfinished">Building US (ft-in, sqft, cft)</translation>
|
||||
<translation>Membina AS (ft-in, sqft, cft)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../UnitsSchemasData.h" line="48"/>
|
||||
<location filename="../../UnitsSchemasData.h" line="53"/>
|
||||
<source>Metric small parts & CNC (mm, mm/min)</source>
|
||||
<translation type="unfinished">Metric small parts & CNC (mm, mm/min)</translation>
|
||||
<translation>Bahagian kecil metrik & CNC (mm, mm/min)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../UnitsSchemasData.h" line="628"/>
|
||||
<source>Imperial for Civil Eng (ft, ft/s)</source>
|
||||
<translation type="unfinished">Imperial for Civil Eng (ft, ft/s)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../UnitsSchemasData.h" line="567"/>
|
||||
<location filename="../../UnitsSchemasData.h" line="581"/>
|
||||
<source>FEM (mm, N, s)</source>
|
||||
<translation type="unfinished">FEM (mm, N, s)</translation>
|
||||
<translation>FEM (mm, N, s)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../UnitsSchemasData.h" line="57"/>
|
||||
<location filename="../../UnitsSchemasData.h" line="62"/>
|
||||
<source>Meter decimal (m, m², m³)</source>
|
||||
<translation type="unfinished">Meter decimal (m, m², m³)</translation>
|
||||
<translation>Perpuluhan meter (m, m², m³)</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
||||
@@ -157,7 +157,7 @@ long NavlibInterface::SetActiveCommand(std::string commandId)
|
||||
if (!std::string(command->getName()).compare(parsedData.commandName)) {
|
||||
if (parsedData.actionIndex == -1) {
|
||||
Gui::Action* pAction = command->getAction();
|
||||
if (pAction != nullptr) {
|
||||
if (pAction) {
|
||||
pAction->action()->trigger();
|
||||
}
|
||||
}
|
||||
@@ -177,7 +177,7 @@ void NavlibInterface::unpackCommands(Gui::Command& command,
|
||||
TDxCategory& category,
|
||||
std::vector<TDx::CImage>& images)
|
||||
{
|
||||
if (command.getAction() == nullptr)
|
||||
if (!command.getAction())
|
||||
return;
|
||||
|
||||
QList<QAction*> pQActions;
|
||||
@@ -185,7 +185,7 @@ void NavlibInterface::unpackCommands(Gui::Command& command,
|
||||
int32_t index = -1;
|
||||
auto actionGroup = qobject_cast<Gui::ActionGroup*>(command.getAction());
|
||||
|
||||
if (actionGroup != nullptr) {
|
||||
if (actionGroup) {
|
||||
pQActions = actionGroup->actions();
|
||||
std::string subCategoryName = actionGroup->text().toStdString();
|
||||
subCategory = TDxCategory(subCategoryName, subCategoryName);
|
||||
|
||||
@@ -58,7 +58,7 @@ NavlibInterface::~NavlibInterface()
|
||||
{
|
||||
disableNavigation();
|
||||
|
||||
if (pivot.pVisibility != nullptr)
|
||||
if (pivot.pVisibility)
|
||||
pivot.pVisibility->unref();
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ long NavlibInterface::GetPointerPosition(navlib::point_t& position) const
|
||||
|
||||
if (is3DView()) {
|
||||
const Gui::View3DInventorViewer* const inventorViewer = currentView.pView3d->getViewer();
|
||||
if (inventorViewer == nullptr)
|
||||
if (!inventorViewer)
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
|
||||
QPoint viewPoint = currentView.pView3d->mapFromGlobal(QCursor::pos());
|
||||
@@ -122,7 +122,7 @@ CameraType NavlibInterface::getCamera() const
|
||||
{
|
||||
if (is3DView()) {
|
||||
const Gui::View3DInventorViewer* inventorViewer = currentView.pView3d->getViewer();
|
||||
if (inventorViewer != nullptr)
|
||||
if (inventorViewer)
|
||||
return dynamic_cast<CameraType>(inventorViewer->getCamera());
|
||||
}
|
||||
return nullptr;
|
||||
@@ -132,18 +132,18 @@ template SoCamera* NavlibInterface::getCamera<SoCamera*>() const;
|
||||
|
||||
void NavlibInterface::onViewChanged(const Gui::MDIView* view)
|
||||
{
|
||||
if (view == nullptr)
|
||||
if (!view)
|
||||
return;
|
||||
|
||||
currentView.pView3d = dynamic_cast<const Gui::View3DInventor*>(view);
|
||||
currentView.pView2d = nullptr;
|
||||
if (currentView.pView3d != nullptr) {
|
||||
if (currentView.pView3d) {
|
||||
const Gui::View3DInventorViewer* const inventorViewer = currentView.pView3d->getViewer();
|
||||
if (inventorViewer == nullptr)
|
||||
if (!inventorViewer)
|
||||
return;
|
||||
|
||||
auto pGroup = dynamic_cast<SoGroup* const>(inventorViewer->getSceneGraph());
|
||||
if (pGroup == nullptr)
|
||||
if (!pGroup)
|
||||
return;
|
||||
|
||||
if (pGroup->findChild(pivot.pVisibility) == -1)
|
||||
@@ -198,11 +198,11 @@ void NavlibInterface::enableNavigation()
|
||||
void NavlibInterface::connectActiveTab()
|
||||
{
|
||||
auto pQMdiArea = Gui::MainWindow::getInstance()->findChild<QMdiArea*>();
|
||||
if (pQMdiArea == nullptr)
|
||||
if (!pQMdiArea)
|
||||
return;
|
||||
|
||||
auto pQTabBar = pQMdiArea->findChild<QTabBar*>();
|
||||
if (pQTabBar == nullptr)
|
||||
if (!pQTabBar)
|
||||
return;
|
||||
|
||||
pQTabBar->connect(pQTabBar, &QTabBar::currentChanged, [this, pQTabBar](int idx) {
|
||||
@@ -222,7 +222,7 @@ long NavlibInterface::GetCameraMatrix(navlib::matrix_t& matrix) const
|
||||
|
||||
if (is3DView()) {
|
||||
auto pCamera = getCamera<SoCamera*>();
|
||||
if (pCamera == nullptr)
|
||||
if (!pCamera)
|
||||
return navlib::make_result_code(navlib::navlib_errc::function_not_supported);
|
||||
|
||||
SbMatrix cameraMatrix;
|
||||
@@ -268,7 +268,7 @@ long NavlibInterface::SetCameraMatrix(const navlib::matrix_t& matrix)
|
||||
|
||||
if (is3DView()) {
|
||||
auto pCamera = getCamera<SoCamera*>();
|
||||
if (pCamera == nullptr)
|
||||
if (!pCamera)
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
|
||||
SbMatrix cameraMatrix(matrix(0, 0), matrix(0, 1), matrix(0, 2), matrix(0, 3),
|
||||
@@ -288,7 +288,7 @@ long NavlibInterface::SetCameraMatrix(const navlib::matrix_t& matrix)
|
||||
long NavlibInterface::GetViewFrustum(navlib::frustum_t& frustum) const
|
||||
{
|
||||
const auto pCamera = getCamera<SoPerspectiveCamera* const>();
|
||||
if (pCamera == nullptr)
|
||||
if (!pCamera)
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
|
||||
const SbViewVolume viewVolume = pCamera->getViewVolume(pCamera->aspectRatio.getValue());
|
||||
@@ -327,7 +327,7 @@ long NavlibInterface::GetViewExtents(navlib::box_t& extents) const
|
||||
}
|
||||
|
||||
const auto pCamera = getCamera<SoOrthographicCamera* const>();
|
||||
if (pCamera == nullptr)
|
||||
if (!pCamera)
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
|
||||
const SbViewVolume viewVolume = pCamera->getViewVolume(pCamera->aspectRatio.getValue());
|
||||
@@ -371,7 +371,7 @@ long NavlibInterface::SetViewExtents(const navlib::box_t& extents)
|
||||
|
||||
if (is3DView()) {
|
||||
auto pCamera = getCamera<SoOrthographicCamera* const>();
|
||||
if (pCamera == nullptr)
|
||||
if (!pCamera)
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
|
||||
navlib::box_t oldExtents;
|
||||
@@ -399,13 +399,13 @@ long NavlibInterface::SetViewFOV(double)
|
||||
long NavlibInterface::GetIsViewPerspective(navlib::bool_t& perspective) const
|
||||
{
|
||||
auto pPerspectiveCamera = getCamera<SoPerspectiveCamera* const>();
|
||||
if (pPerspectiveCamera != nullptr) {
|
||||
if (pPerspectiveCamera) {
|
||||
perspective = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto pOrthographicCamera = getCamera<SoOrthographicCamera* const>();
|
||||
if (pOrthographicCamera != nullptr || is2DView()) {
|
||||
if (pOrthographicCamera || is2DView()) {
|
||||
perspective = false;
|
||||
return 0;
|
||||
}
|
||||
@@ -417,7 +417,7 @@ long NavlibInterface::GetModelExtents(navlib::box_t& extents) const
|
||||
{
|
||||
if (is3DView()) {
|
||||
const Gui::View3DInventorViewer* const inventorViewer = currentView.pView3d->getViewer();
|
||||
if (inventorViewer == nullptr)
|
||||
if (!inventorViewer)
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
|
||||
SoGetBoundingBoxAction action(inventorViewer->getSoRenderManager()->getViewportRegion());
|
||||
|
||||
@@ -71,7 +71,7 @@ long NavlibInterface::GetPivotPosition(navlib::point_t&) const
|
||||
|
||||
long NavlibInterface::SetPivotPosition(const navlib::point_t& position)
|
||||
{
|
||||
if (pivot.pTransform == nullptr)
|
||||
if (!pivot.pTransform)
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
|
||||
pivot.pTransform->translation.setValue(position.x, position.y, position.z);
|
||||
@@ -86,7 +86,7 @@ long NavlibInterface::IsUserPivot(navlib::bool_t& userPivot) const
|
||||
|
||||
long NavlibInterface::GetPivotVisible(navlib::bool_t& visible) const
|
||||
{
|
||||
if (pivot.pVisibility == nullptr)
|
||||
if (!pivot.pVisibility)
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
|
||||
visible = pivot.pVisibility->whichChild.getValue() == SO_SWITCH_ALL;
|
||||
@@ -96,7 +96,7 @@ long NavlibInterface::GetPivotVisible(navlib::bool_t& visible) const
|
||||
|
||||
long NavlibInterface::SetPivotVisible(bool visible)
|
||||
{
|
||||
if (pivot.pVisibility == nullptr)
|
||||
if (!pivot.pVisibility)
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
|
||||
if (visible)
|
||||
@@ -115,11 +115,11 @@ long NavlibInterface::GetHitLookAt(navlib::point_t& position) const
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
|
||||
const Gui::View3DInventorViewer* const inventorViewer = currentView.pView3d->getViewer();
|
||||
if (inventorViewer == nullptr)
|
||||
if (!inventorViewer)
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
|
||||
SoNode* pSceneGraph = inventorViewer->getSceneGraph();
|
||||
if (pSceneGraph == nullptr)
|
||||
if (!pSceneGraph)
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
|
||||
// Prepare the ray-picking object
|
||||
@@ -131,7 +131,7 @@ long NavlibInterface::GetHitLookAt(navlib::point_t& position) const
|
||||
// Get the camera rotation
|
||||
SoCamera* pCamera = getCamera<SoCamera*>();
|
||||
|
||||
if (pCamera == nullptr)
|
||||
if (!pCamera)
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
|
||||
pCamera->orientation.getValue().getValue(cameraMatrix);
|
||||
@@ -177,7 +177,7 @@ long NavlibInterface::GetHitLookAt(navlib::point_t& position) const
|
||||
SoPickedPoint* pickedPoint = rayPickAction.getPickedPoint();
|
||||
|
||||
// Check if there was a hit
|
||||
if (pickedPoint != nullptr) {
|
||||
if (pickedPoint) {
|
||||
SbVec3f hitPoint = pickedPoint->getPoint();
|
||||
float distance = (origin - hitPoint).length();
|
||||
|
||||
@@ -213,7 +213,7 @@ long NavlibInterface::GetSelectionExtents(navlib::box_t& extents) const
|
||||
Gui::ViewProvider* pViewProvider =
|
||||
Gui::Application::Instance->getViewProvider(selection.pObject);
|
||||
|
||||
if (pViewProvider == nullptr)
|
||||
if (!pViewProvider)
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
|
||||
boundingBox.Add(pViewProvider->getBoundingBox(selection.SubName, true));
|
||||
@@ -254,7 +254,7 @@ long NavlibInterface::SetHitLookFrom(const navlib::point_t& eye)
|
||||
}
|
||||
else {
|
||||
auto pCamera = getCamera<SoCamera*>();
|
||||
if (pCamera == nullptr) {
|
||||
if (!pCamera) {
|
||||
return navlib::make_result_code(navlib::navlib_errc::no_data_available);
|
||||
}
|
||||
|
||||
|
||||
+76
-27
@@ -227,7 +227,7 @@ struct ApplicationP
|
||||
std::map<const App::Document*, Gui::Document*> documents;
|
||||
/// Active document
|
||||
Gui::Document* activeDocument {nullptr};
|
||||
Gui::Document* editDocument {nullptr};
|
||||
std::vector<Gui::Document*> editDocuments;
|
||||
|
||||
MacroManager* macroMngr;
|
||||
PreferencePackManager* prefPackManager;
|
||||
@@ -631,7 +631,7 @@ Application::Application(bool GUIenabled)
|
||||
PyObject* module = PyImport_AddModule("FreeCADGui");
|
||||
PyMethodDef* meth = FreeCADGui_methods;
|
||||
PyObject* dict = PyModule_GetDict(module);
|
||||
for (; meth->ml_name != nullptr; meth++) {
|
||||
for (; meth->ml_name; meth++) {
|
||||
PyObject* descr;
|
||||
descr = PyCFunction_NewEx(meth, nullptr, nullptr);
|
||||
if (!descr) {
|
||||
@@ -876,10 +876,8 @@ void Application::importFrom(const char* FileName, const char* DocName, const ch
|
||||
"User parameter:BaseApp/Preferences/View"
|
||||
);
|
||||
if (hGrp->GetBool("AutoFitToView", true)) {
|
||||
MDIView* view = doc->getActiveView();
|
||||
if (view) {
|
||||
const char* ret = nullptr;
|
||||
if (view->onMsg("ViewFit", &ret)) {
|
||||
if (MDIView* view = doc->getActiveView()) {
|
||||
if (view->onMsg("ViewFit")) {
|
||||
updateActions(true);
|
||||
}
|
||||
}
|
||||
@@ -1287,6 +1285,9 @@ void Application::slotActiveDocument(const App::Document& Doc)
|
||||
Py::Module("FreeCADGui").setAttr(std::string("ActiveDocument"), Py::None());
|
||||
}
|
||||
}
|
||||
if (!d->activeDocument->workbench().empty()) {
|
||||
activateWorkbench(d->activeDocument->workbench().c_str());
|
||||
}
|
||||
|
||||
// Update the application to show the unit change
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
|
||||
@@ -1404,10 +1405,10 @@ void Application::onLastWindowClosed(Gui::Document* pcDoc)
|
||||
}
|
||||
|
||||
/// send Messages to the active view
|
||||
bool Application::sendMsgToActiveView(const char* pMsg, const char** ppReturn)
|
||||
bool Application::sendMsgToActiveView(const char* pMsg)
|
||||
{
|
||||
MDIView* pView = getMainWindow()->activeWindow();
|
||||
bool res = pView ? pView->onMsg(pMsg, ppReturn) : false;
|
||||
bool res = pView ? pView->onMsg(pMsg) : false;
|
||||
updateActions(true);
|
||||
return res;
|
||||
}
|
||||
@@ -1419,7 +1420,7 @@ bool Application::sendHasMsgToActiveView(const char* pMsg)
|
||||
}
|
||||
|
||||
/// send Messages to the active view
|
||||
bool Application::sendMsgToFocusView(const char* pMsg, const char** ppReturn)
|
||||
bool Application::sendMsgToFocusView(const char* pMsg)
|
||||
{
|
||||
MDIView* pView = getMainWindow()->activeWindow();
|
||||
if (!pView) {
|
||||
@@ -1427,7 +1428,7 @@ bool Application::sendMsgToFocusView(const char* pMsg, const char** ppReturn)
|
||||
}
|
||||
for (auto focus = qApp->focusWidget(); focus; focus = focus->parentWidget()) {
|
||||
if (focus == pView) {
|
||||
bool res = pView->onMsg(pMsg, ppReturn);
|
||||
bool res = pView->onMsg(pMsg);
|
||||
updateActions(true);
|
||||
return res;
|
||||
}
|
||||
@@ -1493,26 +1494,65 @@ Gui::Document* Application::activeDocument() const
|
||||
|
||||
Gui::Document* Application::editDocument() const
|
||||
{
|
||||
return d->editDocument;
|
||||
}
|
||||
|
||||
Gui::MDIView* Application::editViewOfNode(SoNode* node) const
|
||||
{
|
||||
return d->editDocument ? d->editDocument->getViewOfNode(node) : nullptr;
|
||||
}
|
||||
|
||||
void Application::setEditDocument(Gui::Document* doc)
|
||||
{
|
||||
if (!doc) {
|
||||
d->editDocument = nullptr;
|
||||
if (d->editDocuments.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
else if (doc == d->editDocument) {
|
||||
return d->editDocuments[0];
|
||||
}
|
||||
Gui::Document* Application::editDocument(const std::function<bool(Gui::Document*)>& eval)
|
||||
{
|
||||
auto found = std::ranges::find_if(d->editDocuments, eval);
|
||||
|
||||
return found == d->editDocuments.end() ? nullptr : *found;
|
||||
}
|
||||
std::vector<Gui::Document*> Application::editDocuments() const
|
||||
{
|
||||
return d->editDocuments;
|
||||
}
|
||||
bool Application::isInEdit(Gui::Document* pcDocument) const
|
||||
{
|
||||
return std::ranges::find(d->editDocuments, pcDocument) != d->editDocuments.end();
|
||||
}
|
||||
void Application::unsetEditDocument(Gui::Document* pcDocument)
|
||||
{
|
||||
if (std::erase(d->editDocuments, pcDocument) == 0) {
|
||||
return;
|
||||
}
|
||||
for (auto& v : d->documents) {
|
||||
v.second->_resetEdit();
|
||||
|
||||
pcDocument->_resetEdit();
|
||||
updateActions();
|
||||
}
|
||||
void Application::unsetEditDocumentIf(const std::function<bool(Gui::Document*)>& eval)
|
||||
{
|
||||
std::erase_if(d->editDocuments, [&](Gui::Document* doc) {
|
||||
if (eval(doc)) {
|
||||
doc->_resetEdit();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
updateActions();
|
||||
}
|
||||
Gui::MDIView* Application::editViewOfNode(SoNode* node) const
|
||||
{
|
||||
for (auto editDoc : d->editDocuments) {
|
||||
if (Gui::MDIView* view = editDoc->getViewOfNode(node)) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
d->editDocument = doc;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Application::setEditDocument(Gui::Document* pcDocument)
|
||||
{
|
||||
if (pcDocument == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (std::ranges::find(d->editDocuments, pcDocument) != d->editDocuments.end()) {
|
||||
return;
|
||||
}
|
||||
d->editDocuments.push_back(pcDocument);
|
||||
|
||||
updateActions();
|
||||
}
|
||||
|
||||
@@ -1533,12 +1573,18 @@ void Application::setActiveDocument(Gui::Document* pcDocument)
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (d->activeDocument) {
|
||||
d->activeDocument->setIsActive(false);
|
||||
}
|
||||
d->activeDocument = pcDocument;
|
||||
|
||||
std::string nameApp, nameGui;
|
||||
|
||||
// This adds just a line to the macro file but does not set the active document
|
||||
// Macro recording of this is problematic, thus it's written out as comment.
|
||||
if (pcDocument) {
|
||||
pcDocument->setIsActive(true);
|
||||
|
||||
nameApp += "App.setActiveDocument(\"";
|
||||
nameApp += pcDocument->getDocument()->getName();
|
||||
nameApp += "\")\n";
|
||||
@@ -1895,6 +1941,9 @@ bool Application::activateWorkbench(const char* name)
|
||||
}
|
||||
newWb->activated();
|
||||
}
|
||||
if (activeDocument()) {
|
||||
activeDocument()->setWorkbench(name);
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
@@ -2792,7 +2841,7 @@ void Application::setStyle(const QString& name)
|
||||
return qobject_cast<FreeCADStyle*>(style) != nullptr;
|
||||
};
|
||||
|
||||
if (auto* current = qApp->style(); current != nullptr && requiresEventFilter(current)) {
|
||||
if (auto* current = qApp->style(); current && requiresEventFilter(current)) {
|
||||
qApp->removeEventFilter(current);
|
||||
}
|
||||
|
||||
|
||||
+19
-4
@@ -91,11 +91,11 @@ public:
|
||||
/** @name methods for View handling */
|
||||
//@{
|
||||
/// send Messages to the active view
|
||||
bool sendMsgToActiveView(const char* pMsg, const char** ppReturn = nullptr);
|
||||
bool sendMsgToActiveView(const char* pMsg);
|
||||
/// send Messages test to the active view
|
||||
bool sendHasMsgToActiveView(const char* pMsg);
|
||||
/// send Messages to the focused view
|
||||
bool sendMsgToFocusView(const char* pMsg, const char** ppReturn = nullptr);
|
||||
bool sendMsgToFocusView(const char* pMsg);
|
||||
/// send Messages test to the focused view
|
||||
bool sendHasMsgToFocusView(const char* pMsg);
|
||||
/// Attach a view (get called by the FCView constructor)
|
||||
@@ -181,11 +181,26 @@ public:
|
||||
Gui::Document* activeDocument() const;
|
||||
/// Set the active document
|
||||
void setActiveDocument(Gui::Document* pcDocument);
|
||||
/// Getter for the editing document
|
||||
|
||||
/// Getter for the editing document, will be removed soon
|
||||
Gui::Document* editDocument() const;
|
||||
/// Getter for the first editing document that matches a functor
|
||||
Gui::Document* editDocument(const std::function<bool(Gui::Document*)>& eval);
|
||||
/// Getter for all currently editing documents, all pointers are guaranteed to be non-null
|
||||
std::vector<Gui::Document*> editDocuments() const;
|
||||
|
||||
// Returns true if the document is in edit (will make more sense once the edit document it is a
|
||||
// vector)
|
||||
bool isInEdit(Gui::Document* pcDocument) const;
|
||||
// Reset edit if eval returns true for a document in edit
|
||||
|
||||
Gui::MDIView* editViewOfNode(SoNode* node) const;
|
||||
/// Set editing document, which will reset editing of all other document
|
||||
/// Adds a document in edit
|
||||
void setEditDocument(Gui::Document* pcDocument);
|
||||
// After this, isInEdit(pcDocument) returns false
|
||||
void unsetEditDocument(Gui::Document* pcDocument);
|
||||
void unsetEditDocumentIf(const std::function<bool(Gui::Document*)>& eval);
|
||||
|
||||
/** Retrieves a pointer to the Gui::Document whose App::Document has the name \a name.
|
||||
* If no such document exists 0 is returned.
|
||||
*/
|
||||
|
||||
@@ -253,7 +253,7 @@ PyMethodDef ApplicationPy::Methods[] = {
|
||||
{"SendMsgToActiveView",
|
||||
(PyCFunction)ApplicationPy::sSendActiveView,
|
||||
METH_VARARGS,
|
||||
"SendMsgToActiveView(name, suppress=False) -> str or None\n"
|
||||
"SendMsgToActiveView(name, suppress=False) -> None\n"
|
||||
"\n"
|
||||
"Send message to the active view. Deprecated, use class View.\n"
|
||||
"\n"
|
||||
@@ -262,7 +262,7 @@ PyMethodDef ApplicationPy::Methods[] = {
|
||||
{"sendMsgToFocusView",
|
||||
(PyCFunction)ApplicationPy::sSendFocusView,
|
||||
METH_VARARGS,
|
||||
"sendMsgToFocusView(name, suppress=False) -> str or None\n"
|
||||
"sendMsgToFocusView(name, suppress=False) -> None\n"
|
||||
"\n"
|
||||
"Send message to the focused view.\n"
|
||||
"\n"
|
||||
@@ -928,18 +928,12 @@ PyObject* ApplicationPy::sSendActiveView(PyObject* /*self*/, PyObject* args)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* ppReturn = nullptr;
|
||||
if (!Application::Instance->sendMsgToActiveView(psCommandStr, &ppReturn)) {
|
||||
if (!Application::Instance->sendMsgToActiveView(psCommandStr)) {
|
||||
if (!Base::asBoolean(suppress)) {
|
||||
Base::Console().warning("Unknown view command: %s\n", psCommandStr);
|
||||
}
|
||||
}
|
||||
|
||||
// Print the return value to the output
|
||||
if (ppReturn) {
|
||||
return Py_BuildValue("s", ppReturn);
|
||||
}
|
||||
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
@@ -952,18 +946,12 @@ PyObject* ApplicationPy::sSendFocusView(PyObject* /*self*/, PyObject* args)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* ppReturn = nullptr;
|
||||
if (!Application::Instance->sendMsgToFocusView(psCommandStr, &ppReturn)) {
|
||||
if (!Application::Instance->sendMsgToFocusView(psCommandStr)) {
|
||||
if (!Base::asBoolean(suppress)) {
|
||||
Base::Console().warning("Unknown view command: %s\n", psCommandStr);
|
||||
}
|
||||
}
|
||||
|
||||
// Print the return value to the output
|
||||
if (ppReturn) {
|
||||
return Py_BuildValue("s", ppReturn);
|
||||
}
|
||||
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
|
||||
@@ -490,6 +490,7 @@ SET(Gui_UIC_SRCS
|
||||
InputVector.ui
|
||||
Placement.ui
|
||||
TaskTransform.ui
|
||||
TaskCommandLink.ui
|
||||
TextureMapping.ui
|
||||
TaskView/TaskAppearance.ui
|
||||
TaskView/TaskOrientation.ui
|
||||
@@ -578,6 +579,7 @@ SET(Dialog_CPP_SRCS
|
||||
TaskDlgRelocation.cpp
|
||||
Dialogs/DlgCheckableMessageBox.cpp
|
||||
TaskTransform.cpp
|
||||
TaskCommandLink.cpp
|
||||
Dialogs/DlgUndoRedo.cpp
|
||||
InputVector.cpp
|
||||
Placement.cpp
|
||||
@@ -620,6 +622,7 @@ SET(Dialog_HPP_SRCS
|
||||
Dialogs/DlgVersionMigrator.h
|
||||
TaskDlgRelocation.h
|
||||
TaskTransform.h
|
||||
TaskCommandLink.h
|
||||
Dialogs/DlgUndoRedo.h
|
||||
InputVector.h
|
||||
Placement.h
|
||||
|
||||
@@ -226,7 +226,7 @@ QMap<QString, CallTip> CallTipsList::extractTips(const QString& context) const
|
||||
try {
|
||||
PyErr_Clear();
|
||||
Py::Module module("__main__");
|
||||
if (module.ptr() == nullptr) {
|
||||
if (!module.ptr()) {
|
||||
return tips;
|
||||
}
|
||||
Py::Dict dict = module.getDict();
|
||||
|
||||
+27
-4
@@ -21,6 +21,7 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include <limits>
|
||||
#include <functional>
|
||||
|
||||
#include <Inventor/actions/SoGetBoundingBoxAction.h>
|
||||
#include <Inventor/nodes/SoClipPlane.h>
|
||||
@@ -29,6 +30,8 @@
|
||||
#include <QDockWidget>
|
||||
#include <QPointer>
|
||||
|
||||
#include <App/Application.h>
|
||||
|
||||
#include "Clipping.h"
|
||||
#include "ui_Clipping.h"
|
||||
#include "DockWindowManager.h"
|
||||
@@ -51,6 +54,10 @@ public:
|
||||
bool flipY {false};
|
||||
bool flipZ {false};
|
||||
SoTimerSensor* sensor;
|
||||
App::Document* shownOn {nullptr};
|
||||
QDockWidget* dockWidget {nullptr};
|
||||
fastsignals::scoped_connection activeDocConnection;
|
||||
|
||||
Private()
|
||||
{
|
||||
clipX = new SoClipPlane();
|
||||
@@ -99,7 +106,7 @@ public:
|
||||
|
||||
/* TRANSLATOR Gui::Dialog::Clipping */
|
||||
|
||||
Clipping::Clipping(Gui::View3DInventor* view, QWidget* parent)
|
||||
Clipping::Clipping(Gui::View3DInventor* view, App::Document* showOn, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, d(new Private)
|
||||
{
|
||||
@@ -124,6 +131,7 @@ Clipping::Clipping(Gui::View3DInventor* view, QWidget* parent)
|
||||
d->ui.dirZ->setRange(-max, max);
|
||||
d->ui.dirZ->setSingleStep(0.1f);
|
||||
d->ui.dirZ->setValue(1.0f);
|
||||
d->shownOn = showOn;
|
||||
|
||||
d->view = view;
|
||||
View3DInventorViewer* viewer = view->getViewer();
|
||||
@@ -190,14 +198,15 @@ Clipping::Clipping(Gui::View3DInventor* view, QWidget* parent)
|
||||
}
|
||||
}
|
||||
|
||||
Clipping* Clipping::makeDockWidget(Gui::View3DInventor* view)
|
||||
Clipping* Clipping::makeDockWidget(Gui::View3DInventor* view, App::Document* showOn)
|
||||
{
|
||||
// embed this dialog into a QDockWidget
|
||||
auto clipping = new Clipping(view);
|
||||
auto clipping = new Clipping(view, showOn);
|
||||
Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
|
||||
QDockWidget* dw = pDockMgr->addDockWindow("Clipping", clipping, Qt::LeftDockWidgetArea);
|
||||
dw->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
|
||||
dw->show();
|
||||
clipping->d->dockWidget = dw;
|
||||
|
||||
return clipping;
|
||||
}
|
||||
@@ -205,6 +214,7 @@ Clipping* Clipping::makeDockWidget(Gui::View3DInventor* view)
|
||||
/** Destroys the object and frees any allocated resources */
|
||||
Clipping::~Clipping()
|
||||
{
|
||||
d->activeDocConnection.disconnect();
|
||||
d->node->removeChild(d->clipX);
|
||||
d->node->removeChild(d->clipY);
|
||||
d->node->removeChild(d->clipZ);
|
||||
@@ -216,6 +226,8 @@ Clipping::~Clipping()
|
||||
void Clipping::setupConnections()
|
||||
{
|
||||
// clang-format off
|
||||
d->activeDocConnection = App::GetApplication().signalActiveDocument.connect(
|
||||
std::bind(&Clipping::onActiveDocument, this, std::placeholders::_1));
|
||||
connect(d->ui.groupBoxX, &QGroupBox::toggled,
|
||||
this, &Clipping::onGroupBoxXToggled);
|
||||
connect(d->ui.groupBoxY, &QGroupBox::toggled,
|
||||
@@ -259,7 +271,18 @@ void Clipping::reject()
|
||||
dw->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void Clipping::onActiveDocument(const App::Document& doc)
|
||||
{
|
||||
if (!d || !d->dockWidget) {
|
||||
return;
|
||||
}
|
||||
if (&doc == d->shownOn) {
|
||||
d->dockWidget->show();
|
||||
}
|
||||
else {
|
||||
d->dockWidget->hide();
|
||||
}
|
||||
}
|
||||
void Clipping::onGroupBoxXToggled(bool on)
|
||||
{
|
||||
if (on) {
|
||||
|
||||
+8
-2
@@ -26,6 +26,11 @@
|
||||
#include <QDialog>
|
||||
#include <FCGlobal.h>
|
||||
|
||||
namespace App
|
||||
{
|
||||
class Document;
|
||||
}
|
||||
|
||||
namespace Gui
|
||||
{
|
||||
class View3DInventor;
|
||||
@@ -40,12 +45,13 @@ class GuiExport Clipping: public QDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static Clipping* makeDockWidget(Gui::View3DInventor*);
|
||||
Clipping(Gui::View3DInventor* view, QWidget* parent = nullptr);
|
||||
static Clipping* makeDockWidget(Gui::View3DInventor*, App::Document* showOn);
|
||||
Clipping(Gui::View3DInventor* view, App::Document* showOn, QWidget* parent = nullptr);
|
||||
~Clipping() override;
|
||||
|
||||
protected:
|
||||
void setupConnections();
|
||||
void onActiveDocument(const App::Document& doc);
|
||||
void onGroupBoxXToggled(bool);
|
||||
void onGroupBoxYToggled(bool);
|
||||
void onGroupBoxZToggled(bool);
|
||||
|
||||
+59
-24
@@ -36,7 +36,7 @@
|
||||
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/AutoTransaction.h>
|
||||
#include <App/Transactions.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Interpreter.h>
|
||||
@@ -45,6 +45,7 @@
|
||||
|
||||
#include "Command.h"
|
||||
#include "Action.h"
|
||||
#include "App/Application.h"
|
||||
#include "Application.h"
|
||||
#include "BitmapFactory.h"
|
||||
#include "Control.h"
|
||||
@@ -447,11 +448,6 @@ void Command::invoke(int i, TriggerSource trigger)
|
||||
void Command::_invoke(int id, bool disablelog)
|
||||
{
|
||||
try {
|
||||
// Because Transaction now captures ViewObject changes, auto named
|
||||
// transaction is disabled here to avoid too many unnecessary transactions.
|
||||
//
|
||||
App::AutoTransaction committer(nullptr, true);
|
||||
|
||||
// set the application module type for the macro
|
||||
getGuiApplication()->macroManager()->setModule(sAppModule);
|
||||
|
||||
@@ -463,7 +459,6 @@ void Command::_invoke(int id, bool disablelog)
|
||||
// check if it really works NOW (could be a delay between click deactivation of the button)
|
||||
if (isActive()) {
|
||||
auto manager = getGuiApplication()->macroManager();
|
||||
auto editDoc = getGuiApplication()->editDocument();
|
||||
|
||||
if (!logdisabler) {
|
||||
activated(id);
|
||||
@@ -499,12 +494,10 @@ void Command::_invoke(int id, bool disablelog)
|
||||
}
|
||||
|
||||
getMainWindow()->updateActions();
|
||||
|
||||
// If this command starts an editing, let the transaction persist
|
||||
if (!editDoc && getGuiApplication()->editDocument()) {
|
||||
committer.setEnable(false);
|
||||
}
|
||||
}
|
||||
// here we assume that the overriden activated() function
|
||||
// commited, aborted or gave the transaction id to a dialog
|
||||
currentTransactionID = App::NullTransaction; // Get ready for next invoke
|
||||
}
|
||||
catch (const Base::SystemExitException&) {
|
||||
throw;
|
||||
@@ -550,9 +543,10 @@ void Command::testActive()
|
||||
|
||||
if (!(eType & ForEdit)) { // special case for commands which are only in some edit modes active
|
||||
|
||||
if ((!Gui::Control().isAllowedAlterDocument() && eType & AlterDoc)
|
||||
|| (!Gui::Control().isAllowedAlterView() && eType & Alter3DView)
|
||||
|| (!Gui::Control().isAllowedAlterSelection() && eType & AlterSelection)) {
|
||||
App::Document* doc = getDocument();
|
||||
if ((!Gui::Control().isAllowedAlterDocument(doc) && eType & AlterDoc)
|
||||
|| (!Gui::Control().isAllowedAlterView(doc) && eType & Alter3DView)
|
||||
|| (!Gui::Control().isAllowedAlterSelection(doc) && eType & AlterSelection)) {
|
||||
_pcAction->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
@@ -681,27 +675,68 @@ QString Command::translatedGroupName() const
|
||||
* operation default is the Command name.
|
||||
* @see CommitCommand(),AbortCommand()
|
||||
*/
|
||||
void Command::openCommand(const char* sCmdName)
|
||||
int Command::openCommand(App::TransactionName name)
|
||||
{
|
||||
if (!sCmdName) {
|
||||
sCmdName = "Command";
|
||||
currentTransactionID = openActiveDocumentCommand(name);
|
||||
return currentTransactionID;
|
||||
}
|
||||
int Command::openCommand(std::string name)
|
||||
{
|
||||
return openCommand(App::TransactionName {.name = name, .temporary = false});
|
||||
}
|
||||
int Command::openActiveDocumentCommand(App::TransactionName name, int tid)
|
||||
{
|
||||
if (Gui::Document* guidoc = getGuiApplication()->activeDocument()) {
|
||||
return guidoc->getDocument()->setActiveTransaction(name, tid);
|
||||
}
|
||||
App::GetApplication().setActiveTransaction(sCmdName);
|
||||
return 0;
|
||||
}
|
||||
int Command::openActiveDocumentCommand(std::string name, int tid)
|
||||
{
|
||||
return openActiveDocumentCommand(App::TransactionName {.name = name, .temporary = false}, tid);
|
||||
}
|
||||
void Command::rename(const std::string& name)
|
||||
{
|
||||
App::GetApplication().setTransactionName(
|
||||
currentTransactionID,
|
||||
App::TransactionName {.name = name, .temporary = false}
|
||||
);
|
||||
}
|
||||
|
||||
void Command::commitCommand()
|
||||
{
|
||||
App::GetApplication().closeActiveTransaction();
|
||||
commitCommand(currentTransactionID);
|
||||
currentTransactionID = App::NullTransaction;
|
||||
}
|
||||
void Command::commitCommand(int tid)
|
||||
{
|
||||
if (tid != App::NullTransaction) {
|
||||
App::GetApplication().commitTransaction(tid);
|
||||
}
|
||||
}
|
||||
|
||||
void Command::abortCommand()
|
||||
{
|
||||
App::GetApplication().closeActiveTransaction(true);
|
||||
abortCommand(currentTransactionID);
|
||||
currentTransactionID = App::NullTransaction;
|
||||
}
|
||||
void Command::abortCommand(int tid)
|
||||
{
|
||||
if (tid != App::NullTransaction) {
|
||||
App::GetApplication().abortTransaction(tid);
|
||||
}
|
||||
}
|
||||
int Command::transactionID() const
|
||||
{
|
||||
return currentTransactionID;
|
||||
}
|
||||
void Command::resetTransactionID()
|
||||
{
|
||||
currentTransactionID = App::NullTransaction;
|
||||
}
|
||||
|
||||
bool Command::hasPendingCommand()
|
||||
{
|
||||
return !!App::GetApplication().getActiveTransaction();
|
||||
App::Document* doc = App::GetApplication().getActiveDocument();
|
||||
return doc && doc->getBookedTransactionID() != App::NullTransaction;
|
||||
}
|
||||
|
||||
bool Command::_blockCmd = false;
|
||||
|
||||
+18
-3
@@ -473,11 +473,24 @@ public:
|
||||
/** @name Helper methods for the Undo/Redo and Update handling */
|
||||
//@{
|
||||
/// Open a new Undo transaction on the active document
|
||||
static void openCommand(const char* sName = nullptr);
|
||||
int openCommand(App::TransactionName name);
|
||||
int openCommand(std::string name);
|
||||
static int openActiveDocumentCommand(App::TransactionName name, int tid = App::NullTransaction);
|
||||
static int openActiveDocumentCommand(std::string name, int tid = App::NullTransaction);
|
||||
|
||||
void rename(const std::string& name);
|
||||
|
||||
/// Commit the Undo transaction on the active document
|
||||
static void commitCommand();
|
||||
void commitCommand();
|
||||
static void commitCommand(int tid);
|
||||
|
||||
/// Abort the Undo transaction on the active document
|
||||
static void abortCommand();
|
||||
void abortCommand();
|
||||
static void abortCommand(int tid);
|
||||
|
||||
int transactionID() const;
|
||||
void resetTransactionID();
|
||||
|
||||
/// Check if an Undo transaction is open on the active document
|
||||
static bool hasPendingCommand();
|
||||
/// Updates the (active) document (propagate changes)
|
||||
@@ -715,6 +728,8 @@ protected:
|
||||
/// Indicate if the command shall log to MacroManager
|
||||
bool bCanLog;
|
||||
//@}
|
||||
|
||||
int currentTransactionID {0}; // TransactionID created in _invoke
|
||||
private:
|
||||
static int _busy;
|
||||
bool bEnabled;
|
||||
|
||||
+51
-26
@@ -710,6 +710,7 @@ StdCmdNew::StdCmdNew()
|
||||
sStatusTip = sToolTipText;
|
||||
sPixmap = "document-new";
|
||||
sAccel = keySequenceToAccel(QKeySequence::New);
|
||||
eType = NoTransaction;
|
||||
}
|
||||
|
||||
void StdCmdNew::activated(int iMsg)
|
||||
@@ -1446,41 +1447,57 @@ void StdCmdDelete::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
|
||||
std::set<App::Document*> docs;
|
||||
int tid = 0;
|
||||
try {
|
||||
openCommand(QT_TRANSLATE_NOOP("Command", "Delete"));
|
||||
std::set<App::Document*> docs;
|
||||
std::vector<App::TransactionLocker> tlocks;
|
||||
auto manageDocCommand = [&tid, &tlocks](App::Document* doc) {
|
||||
// The tid will not be updated if non-zero
|
||||
tid = doc->openTransaction(QT_TRANSLATE_NOOP("Command", "Delete"), tid);
|
||||
tlocks.emplace_back(doc);
|
||||
};
|
||||
|
||||
if (getGuiApplication()->sendHasMsgToFocusView(getName())) {
|
||||
commitCommand();
|
||||
// no command has been opened yet so we can skip this commit
|
||||
// commitCommand();
|
||||
return;
|
||||
}
|
||||
|
||||
App::TransactionLocker tlock;
|
||||
// Ensure that the document from which we send the command
|
||||
// can undo it (e.g delete a subobject of an assembly
|
||||
// from the assembly file)
|
||||
manageDocCommand(getActiveGuiDocument()->getDocument());
|
||||
|
||||
Gui::getMainWindow()->setUpdatesEnabled(false);
|
||||
auto editDoc = Application::Instance->editDocument();
|
||||
ViewProviderDocumentObject* vpedit = nullptr;
|
||||
if (editDoc) {
|
||||
vpedit = freecad_cast<ViewProviderDocumentObject*>(editDoc->getInEdit());
|
||||
}
|
||||
if (vpedit && !vpedit->acceptDeletionsInEdit()) {
|
||||
for (auto& sel : Selection().getSelectionEx(editDoc->getDocument()->getName())) {
|
||||
if (sel.getObject() == vpedit->getObject()) {
|
||||
if (!sel.getSubNames().empty()) {
|
||||
vpedit->onDelete(sel.getSubNames());
|
||||
docs.insert(editDoc->getDocument());
|
||||
|
||||
bool deletedSelectionOfEditDocument = false;
|
||||
std::vector<Gui::Document*> editDocs = Application::Instance->editDocuments();
|
||||
for (auto& editDoc : editDocs) {
|
||||
auto vpedit = freecad_cast<ViewProviderDocumentObject*>(editDoc->getInEdit());
|
||||
|
||||
// In practice, no ViewProviderDocumentObject accepts deletion in edit - 2025-06-17
|
||||
if (vpedit && !vpedit->acceptDeletionsInEdit()) {
|
||||
for (auto& sel : Selection().getSelectionEx(editDoc->getDocument()->getName())) {
|
||||
if (sel.getObject() == vpedit->getObject()) {
|
||||
if (!sel.getSubNames().empty()) {
|
||||
deletedSelectionOfEditDocument = true;
|
||||
manageDocCommand(editDoc->getDocument());
|
||||
vpedit->onDelete(sel.getSubNames());
|
||||
docs.insert(editDoc->getDocument());
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
if (!deletedSelectionOfEditDocument) {
|
||||
std::set<QString> affectedLabels;
|
||||
bool more = false;
|
||||
auto sels = Selection().getSelectionEx();
|
||||
bool autoDeletion = true;
|
||||
for (auto& sel : sels) {
|
||||
auto obj = sel.getObject();
|
||||
if (obj == nullptr) {
|
||||
if (!obj) {
|
||||
Base::Console().developerWarning(
|
||||
"StdCmdDelete::activated",
|
||||
"App::DocumentObject pointer is nullptr\n"
|
||||
@@ -1548,6 +1565,7 @@ void StdCmdDelete::activated(int iMsg)
|
||||
auto obj = sel.getObject();
|
||||
Gui::ViewProvider* vp = Application::Instance->getViewProvider(obj);
|
||||
if (vp) {
|
||||
manageDocCommand(obj->getDocument());
|
||||
// ask the ViewProvider if it wants to do some clean up
|
||||
if (vp->onDelete(sel.getSubNames())) {
|
||||
docs.insert(obj->getDocument());
|
||||
@@ -1581,6 +1599,8 @@ void StdCmdDelete::activated(int iMsg)
|
||||
QString::fromLatin1(e.what())
|
||||
);
|
||||
e.reportException();
|
||||
App::GetApplication().abortTransaction(tid);
|
||||
tid = 0;
|
||||
}
|
||||
catch (...) {
|
||||
QMessageBox::critical(
|
||||
@@ -1588,8 +1608,11 @@ void StdCmdDelete::activated(int iMsg)
|
||||
QObject::tr("Delete Failed"),
|
||||
QStringLiteral("Unknown error")
|
||||
);
|
||||
App::GetApplication().abortTransaction(tid);
|
||||
tid = 0;
|
||||
}
|
||||
commitCommand();
|
||||
|
||||
App::GetApplication().commitTransaction(tid);
|
||||
Gui::getMainWindow()->setUpdatesEnabled(true);
|
||||
Gui::getMainWindow()->update();
|
||||
}
|
||||
@@ -1635,7 +1658,8 @@ void StdCmdRefresh::activated([[maybe_unused]] int iMsg)
|
||||
return;
|
||||
}
|
||||
|
||||
App::AutoTransaction trans((eType & NoTransaction) ? nullptr : "Recompute");
|
||||
App::AutoTransaction trans((eType & NoTransaction) ? 0 : openActiveDocumentCommand("Recompute"));
|
||||
|
||||
try {
|
||||
doCommand(Doc, "App.activeDocument().recompute(None,True,True)");
|
||||
}
|
||||
@@ -1738,7 +1762,7 @@ void StdCmdPlacement::activated(int iMsg)
|
||||
plm->clearSelection();
|
||||
}
|
||||
}
|
||||
Gui::Control().showDialog(plm);
|
||||
Gui::Control().showDialog(plm, getDocument());
|
||||
}
|
||||
|
||||
bool StdCmdPlacement::isActive()
|
||||
@@ -2133,9 +2157,10 @@ protected:
|
||||
return;
|
||||
}
|
||||
|
||||
openCommand(QT_TRANSLATE_NOOP("Command", "Paste expressions"));
|
||||
int tid = App::NullTransaction;
|
||||
try {
|
||||
for (auto& v : exprs) {
|
||||
tid = v.first->openTransaction(QT_TRANSLATE_NOOP("Command", "Paste expressions"), tid);
|
||||
for (auto& v2 : v.second) {
|
||||
auto& expressions = v2.second;
|
||||
auto old = v2.first->getExpressions();
|
||||
@@ -2152,13 +2177,13 @@ protected:
|
||||
}
|
||||
}
|
||||
}
|
||||
commitCommand();
|
||||
App::GetApplication().commitTransaction(tid);
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
abortCommand();
|
||||
App::GetApplication().abortTransaction(tid);
|
||||
QMessageBox::critical(
|
||||
getMainWindow(),
|
||||
QObject::tr("Failed to Paste Expressions"),
|
||||
QObject::tr("Failed to paste expressions"),
|
||||
QString::fromLatin1(e.what())
|
||||
);
|
||||
e.reportException();
|
||||
|
||||
@@ -330,7 +330,7 @@ Gui::Action* StdCmdToggleSkipRecompute::createAction()
|
||||
void StdCmdToggleSkipRecompute::activated(int iMsg)
|
||||
{
|
||||
const auto doc = this->getDocument();
|
||||
if (doc == nullptr) {
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -345,7 +345,7 @@ void StdCmdToggleSkipRecompute::activated(int iMsg)
|
||||
bool StdCmdToggleSkipRecompute::isActive()
|
||||
{
|
||||
const auto doc = this->getDocument();
|
||||
if (doc == nullptr) {
|
||||
if (!doc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+62
-44
@@ -34,9 +34,11 @@
|
||||
#include "Action.h"
|
||||
#include "Application.h"
|
||||
#include "Command.h"
|
||||
#include "Control.h"
|
||||
#include "Document.h"
|
||||
#include "MainWindow.h"
|
||||
#include "Selection.h"
|
||||
#include "TaskCommandLink.h"
|
||||
#include "Tree.h"
|
||||
#include "ViewProviderDocumentObject.h"
|
||||
#include "WaitCursor.h"
|
||||
@@ -280,6 +282,51 @@ void StdCmdLinkMake::activated(int)
|
||||
return;
|
||||
}
|
||||
|
||||
auto exec = [=](std::vector<App::DocumentObject*> objs) {
|
||||
doc->openTransaction(QT_TRANSLATE_NOOP("Command", "Make link"));
|
||||
try {
|
||||
if (objs.empty()) {
|
||||
std::string name = doc->getUniqueObjectName("Link");
|
||||
Command::doCommand(
|
||||
Command::Doc,
|
||||
"App.getDocument('%s').addObject('App::Link','%s')",
|
||||
doc->getName(),
|
||||
name.c_str()
|
||||
);
|
||||
Selection().addSelection(doc->getName(), name.c_str());
|
||||
}
|
||||
else {
|
||||
for (auto obj : objs) {
|
||||
std::string name = doc->getUniqueObjectName("Link");
|
||||
Command::doCommand(
|
||||
Command::Doc,
|
||||
"App.getDocument('%s').addObject('App::Link','%s').setLink(App.getDocument("
|
||||
"'%s'"
|
||||
").%s)",
|
||||
doc->getName(),
|
||||
name.c_str(),
|
||||
obj->getDocument()->getName(),
|
||||
obj->getNameInDocument()
|
||||
);
|
||||
setLinkLabel(obj, doc->getName(), name.c_str());
|
||||
Selection().addSelection(doc->getName(), name.c_str());
|
||||
}
|
||||
}
|
||||
Selection().selStackPush();
|
||||
doc->commitTransaction();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
doc->abortTransaction();
|
||||
QMessageBox::critical(
|
||||
getMainWindow(),
|
||||
QObject::tr("Create link failed"),
|
||||
QString::fromLatin1(e.what())
|
||||
);
|
||||
e.reportException();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
std::set<App::DocumentObject*> objs;
|
||||
for (auto& sel : Selection().getCompleteSelection()) {
|
||||
if (sel.pObject && sel.pObject->isAttachedToDocument()) {
|
||||
@@ -287,48 +334,13 @@ void StdCmdLinkMake::activated(int)
|
||||
}
|
||||
}
|
||||
|
||||
Selection().selStackPush();
|
||||
Selection().clearCompleteSelection();
|
||||
|
||||
Command::openCommand(QT_TRANSLATE_NOOP("Command", "Make link"));
|
||||
try {
|
||||
if (objs.empty()) {
|
||||
std::string name = doc->getUniqueObjectName("Link");
|
||||
Command::doCommand(
|
||||
Command::Doc,
|
||||
"App.getDocument('%s').addObject('App::Link','%s')",
|
||||
doc->getName(),
|
||||
name.c_str()
|
||||
);
|
||||
Selection().addSelection(doc->getName(), name.c_str());
|
||||
}
|
||||
else {
|
||||
for (auto obj : objs) {
|
||||
std::string name = doc->getUniqueObjectName("Link");
|
||||
Command::doCommand(
|
||||
Command::Doc,
|
||||
"App.getDocument('%s').addObject('App::Link','%s').setLink(App.getDocument('%s'"
|
||||
").%s)",
|
||||
doc->getName(),
|
||||
name.c_str(),
|
||||
obj->getDocument()->getName(),
|
||||
obj->getNameInDocument()
|
||||
);
|
||||
setLinkLabel(obj, doc->getName(), name.c_str());
|
||||
Selection().addSelection(doc->getName(), name.c_str());
|
||||
}
|
||||
}
|
||||
Selection().selStackPush();
|
||||
Command::commitCommand();
|
||||
if (objs.empty()) {
|
||||
Gui::Control().showDialog(new TaskCommandLinkDialog(exec));
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Command::abortCommand();
|
||||
QMessageBox::critical(
|
||||
getMainWindow(),
|
||||
QObject::tr("Create link failed"),
|
||||
QString::fromLatin1(e.what())
|
||||
);
|
||||
e.reportException();
|
||||
else {
|
||||
Selection().selStackPush();
|
||||
Selection().clearCompleteSelection();
|
||||
exec(std::vector<App::DocumentObject*>(objs.begin(), objs.end()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,7 +488,7 @@ static void linkConvert(bool unlink)
|
||||
|
||||
// now, do actual operation
|
||||
const char* transactionName = unlink ? "Unlink" : "Replace with link";
|
||||
Command::openCommand(transactionName);
|
||||
int tid = 0;
|
||||
try {
|
||||
std::unordered_map<App::DocumentObject*, App::DocumentObjectT> recomputeSet;
|
||||
for (auto& v : infos) {
|
||||
@@ -493,6 +505,12 @@ static void linkConvert(bool unlink)
|
||||
recomputeSet.emplace(parent, parent);
|
||||
}
|
||||
auto doc = parent->getDocument();
|
||||
|
||||
tid = doc->openTransaction(
|
||||
App::TransactionName {.name = transactionName, .temporary = false},
|
||||
tid
|
||||
);
|
||||
|
||||
App::DocumentObject* replaceObj;
|
||||
if (unlink) {
|
||||
replaceObj = obj->getLinkedObject(false);
|
||||
@@ -552,10 +570,10 @@ static void linkConvert(bool unlink)
|
||||
recomputes.front()->getDocument()->recompute(recomputes);
|
||||
}
|
||||
|
||||
Command::commitCommand();
|
||||
App::GetApplication().commitTransaction(tid);
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Command::abortCommand();
|
||||
App::GetApplication().abortTransaction(tid);
|
||||
auto title = unlink ? QObject::tr("Unlink failed") : QObject::tr("Replace link failed");
|
||||
QMessageBox::critical(getMainWindow(), title, QString::fromLatin1(e.what()));
|
||||
e.reportException();
|
||||
|
||||
@@ -101,6 +101,8 @@ void StdCmdPart::activated(int iMsg)
|
||||
PartName.c_str()
|
||||
);
|
||||
|
||||
commitCommand();
|
||||
|
||||
updateActive();
|
||||
}
|
||||
|
||||
|
||||
+112
-42
@@ -21,6 +21,9 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
#include <Inventor/events/SoMouseButtonEvent.h>
|
||||
#include <Inventor/nodes/SoOrthographicCamera.h>
|
||||
#include <Inventor/nodes/SoPerspectiveCamera.h>
|
||||
@@ -393,8 +396,15 @@ void StdCmdFreezeViews::activated(int iMsg)
|
||||
}
|
||||
else if (iMsg == 3) {
|
||||
// Create a new view
|
||||
const char* ppReturn = nullptr;
|
||||
getGuiApplication()->sendMsgToActiveView("GetCamera", &ppReturn);
|
||||
auto* view3d = freecad_cast<View3DInventor*>(getGuiApplication()->activeView());
|
||||
if (!view3d) {
|
||||
Base::Console().developerError(
|
||||
"StdCmdFreezeViews",
|
||||
"Expected the active view to be View3DInventor\n"
|
||||
);
|
||||
return;
|
||||
}
|
||||
const std::string& camera = view3d->getCamera();
|
||||
|
||||
QList<QAction*> acts = pcAction->actions();
|
||||
int index = 1;
|
||||
@@ -403,7 +413,7 @@ void StdCmdFreezeViews::activated(int iMsg)
|
||||
savedViews++;
|
||||
QString viewnr = QString(QObject::tr("Restore View &%1")).arg(index);
|
||||
(*it)->setText(viewnr);
|
||||
(*it)->setToolTip(QString::fromLatin1(ppReturn));
|
||||
(*it)->setToolTip(QString::fromStdString(camera));
|
||||
(*it)->setVisible(true);
|
||||
if (index < 10) {
|
||||
(*it)->setShortcut(QKeySequence(QStringLiteral("CTRL+%1").arg(index)));
|
||||
@@ -423,11 +433,28 @@ void StdCmdFreezeViews::activated(int iMsg)
|
||||
// Activate a view
|
||||
QList<QAction*> acts = pcAction->actions();
|
||||
QString data = acts[iMsg]->toolTip();
|
||||
QString send = QStringLiteral("SetCamera %1").arg(data);
|
||||
getGuiApplication()->sendMsgToActiveView(send.toLatin1());
|
||||
MDIView* view = getGuiApplication()->activeView();
|
||||
if (auto* view3D = freecad_cast<View3DInventor*>(view)) {
|
||||
view3D->setCamera(data.toStdString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool StdCmdFreezeViews::isActive()
|
||||
{
|
||||
auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
|
||||
|
||||
separator->setVisible(savedViews > 0);
|
||||
if (view) {
|
||||
saveView->setEnabled(savedViews > 0);
|
||||
freezeView->setEnabled(savedViews < maxViews);
|
||||
clearView->setEnabled(savedViews > 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void StdCmdFreezeViews::onSaveViews()
|
||||
{
|
||||
// Save the views to an XML file
|
||||
@@ -589,23 +616,6 @@ void StdCmdFreezeViews::onRestoreViews()
|
||||
}
|
||||
}
|
||||
|
||||
bool StdCmdFreezeViews::isActive()
|
||||
{
|
||||
auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
|
||||
if (view) {
|
||||
saveView->setEnabled(savedViews > 0);
|
||||
freezeView->setEnabled(savedViews < maxViews);
|
||||
clearView->setEnabled(savedViews > 0);
|
||||
separator->setVisible(savedViews > 0);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
separator->setVisible(savedViews > 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void StdCmdFreezeViews::languageChange()
|
||||
{
|
||||
Command::languageChange();
|
||||
@@ -633,7 +643,34 @@ void StdCmdFreezeViews::languageChange()
|
||||
// Std_ToggleClipPlane
|
||||
//===========================================================================
|
||||
|
||||
DEF_STD_CMD_AC(StdCmdToggleClipPlane)
|
||||
class StdCmdToggleClipPlane: public Gui::Command
|
||||
{
|
||||
public:
|
||||
StdCmdToggleClipPlane();
|
||||
virtual ~StdCmdToggleClipPlane()
|
||||
{}
|
||||
virtual const char* className() const
|
||||
{
|
||||
return "StdCmdToggleClipPlane";
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void activated(int iMsg);
|
||||
virtual bool isActive(void);
|
||||
virtual Gui::Action* createAction(void);
|
||||
|
||||
private:
|
||||
StdCmdToggleClipPlane(const StdCmdToggleClipPlane&) = delete;
|
||||
StdCmdToggleClipPlane(StdCmdToggleClipPlane&&) = delete;
|
||||
StdCmdToggleClipPlane& operator=(const StdCmdToggleClipPlane&) = delete;
|
||||
StdCmdToggleClipPlane& operator=(StdCmdToggleClipPlane&&) = delete;
|
||||
|
||||
void garbageCollect();
|
||||
bool hasClipping(App::Document* doc) const;
|
||||
|
||||
private:
|
||||
std::vector<std::pair<App::Document*, QPointer<Gui::Dialog::Clipping>>> clippings;
|
||||
};
|
||||
|
||||
StdCmdToggleClipPlane::StdCmdToggleClipPlane()
|
||||
: Command("Std_ToggleClipPlane")
|
||||
@@ -656,11 +693,15 @@ Action* StdCmdToggleClipPlane::createAction()
|
||||
void StdCmdToggleClipPlane::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
static QPointer<Gui::Dialog::Clipping> clipping = nullptr;
|
||||
if (!clipping) {
|
||||
App::Document* doc = getActiveDocument();
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
garbageCollect(); // remove dead pointers
|
||||
if (!hasClipping(doc)) {
|
||||
auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
|
||||
if (view) {
|
||||
clipping = Gui::Dialog::Clipping::makeDockWidget(view);
|
||||
clippings.push_back(std::make_pair(doc, Gui::Dialog::Clipping::makeDockWidget(view, doc)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -671,6 +712,31 @@ bool StdCmdToggleClipPlane::isActive()
|
||||
return view ? true : false;
|
||||
}
|
||||
|
||||
void StdCmdToggleClipPlane::garbageCollect()
|
||||
{
|
||||
// We assume the vector to be small
|
||||
std::vector<std::pair<App::Document*, QPointer<Gui::Dialog::Clipping>>> newClippings;
|
||||
newClippings.reserve(clippings.size());
|
||||
std::copy_if(
|
||||
clippings.begin(),
|
||||
clippings.end(),
|
||||
std::back_inserter(newClippings),
|
||||
[](const std::pair<App::Document*, QPointer<Gui::Dialog::Clipping>>& clipPair) -> bool {
|
||||
return clipPair.second != nullptr;
|
||||
}
|
||||
);
|
||||
clippings = newClippings;
|
||||
}
|
||||
bool StdCmdToggleClipPlane::hasClipping(App::Document* doc) const
|
||||
{
|
||||
return std::ranges::find(
|
||||
clippings,
|
||||
doc,
|
||||
&std::pair<App::Document*, QPointer<Gui::Dialog::Clipping>>::first
|
||||
)
|
||||
!= clippings.end();
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// StdCmdDrawStyle
|
||||
//===========================================================================
|
||||
@@ -2630,34 +2696,38 @@ StdCmdViewIvIssueCamPos::StdCmdViewIvIssueCamPos()
|
||||
void StdCmdViewIvIssueCamPos::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
std::string Temp, Temp2;
|
||||
std::string::size_type pos;
|
||||
|
||||
const char* ppReturn = nullptr;
|
||||
getGuiApplication()->sendMsgToActiveView("GetCamera", &ppReturn);
|
||||
auto* view3d = freecad_cast<View3DInventor*>(getGuiApplication()->activeView());
|
||||
if (!view3d) {
|
||||
Base::Console().developerError(
|
||||
"StdCmdViewIvIssueCameraPos",
|
||||
"Expected the active view to be View3DInventor\n"
|
||||
);
|
||||
return;
|
||||
}
|
||||
std::string camera = view3d->getCamera();
|
||||
|
||||
// remove the #inventor line...
|
||||
Temp2 = ppReturn;
|
||||
pos = Temp2.find_first_of("\n");
|
||||
Temp2.erase(0, pos);
|
||||
std::string::size_type pos = camera.find_first_of('\n');
|
||||
camera.erase(0, pos);
|
||||
|
||||
// remove all returns
|
||||
while ((pos = Temp2.find('\n')) != std::string::npos) {
|
||||
Temp2.replace(pos, 1, " ");
|
||||
while ((pos = camera.find('\n')) != std::string::npos) {
|
||||
camera.replace(pos, 1, " ");
|
||||
}
|
||||
|
||||
// build up the command string
|
||||
Temp += "Gui.SendMsgToActiveView(\"SetCamera ";
|
||||
Temp += Temp2;
|
||||
Temp += "\")";
|
||||
std::string command = "Gui.activeView().setCamera(\"";
|
||||
command += camera;
|
||||
command += "\")";
|
||||
|
||||
Base::Console().message("%s\n", Temp2.c_str());
|
||||
getGuiApplication()->macroManager()->addLine(MacroManager::Gui, Temp.c_str());
|
||||
Base::Console().message("%s\n", camera.c_str());
|
||||
getGuiApplication()->macroManager()->addLine(MacroManager::Gui, command.c_str());
|
||||
}
|
||||
|
||||
bool StdCmdViewIvIssueCamPos::isActive()
|
||||
{
|
||||
return getGuiApplication()->sendHasMsgToActiveView("GetCamera");
|
||||
return freecad_cast<View3DInventor*>(getGuiApplication()->activeView());
|
||||
}
|
||||
|
||||
|
||||
@@ -3420,7 +3490,7 @@ StdCmdTextureMapping::StdCmdTextureMapping()
|
||||
void StdCmdTextureMapping::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
Gui::Control().showDialog(new Gui::Dialog::TaskTextureMapping);
|
||||
Gui::Control().showDialog(new Gui::Dialog::TaskTextureMapping, getDocument());
|
||||
}
|
||||
|
||||
bool StdCmdTextureMapping::isActive()
|
||||
|
||||
+110
-51
@@ -26,10 +26,12 @@
|
||||
#include <QDockWidget>
|
||||
#include <QPointer>
|
||||
|
||||
#include <App/AutoTransaction.h>
|
||||
#include <App/Document.h>
|
||||
#include <Gui/Application.h>
|
||||
#include <Gui/ComboView.h>
|
||||
#include <Gui/DockWindowManager.h>
|
||||
#include <Gui/MainWindow.h>
|
||||
#include <Gui/Document.h>
|
||||
|
||||
#include "Control.h"
|
||||
#include "BitmapFactory.h"
|
||||
@@ -45,8 +47,7 @@ using namespace std;
|
||||
ControlSingleton* ControlSingleton::_pcSingleton = nullptr;
|
||||
|
||||
ControlSingleton::ControlSingleton()
|
||||
: ActiveDialog(nullptr)
|
||||
, oldTabIndex(-1)
|
||||
: oldTabIndex(-1)
|
||||
{}
|
||||
|
||||
ControlSingleton::~ControlSingleton() = default;
|
||||
@@ -145,14 +146,28 @@ void ControlSingleton::showModelView()
|
||||
}
|
||||
}
|
||||
|
||||
void ControlSingleton::showDialog(Gui::TaskView::TaskDialog* dlg)
|
||||
void ControlSingleton::showDialog(Gui::TaskView::TaskDialog* dlg, App::Document* attachTo)
|
||||
{
|
||||
attachTo = docOrDefault(attachTo);
|
||||
if (!attachTo) {
|
||||
qWarning() << "ControlSingleton::showDialog: Cannot attach to nullptr document";
|
||||
return;
|
||||
}
|
||||
|
||||
Gui::TaskView::TaskView* taskView = taskPanel();
|
||||
// should return the pointer to combo view
|
||||
if (!taskView) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only one dialog at a time, print a warning instead of raising an assert
|
||||
if (ActiveDialog && ActiveDialog != dlg) {
|
||||
TaskView::TaskDialog* foundDialog = taskView->dialog(attachTo);
|
||||
if (!dlg || foundDialog == dlg) {
|
||||
if (dlg) {
|
||||
qWarning() << "ControlSingleton::showDialog: Can't show "
|
||||
<< dlg->metaObject()->className()
|
||||
<< " since there is already an active task dialog";
|
||||
<< " since there is already an active task dialog in Document "
|
||||
<< (attachTo ? attachTo->getName() : "''");
|
||||
}
|
||||
else {
|
||||
qWarning() << "ControlSingleton::showDialog: Task dialog is null";
|
||||
@@ -160,69 +175,87 @@ void ControlSingleton::showDialog(Gui::TaskView::TaskDialog* dlg)
|
||||
return;
|
||||
}
|
||||
|
||||
// Since the caller sets up a modeless task panel, it indicates intention
|
||||
// for prolonged editing. So disable auto transaction in the current call
|
||||
// stack.
|
||||
// Do this before showing the dialog because its open() function is called
|
||||
// which may open a transaction but fails when auto transaction is still active.
|
||||
App::AutoTransaction::setEnable(false);
|
||||
bool addedDialog = taskView->showDialog(dlg, attachTo);
|
||||
|
||||
Gui::TaskView::TaskView* taskView = taskPanel();
|
||||
// should return the pointer to combo view
|
||||
if (taskView) {
|
||||
taskView->showDialog(dlg);
|
||||
|
||||
// make sure that the combo view is shown
|
||||
auto dw = qobject_cast<QDockWidget*>(taskView->parentWidget());
|
||||
if (dw) {
|
||||
aboutToShowDialog(dw);
|
||||
dw->setVisible(true);
|
||||
dw->toggleViewAction()->setVisible(true);
|
||||
dw->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
|
||||
}
|
||||
|
||||
if (ActiveDialog == dlg) {
|
||||
return; // dialog is already defined
|
||||
}
|
||||
ActiveDialog = dlg;
|
||||
connect(dlg, &TaskView::TaskDialog::aboutToBeDestroyed, this, &ControlSingleton::closedDialog);
|
||||
// make sure that the combo view is shown
|
||||
if (auto dw = qobject_cast<QDockWidget*>(taskView->parentWidget())) {
|
||||
aboutToShowDialog(dw);
|
||||
dw->setVisible(true);
|
||||
dw->toggleViewAction()->setVisible(true);
|
||||
dw->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
|
||||
}
|
||||
|
||||
if (!addedDialog) {
|
||||
return; // dialog is already defined
|
||||
}
|
||||
|
||||
connect(dlg, &TaskView::TaskDialog::aboutToBeDestroyed, this, [this, attachTo] {
|
||||
closedDialog(attachTo);
|
||||
});
|
||||
}
|
||||
|
||||
Gui::TaskView::TaskDialog* ControlSingleton::activeDialog() const
|
||||
Gui::TaskView::TaskDialog* ControlSingleton::activeDialog(App::Document* attachedTo) const
|
||||
{
|
||||
return ActiveDialog;
|
||||
attachedTo = docOrDefault(attachedTo);
|
||||
if (!attachedTo) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Gui::TaskView::TaskView* taskView = taskPanel();
|
||||
|
||||
if (taskView) {
|
||||
return taskView->dialog(attachedTo);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ControlSingleton::accept()
|
||||
void ControlSingleton::accept(App::Document* attachedTo)
|
||||
{
|
||||
attachedTo = docOrDefault(attachedTo);
|
||||
if (!attachedTo) {
|
||||
qWarning() << "ControlSingleton::accept: Cannot accept dialog of nullptr document";
|
||||
return;
|
||||
}
|
||||
|
||||
Gui::TaskView::TaskView* taskView = taskPanel();
|
||||
if (taskView) {
|
||||
taskView->accept();
|
||||
taskView->accept(attachedTo);
|
||||
qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
|
||||
}
|
||||
}
|
||||
|
||||
void ControlSingleton::reject()
|
||||
void ControlSingleton::reject(App::Document* attachedTo)
|
||||
{
|
||||
attachedTo = docOrDefault(attachedTo);
|
||||
if (!attachedTo) {
|
||||
qWarning() << "ControlSingleton::reject: Cannot reject dialog of nullptr document";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Gui::TaskView::TaskView* taskView = taskPanel();
|
||||
if (taskView) {
|
||||
taskView->reject();
|
||||
taskView->reject(attachedTo);
|
||||
qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
|
||||
}
|
||||
}
|
||||
|
||||
void ControlSingleton::closeDialog()
|
||||
void ControlSingleton::closeDialog(App::Document* attachedTo)
|
||||
{
|
||||
attachedTo = docOrDefault(attachedTo);
|
||||
if (!attachedTo) {
|
||||
qWarning() << "ControlSingleton::closeDialog: Cannot close dialog of nullptr document";
|
||||
return;
|
||||
}
|
||||
|
||||
Gui::TaskView::TaskView* taskView = taskPanel();
|
||||
if (taskView) {
|
||||
taskView->removeDialog();
|
||||
taskView->removeDialog(attachedTo);
|
||||
}
|
||||
}
|
||||
|
||||
void ControlSingleton::closedDialog()
|
||||
void ControlSingleton::closedDialog(App::Document* attachedTo)
|
||||
{
|
||||
ActiveDialog = nullptr;
|
||||
Gui::TaskView::TaskView* taskView = taskPanel();
|
||||
assert(taskView);
|
||||
|
||||
@@ -237,31 +270,57 @@ void ControlSingleton::closedDialog()
|
||||
}
|
||||
}
|
||||
|
||||
bool ControlSingleton::isAllowedAlterDocument() const
|
||||
bool ControlSingleton::isAllowedAlterDocument(App::Document* attachedTo) const
|
||||
{
|
||||
if (ActiveDialog) {
|
||||
return ActiveDialog->isAllowedAlterDocument();
|
||||
attachedTo = docOrDefault(attachedTo);
|
||||
if (!attachedTo) {
|
||||
return true;
|
||||
}
|
||||
Gui::TaskView::TaskDialog* dlg = activeDialog(attachedTo);
|
||||
|
||||
if (dlg) {
|
||||
return dlg->isAllowedAlterDocument();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ControlSingleton::isAllowedAlterView() const
|
||||
bool ControlSingleton::isAllowedAlterView(App::Document* attachedTo) const
|
||||
{
|
||||
if (ActiveDialog) {
|
||||
return ActiveDialog->isAllowedAlterView();
|
||||
attachedTo = docOrDefault(attachedTo);
|
||||
if (!attachedTo) {
|
||||
return true;
|
||||
}
|
||||
Gui::TaskView::TaskDialog* dlg = activeDialog(attachedTo);
|
||||
|
||||
if (dlg) {
|
||||
return dlg->isAllowedAlterView();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ControlSingleton::isAllowedAlterSelection() const
|
||||
bool ControlSingleton::isAllowedAlterSelection(App::Document* attachedTo) const
|
||||
{
|
||||
if (ActiveDialog) {
|
||||
return ActiveDialog->isAllowedAlterSelection();
|
||||
attachedTo = docOrDefault(attachedTo);
|
||||
if (!attachedTo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Gui::TaskView::TaskDialog* dlg = activeDialog(attachedTo);
|
||||
|
||||
if (dlg) {
|
||||
return dlg->isAllowedAlterSelection();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
App::Document* ControlSingleton::docOrDefault(App::Document* attachedTo)
|
||||
{
|
||||
if (!attachedTo && Application::Instance->activeDocument()) {
|
||||
attachedTo = Application::Instance->activeDocument()->getDocument();
|
||||
}
|
||||
return attachedTo;
|
||||
}
|
||||
|
||||
// -------------------------------------------
|
||||
|
||||
ControlSingleton& ControlSingleton::instance()
|
||||
|
||||
+16
-11
@@ -64,8 +64,9 @@ public:
|
||||
*/
|
||||
//@{
|
||||
/// This method starts a task dialog in the task view
|
||||
void showDialog(Gui::TaskView::TaskDialog* dlg);
|
||||
Gui::TaskView::TaskDialog* activeDialog() const;
|
||||
/// The dialog is relative to a specific document
|
||||
void showDialog(Gui::TaskView::TaskDialog* dlg, App::Document* attachTo = nullptr);
|
||||
Gui::TaskView::TaskDialog* activeDialog(App::Document* attachedTo = nullptr) const;
|
||||
// void closeDialog();
|
||||
//@}
|
||||
|
||||
@@ -81,28 +82,29 @@ public:
|
||||
If a task dialog is open then it indicates whether this task dialog allows other commands to
|
||||
modify the document while it is open. If no task dialog is open true is returned.
|
||||
*/
|
||||
bool isAllowedAlterDocument() const;
|
||||
bool isAllowedAlterDocument(App::Document* attachedTo = nullptr) const;
|
||||
/*!
|
||||
If a task dialog is open then it indicates whether this task dialog allows other commands to
|
||||
modify the 3d view while it is open. If no task dialog is open true is returned.
|
||||
*/
|
||||
bool isAllowedAlterView() const;
|
||||
bool isAllowedAlterView(App::Document* attachedTo = nullptr) const;
|
||||
/*!
|
||||
If a task dialog is open then it indicates whether this task dialog allows other commands to
|
||||
modify the selection while it is open. If no task dialog is open true is returned.
|
||||
*/
|
||||
bool isAllowedAlterSelection() const;
|
||||
bool isAllowedAlterSelection(App::Document* attachedTo = nullptr) const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void accept();
|
||||
void reject();
|
||||
void closeDialog();
|
||||
void accept(App::Document* attachedTo = nullptr);
|
||||
void reject(App::Document* attachedTo = nullptr);
|
||||
void closeDialog(App::Document* attachedTo = nullptr);
|
||||
|
||||
/// raises the task view panel
|
||||
void showTaskView();
|
||||
|
||||
private Q_SLOTS:
|
||||
private:
|
||||
/// This get called by the TaskView when the Dialog is finished
|
||||
void closedDialog();
|
||||
void closedDialog(App::Document* attachedTo = nullptr);
|
||||
|
||||
private:
|
||||
struct status
|
||||
@@ -112,7 +114,7 @@ private:
|
||||
|
||||
std::stack<status> StatusStack;
|
||||
|
||||
Gui::TaskView::TaskDialog* ActiveDialog;
|
||||
std::map<App::Document*, Gui::TaskView::TaskDialog*> ActiveDialogs;
|
||||
int oldTabIndex;
|
||||
|
||||
private:
|
||||
@@ -125,6 +127,9 @@ private:
|
||||
void aboutToShowDialog(QDockWidget* widget);
|
||||
void aboutToHideDialog(QDockWidget* widget);
|
||||
|
||||
// Returns attachTo if not nullptr, otherwise return the active document
|
||||
static App::Document* docOrDefault(App::Document* attachedTo);
|
||||
|
||||
static ControlSingleton* _pcSingleton;
|
||||
};
|
||||
|
||||
|
||||
@@ -194,14 +194,13 @@ void DemoMode::onAngleSliderValueChanged(int v)
|
||||
void DemoMode::reorientCamera(SoCamera* cam, const SbRotation& rot)
|
||||
{
|
||||
// Find global coordinates of focal point.
|
||||
SbVec3f direction;
|
||||
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
|
||||
SbVec3f focalpoint = cam->position.getValue() + cam->focalDistance.getValue() * direction;
|
||||
SbVec3f focalpoint = activeView()->getViewer()->getFocalPoint();
|
||||
|
||||
// Set new orientation value by accumulating the new rotation.
|
||||
cam->orientation = rot * cam->orientation.getValue();
|
||||
|
||||
// Reposition camera so we are still pointing at the same old focal point.
|
||||
SbVec3f direction;
|
||||
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
|
||||
cam->position = focalpoint - cam->focalDistance.getValue() * direction;
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ int DlgAddProperty::findLabelRow(const char* labelName, QFormLayout* layout)
|
||||
{
|
||||
for (int row = 0; row < layout->rowCount(); ++row) {
|
||||
QLayoutItem* labelItem = layout->itemAt(row, QFormLayout::LabelRole);
|
||||
if (labelItem == nullptr) {
|
||||
if (!labelItem) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ void DlgAddProperty::removeExistingWidget(QFormLayout* formLayout, int labelRow)
|
||||
void DlgAddProperty::setWidgetForLabel(const char* labelName, QWidget* widget, QLayout* layout)
|
||||
{
|
||||
auto formLayout = qobject_cast<QFormLayout*>(layout);
|
||||
if (formLayout == nullptr) {
|
||||
if (!formLayout) {
|
||||
FC_ERR("Form layout not found");
|
||||
return;
|
||||
}
|
||||
@@ -403,7 +403,7 @@ void DlgAddProperty::addEditor(PropertyItem* propertyItem)
|
||||
else {
|
||||
addNormalEditor(propertyItem);
|
||||
}
|
||||
if (editor == nullptr) {
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -451,12 +451,12 @@ bool DlgAddProperty::isExcluded(const Base::Type& type) const
|
||||
|
||||
bool DlgAddProperty::isTypeWithEditor(PropertyItem* propertyItem) const
|
||||
{
|
||||
if (propertyItem == nullptr) {
|
||||
if (!propertyItem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
App::Property* prop = propertyItem->getFirstProperty();
|
||||
if (prop == nullptr) {
|
||||
if (!prop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -689,7 +689,7 @@ void DlgAddProperty::showStatusMessage()
|
||||
|
||||
void DlgAddProperty::removeEditor()
|
||||
{
|
||||
if (editor == nullptr) {
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -713,7 +713,7 @@ QVariant DlgAddProperty::getEditorData() const
|
||||
{
|
||||
if (isEnumPropertyItem()) {
|
||||
PropertyItem* child = propertyItem->child(0);
|
||||
if (child == nullptr) {
|
||||
if (!child) {
|
||||
return {};
|
||||
}
|
||||
return child->editorData(editor.get());
|
||||
@@ -726,7 +726,7 @@ void DlgAddProperty::setEditorData(const QVariant& data)
|
||||
{
|
||||
if (isEnumPropertyItem()) {
|
||||
PropertyItem* child = propertyItem->child(0);
|
||||
if (child == nullptr) {
|
||||
if (!child) {
|
||||
return;
|
||||
}
|
||||
child->setEditorData(editor.get(), data);
|
||||
@@ -741,7 +741,7 @@ void DlgAddProperty::setEditor(bool valueNeedsReset)
|
||||
if (editor && !valueNeedsReset) {
|
||||
QVariant data = getEditorData();
|
||||
addEditor(propertyItem.get());
|
||||
if (editor == nullptr) {
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
setEditorData(data);
|
||||
@@ -762,15 +762,15 @@ void DlgAddProperty::setEditor(bool valueNeedsReset)
|
||||
|
||||
void DlgAddProperty::setPropertyItem(App::Property* prop, bool supportsExpressions)
|
||||
{
|
||||
if (prop == nullptr) {
|
||||
if (!prop) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (propertyItem == nullptr) {
|
||||
if (!propertyItem) {
|
||||
propertyItem.reset(createPropertyItem(prop));
|
||||
}
|
||||
|
||||
if (propertyItem == nullptr) {
|
||||
if (!propertyItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -816,7 +816,7 @@ bool DlgAddProperty::clearBoundProperty()
|
||||
|
||||
bool DlgAddProperty::clear(FieldChange fieldChange)
|
||||
{
|
||||
if (propertyItem == nullptr) {
|
||||
if (!propertyItem) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -890,7 +890,7 @@ void DlgAddProperty::changeEvent(QEvent* e)
|
||||
void DlgAddProperty::valueChangedEnum()
|
||||
{
|
||||
auto* propEnum = static_cast<App::PropertyEnumeration*>(propertyItem->getFirstProperty());
|
||||
if (propEnum == nullptr || propertyItem->childCount() == 0) {
|
||||
if (!propEnum || propertyItem->childCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -920,7 +920,9 @@ void DlgAddProperty::valueChanged()
|
||||
*/
|
||||
void DlgAddProperty::openTransaction()
|
||||
{
|
||||
transactionID = App::GetApplication().setActiveTransaction("Add property");
|
||||
transactionID = App::GetApplication().setActiveTransaction(
|
||||
App::TransactionName {.name = "Add property", .temporary = false}
|
||||
);
|
||||
}
|
||||
|
||||
void DlgAddProperty::critical(const QString& title, const QString& text)
|
||||
@@ -993,7 +995,12 @@ void DlgAddProperty::closeTransaction(TransactionOption option)
|
||||
return;
|
||||
}
|
||||
|
||||
App::GetApplication().closeActiveTransaction(static_cast<bool>(option), transactionID);
|
||||
if (option == TransactionOption::Abort) {
|
||||
App::GetApplication().abortTransaction(transactionID);
|
||||
}
|
||||
else {
|
||||
App::GetApplication().commitTransaction(transactionID);
|
||||
}
|
||||
transactionID = 0;
|
||||
}
|
||||
|
||||
@@ -1019,13 +1026,13 @@ void DlgAddProperty::addDocumentation()
|
||||
std::string group = comboBoxGroup.currentText().toStdString();
|
||||
std::string doc = ui->lineEditToolTip->text().toStdString();
|
||||
|
||||
if (propertyItem == nullptr) {
|
||||
if (!propertyItem) {
|
||||
// If there is no property item, we cannot add documentation.
|
||||
return;
|
||||
}
|
||||
|
||||
App::Property* prop = propertyItem->getFirstProperty();
|
||||
if (prop == nullptr) {
|
||||
if (!prop) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -697,7 +697,7 @@ static App::Document* getPreselectedDocument()
|
||||
}
|
||||
|
||||
App::Document* doc = App::GetApplication().getDocument(lastDoc.c_str());
|
||||
if (doc == nullptr) {
|
||||
if (!doc) {
|
||||
return App::GetApplication().getActiveDocument();
|
||||
}
|
||||
|
||||
@@ -728,7 +728,7 @@ int DlgExpressionInput::getVarSetIndex(const App::Document* doc) const
|
||||
void DlgExpressionInput::preselectVarSet()
|
||||
{
|
||||
const App::Document* doc = getPreselectedDocument();
|
||||
if (doc == nullptr) {
|
||||
if (!doc) {
|
||||
FC_ERR("No active document found");
|
||||
}
|
||||
ui->comboBoxVarSet->setCurrentIndex(getVarSetIndex(doc));
|
||||
@@ -763,7 +763,7 @@ static void addVarSetsVarSetComboBox(
|
||||
auto* vp = freecad_cast<Gui::ViewProviderDocumentObject*>(
|
||||
Gui::Application::Instance->getViewProvider(varSet)
|
||||
);
|
||||
if (vp == nullptr) {
|
||||
if (!vp) {
|
||||
FC_ERR("No ViewProvider found for VarSet: " << varSet->getNameInDocument());
|
||||
continue;
|
||||
}
|
||||
@@ -889,13 +889,13 @@ void DlgExpressionInput::onVarSetSelected(int /*index*/)
|
||||
}
|
||||
|
||||
App::Document* doc = App::GetApplication().getDocument(docName.toUtf8());
|
||||
if (doc == nullptr) {
|
||||
if (!doc) {
|
||||
FC_ERR("Document not found: " << docName.toStdString());
|
||||
return;
|
||||
}
|
||||
|
||||
App::DocumentObject* varSet = doc->getObject(varSetName.toUtf8());
|
||||
if (varSet == nullptr) {
|
||||
if (!varSet) {
|
||||
FC_ERR("Variable set not found: " << varSetName.toStdString());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
return "DockWindow";
|
||||
}
|
||||
/// Message handler
|
||||
bool onMsg(const char*, const char**) override
|
||||
bool onMsg(const char*) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
+78
-51
@@ -90,6 +90,7 @@ struct DocumentP
|
||||
bool _isClosing;
|
||||
bool _isModified;
|
||||
bool _isTransacting;
|
||||
bool _isActive;
|
||||
bool _changeViewTouchDocument;
|
||||
bool _editWantsRestore;
|
||||
bool _editWantsRestorePrevious;
|
||||
@@ -101,6 +102,7 @@ struct DocumentP
|
||||
ViewProviderDocumentObject* _editViewProviderParent;
|
||||
std::string _editSubname;
|
||||
std::string _editSubElement;
|
||||
std::string _workbenchName; // Name of the workbench acting on this document
|
||||
Base::Matrix4D _editingTransform;
|
||||
View3DInventorViewer* _editingViewer;
|
||||
std::set<const App::DocumentObject*> _editObjs;
|
||||
@@ -284,6 +286,8 @@ struct DocumentP
|
||||
{
|
||||
_editingObject = sobj;
|
||||
_editMode = ModNum;
|
||||
_editViewProvider = svp; // Used to resolve start editing (find the document in edit from
|
||||
// within the viewprovider)
|
||||
_editViewProvider = svp->startEditing(ModNum);
|
||||
if (!_editViewProvider) {
|
||||
_editViewProviderParent = nullptr;
|
||||
@@ -313,7 +317,7 @@ struct DocumentP
|
||||
|
||||
void setDocumentNameOfTaskDialog(App::Document* doc)
|
||||
{
|
||||
Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog();
|
||||
Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(_pcDocument);
|
||||
if (dlg) {
|
||||
dlg->setDocumentName(doc->getName());
|
||||
}
|
||||
@@ -432,6 +436,7 @@ Document::Document(App::Document* pcDocument, Application* app)
|
||||
d->_isClosing = false;
|
||||
d->_isModified = false;
|
||||
d->_isTransacting = false;
|
||||
d->_isActive = false;
|
||||
d->_pcAppWnd = app;
|
||||
d->_pcDocument = pcDocument;
|
||||
d->_editViewProvider = nullptr;
|
||||
@@ -690,7 +695,6 @@ bool Document::trySetEdit(Gui::ViewProvider* p, int ModNum, const char* subname)
|
||||
d->setEditingViewerIfPossible(view3d, ModNum);
|
||||
d->signalEditMode();
|
||||
|
||||
App::AutoTransaction::setEnable(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -717,7 +721,7 @@ void Document::resetEdit()
|
||||
Gui::ViewProvider* vpToRestore = d->_editViewProviderPrevious;
|
||||
bool shouldRestorePrevious = d->_editWantsRestorePrevious;
|
||||
|
||||
Application::Instance->setEditDocument(nullptr);
|
||||
Application::Instance->unsetEditDocument(this);
|
||||
|
||||
if (vpIsNotNull && vpHasChanged && shouldRestorePrevious) {
|
||||
setEdit(vpToRestore, modeToRestore);
|
||||
@@ -755,18 +759,16 @@ void Document::_resetEdit()
|
||||
|
||||
// The logic below is not necessary anymore, because this method is
|
||||
// changed into a private one, _resetEdit(). And the exposed
|
||||
// resetEdit() above calls into Application->setEditDocument(0) which
|
||||
// resetEdit() above calls into Application->unsetEditDocument() which
|
||||
// will prevent recursive calling.
|
||||
|
||||
App::GetApplication().closeActiveTransaction();
|
||||
App::GetApplication().commitTransaction(getDocument()->getBookedTransactionID());
|
||||
}
|
||||
d->_editViewProviderParent = nullptr;
|
||||
d->_editingViewer = nullptr;
|
||||
d->_editObjs.clear();
|
||||
d->_editingObject = nullptr;
|
||||
if (Application::Instance->editDocument() == this) {
|
||||
Application::Instance->setEditDocument(nullptr);
|
||||
}
|
||||
Application::Instance->unsetEditDocument(this);
|
||||
}
|
||||
|
||||
ViewProvider* Document::getInEdit(
|
||||
@@ -799,6 +801,10 @@ ViewProvider* Document::getInEdit(
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
ViewProvider* Document::getEditViewProvider() const
|
||||
{
|
||||
return d->_editViewProvider;
|
||||
}
|
||||
|
||||
void Document::setInEdit(ViewProviderDocumentObject* parentVp, const char* subname)
|
||||
{
|
||||
@@ -1068,12 +1074,11 @@ void Document::slotDeletedObject(const App::DocumentObject& Obj)
|
||||
if (d->_editViewProvider == viewProvider || d->_editViewProviderParent == viewProvider) {
|
||||
_resetEdit();
|
||||
}
|
||||
else if (Application::Instance->editDocument()) {
|
||||
auto editDoc = Application::Instance->editDocument();
|
||||
if (editDoc->d->_editViewProvider == viewProvider
|
||||
|| editDoc->d->_editViewProviderParent == viewProvider) {
|
||||
Application::Instance->setEditDocument(nullptr);
|
||||
}
|
||||
else {
|
||||
Application::Instance->unsetEditDocumentIf([&viewProvider](Gui::Document* editdoc) {
|
||||
return editdoc->d->_editViewProvider == viewProvider
|
||||
|| editdoc->d->_editViewProviderParent == viewProvider;
|
||||
});
|
||||
}
|
||||
|
||||
handleChildren3D(viewProvider, true);
|
||||
@@ -1096,15 +1101,13 @@ void Document::slotDeletedObject(const App::DocumentObject& Obj)
|
||||
|
||||
void Document::beforeDelete()
|
||||
{
|
||||
auto editDoc = Application::Instance->editDocument();
|
||||
if (editDoc) {
|
||||
Application::Instance->unsetEditDocumentIf([this](Gui::Document* editDoc) {
|
||||
auto vp = freecad_cast<ViewProviderDocumentObject*>(editDoc->d->_editViewProvider);
|
||||
auto vpp = freecad_cast<ViewProviderDocumentObject*>(editDoc->d->_editViewProviderParent);
|
||||
if (editDoc == this || (vp && vp->getDocument() == this)
|
||||
|| (vpp && vpp->getDocument() == this)) {
|
||||
Application::Instance->setEditDocument(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return editDoc == this || (vp && vp->getDocument() == this)
|
||||
|| (vpp && vpp->getDocument() == this);
|
||||
});
|
||||
for (auto& v : d->_ViewProviderMap) {
|
||||
v.second->beforeDelete();
|
||||
}
|
||||
@@ -1251,6 +1254,18 @@ void Document::slotSkipRecompute(const App::Document& doc, const std::vector<App
|
||||
return;
|
||||
}
|
||||
App::DocumentObject* obj = nullptr;
|
||||
|
||||
if (Gui::Application::Instance->isInEdit(this)) {
|
||||
auto vp = freecad_cast<ViewProviderDocumentObject*>(getInEdit());
|
||||
if (vp) {
|
||||
obj = vp->getObject();
|
||||
}
|
||||
}
|
||||
if (objs.size() > 1 || App::GetApplication().getActiveDocument() != &doc
|
||||
|| !doc.testStatus(App::Document::AllowPartialRecompute)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto editDoc = Application::Instance->editDocument();
|
||||
if (editDoc) {
|
||||
auto vp = freecad_cast<ViewProviderDocumentObject*>(editDoc->getInEdit());
|
||||
@@ -1329,6 +1344,14 @@ bool Document::isModified() const
|
||||
{
|
||||
return d->_isModified;
|
||||
}
|
||||
void Document::setWorkbench(const std::string& name)
|
||||
{
|
||||
d->_workbenchName = name;
|
||||
}
|
||||
std::string Document::workbench() const
|
||||
{
|
||||
return d->_workbenchName;
|
||||
}
|
||||
|
||||
bool Document::isAboutToClose() const
|
||||
{
|
||||
@@ -1399,6 +1422,17 @@ App::Document* Document::getDocument() const
|
||||
{
|
||||
return d->_pcDocument;
|
||||
}
|
||||
void Document::setIsActive(bool active)
|
||||
{
|
||||
d->_isActive = active;
|
||||
if (d->_editViewProvider) {
|
||||
d->_editViewProvider->setActive(active);
|
||||
}
|
||||
}
|
||||
bool Document::isActive() const
|
||||
{
|
||||
return d->_isActive;
|
||||
}
|
||||
|
||||
static bool checkCanonicalPath(const std::map<App::Document*, bool>& docs)
|
||||
{
|
||||
@@ -1568,7 +1602,7 @@ bool Document::save()
|
||||
for (auto doc : docs) {
|
||||
// Changed 'mustExecute' status may be triggered by saving external document
|
||||
if (!dmap[doc] && doc->mustExecute()) {
|
||||
App::AutoTransaction trans("Recompute");
|
||||
App::AutoTransaction trans(doc, "Recompute");
|
||||
Command::doCommand(
|
||||
Command::Doc,
|
||||
"App.getDocument(\"%s\").recompute()",
|
||||
@@ -1711,7 +1745,7 @@ void Document::saveAll()
|
||||
try {
|
||||
// Changed 'mustExecute' status may be triggered by saving external document
|
||||
if (!dmap[doc] && doc->mustExecute()) {
|
||||
App::AutoTransaction trans("Recompute");
|
||||
App::AutoTransaction trans(doc, "Recompute");
|
||||
Command::doCommand(Command::Doc, "App.getDocument('%s').recompute()", doc->getName());
|
||||
}
|
||||
Command::doCommand(Command::Doc, "App.getDocument('%s').save()", doc->getName());
|
||||
@@ -1898,11 +1932,9 @@ void Document::RestoreDocFile(Base::Reader& reader)
|
||||
if (!Base::Tools::isNullOrEmpty(ppReturn)) {
|
||||
saveCameraSettings(ppReturn);
|
||||
try {
|
||||
const char** pReturnIgnore = nullptr;
|
||||
std::list<MDIView*> mdi = getMDIViews();
|
||||
for (const auto& it : mdi) {
|
||||
if (it->onHasMsg("SetCamera")) {
|
||||
it->onMsg(cameraSettings.c_str(), pReturnIgnore);
|
||||
for (const auto& it : getMDIViews()) {
|
||||
if (auto* view3D = freecad_cast<View3DInventor*>(it)) {
|
||||
view3D->setCamera(cameraSettings.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2023,12 +2055,10 @@ void Document::SaveDocFile(Base::Writer& writer) const
|
||||
writer.decInd(); // indentation for 'ViewProviderData Count'
|
||||
|
||||
// save camera settings
|
||||
std::list<MDIView*> mdi = getMDIViews();
|
||||
for (const auto& it : mdi) {
|
||||
if (it->onHasMsg("GetCamera")) {
|
||||
const char* ppReturn = nullptr;
|
||||
it->onMsg("GetCamera", &ppReturn);
|
||||
if (saveCameraSettings(ppReturn)) {
|
||||
for (const auto& it : getMDIViews()) {
|
||||
if (auto* view3D = freecad_cast<View3DInventor*>(it)) {
|
||||
const std::string& camera = view3D->getCamera();
|
||||
if (saveCameraSettings(camera.c_str())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2217,9 +2247,8 @@ MDIView* Document::createView(const Base::Type& typeId, CreateViewMode mode)
|
||||
auto firstView = static_cast<View3DInventor*>(theViews.front());
|
||||
shareWidget = qobject_cast<QOpenGLWidget*>(firstView->getViewer()->getGLWidget());
|
||||
|
||||
const char* ppReturn = nullptr;
|
||||
firstView->onMsg("GetCamera", &ppReturn);
|
||||
saveCameraSettings(ppReturn);
|
||||
const std::string& camera = firstView->getCamera();
|
||||
saveCameraSettings(camera.c_str());
|
||||
}
|
||||
|
||||
auto view3D = new View3DInventor(this, getMainWindow(), shareWidget);
|
||||
@@ -2265,8 +2294,7 @@ MDIView* Document::createView(const Base::Type& typeId, CreateViewMode mode)
|
||||
view3D->resize(400, 300);
|
||||
|
||||
if (!cameraSettings.empty()) {
|
||||
const char* ppReturn = nullptr;
|
||||
view3D->onMsg(cameraSettings.c_str(), &ppReturn);
|
||||
view3D->setCamera(cameraSettings.c_str());
|
||||
}
|
||||
|
||||
// When cloning the view, don't add the view to the main window. The whole purpose of the
|
||||
@@ -2284,7 +2312,7 @@ MDIView* Document::createView(const Base::Type& typeId, CreateViewMode mode)
|
||||
|
||||
const char* Document::getCameraSettings() const
|
||||
{
|
||||
return cameraSettings.size() > 10 ? cameraSettings.c_str() + 10 : cameraSettings.c_str();
|
||||
return cameraSettings.c_str();
|
||||
}
|
||||
|
||||
bool Document::saveCameraSettings(const char* settings) const
|
||||
@@ -2314,7 +2342,7 @@ bool Document::saveCameraSettings(const char* settings) const
|
||||
return false;
|
||||
}
|
||||
|
||||
cameraSettings = std::string("SetCamera ") + settings;
|
||||
cameraSettings = settings;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2482,8 +2510,8 @@ bool Document::canClose(bool checkModify, bool checkLink)
|
||||
// If a task dialog is open that doesn't allow other commands to modify
|
||||
// the document it must be closed by resetting the edit mode of the
|
||||
// corresponding view provider.
|
||||
if (!Gui::Control().isAllowedAlterDocument()) {
|
||||
std::string name = Gui::Control().activeDialog()->getDocumentName();
|
||||
if (!Gui::Control().isAllowedAlterDocument(getDocument())) {
|
||||
std::string name = Gui::Control().activeDialog(getDocument())->getDocumentName();
|
||||
if (name == this->getDocument()->getName()) {
|
||||
// getInEdit() only checks if the currently active MDI view is
|
||||
// a 3D view and that it is in edit mode. However, when closing a
|
||||
@@ -2531,16 +2559,15 @@ std::list<MDIView*> Document::getMDIViewsOfType(const Base::Type& typeId) const
|
||||
bool Document::sendMsgToViews(const char* pMsg)
|
||||
{
|
||||
std::list<Gui::BaseView*>::iterator it;
|
||||
const char** pReturnIgnore = nullptr;
|
||||
|
||||
for (it = d->baseViews.begin(); it != d->baseViews.end(); ++it) {
|
||||
if ((*it)->onMsg(pMsg, pReturnIgnore)) {
|
||||
if ((*it)->onMsg(pMsg)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (it = d->passiveViews.begin(); it != d->passiveViews.end(); ++it) {
|
||||
if ((*it)->onMsg(pMsg, pReturnIgnore)) {
|
||||
if ((*it)->onMsg(pMsg)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2548,12 +2575,12 @@ bool Document::sendMsgToViews(const char* pMsg)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Document::sendMsgToFirstView(const Base::Type& typeId, const char* pMsg, const char** ppReturn)
|
||||
bool Document::sendMsgToFirstView(const Base::Type& typeId, const char* pMsg)
|
||||
{
|
||||
// first try the active view
|
||||
Gui::MDIView* view = getActiveView();
|
||||
if (view && view->isDerivedFrom(typeId)) {
|
||||
if (view->onMsg(pMsg, ppReturn)) {
|
||||
if (view->onMsg(pMsg)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2561,7 +2588,7 @@ bool Document::sendMsgToFirstView(const Base::Type& typeId, const char* pMsg, co
|
||||
// now try the other views
|
||||
std::list<Gui::MDIView*> views = getMDIViewsOfType(typeId);
|
||||
for (const auto& it : views) {
|
||||
if ((it != view) && it->onMsg(pMsg, ppReturn)) {
|
||||
if ((it != view) && it->onMsg(pMsg)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2740,9 +2767,9 @@ Gui::MDIView* Document::getEditingViewOfViewProvider(Gui::ViewProvider* vp) cons
|
||||
* operation default is the command name.
|
||||
* @see CommitCommand(),AbortCommand()
|
||||
*/
|
||||
void Document::openCommand(const char* sName)
|
||||
int Document::openCommand(const char* sName)
|
||||
{
|
||||
getDocument()->openTransaction(sName);
|
||||
return getDocument()->openTransaction(App::TransactionName {.name = sName, .temporary = false});
|
||||
}
|
||||
|
||||
void Document::commitCommand()
|
||||
|
||||
+13
-2
@@ -191,12 +191,21 @@ public:
|
||||
void setModified(bool);
|
||||
bool isModified() const;
|
||||
|
||||
/// getter-setter for workbench name
|
||||
void setWorkbench(const std::string& name);
|
||||
std::string workbench() const;
|
||||
|
||||
/// Returns true if the document is about to be closed, false otherwise
|
||||
bool isAboutToClose() const;
|
||||
|
||||
/// Getter for the App Document
|
||||
App::Document* getDocument() const;
|
||||
|
||||
/// Notify the document when it becomes
|
||||
/// the active document/stops being the active document
|
||||
void setIsActive(bool active);
|
||||
bool isActive() const;
|
||||
|
||||
/** @name methods for View handling */
|
||||
//@{
|
||||
/// Getter for the active view
|
||||
@@ -218,7 +227,7 @@ public:
|
||||
* first checked view is the current active view.
|
||||
* If a view supports the message true is returned and false otherwise.
|
||||
*/
|
||||
bool sendMsgToFirstView(const Base::Type& typeId, const char* pMsg, const char** ppReturn);
|
||||
bool sendMsgToFirstView(const Base::Type& typeId, const char* pMsg);
|
||||
/// Attach a view (get called by the MDIView constructor)
|
||||
void attachView(Gui::BaseView* pcView, bool bPassiv = false);
|
||||
/// Detach a view (get called by the MDIView destructor)
|
||||
@@ -287,6 +296,8 @@ public:
|
||||
int* mode = nullptr,
|
||||
std::string* subElement = nullptr
|
||||
) const;
|
||||
ViewProvider* getEditViewProvider() const; // Returns the _editViewProvider even if it is not
|
||||
// in edit at the moment
|
||||
/// set the in edit ViewProvider subname reference
|
||||
void setInEdit(ViewProviderDocumentObject* parentVp, const char* subname);
|
||||
/** Add or remove view provider from scene graphs of all views
|
||||
@@ -300,7 +311,7 @@ public:
|
||||
/** @name methods for the UNDO REDO handling */
|
||||
//@{
|
||||
/// Open a new Undo transaction on the document
|
||||
void openCommand(const char* sName = nullptr);
|
||||
int openCommand(const char* sName = nullptr);
|
||||
/// Commit the Undo transaction on the document
|
||||
void commitCommand();
|
||||
/// Abort the Undo transaction on the document
|
||||
|
||||
@@ -187,6 +187,31 @@ class Document(Persistence):
|
||||
obj : Gui.ViewProvider
|
||||
"""
|
||||
...
|
||||
|
||||
def openCommand(self, name: str) -> int:
|
||||
"""
|
||||
openCommand(name) -> int
|
||||
|
||||
Opens a named transaction for the document and returns it's
|
||||
id or 0 on failure
|
||||
"""
|
||||
...
|
||||
|
||||
def commitCommand(self) -> None:
|
||||
"""
|
||||
commitCommand() -> None
|
||||
|
||||
Commits the current transaction of the document
|
||||
"""
|
||||
...
|
||||
|
||||
def abortCommand(self) -> None:
|
||||
"""
|
||||
abortCommand() -> None
|
||||
|
||||
Aborts the current transaction of the document
|
||||
"""
|
||||
...
|
||||
ActiveObject: Any = ...
|
||||
"""The active object of the document."""
|
||||
|
||||
|
||||
@@ -577,6 +577,34 @@ Py::Long DocumentPy::getEditMode() const
|
||||
return Py::Long(mode);
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::openCommand(PyObject* arg, PyObject* kwd)
|
||||
{
|
||||
const char* name = nullptr;
|
||||
if (!PyArg_ParseTuple(arg, "s", &name)) {
|
||||
throw Py::Exception();
|
||||
}
|
||||
int tid = getDocumentPtr()->openCommand(name);
|
||||
|
||||
return Py::new_reference_to(Py::Long(tid));
|
||||
}
|
||||
PyObject* DocumentPy::commitCommand(PyObject* args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
return nullptr;
|
||||
}
|
||||
getDocumentPtr()->commitCommand();
|
||||
|
||||
Py_Return;
|
||||
}
|
||||
PyObject* DocumentPy::abortCommand(PyObject* args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
return nullptr;
|
||||
}
|
||||
getDocumentPtr()->abortCommand();
|
||||
|
||||
Py_Return;
|
||||
}
|
||||
Py::Boolean DocumentPy::getTransacting() const
|
||||
{
|
||||
return {getDocumentPtr()->isPerformingTransaction()};
|
||||
|
||||
@@ -29,9 +29,11 @@
|
||||
#include <Inventor/nodes/SoSwitch.h>
|
||||
|
||||
#include <QEvent>
|
||||
#include <QFontMetrics>
|
||||
#include <QKeyEvent>
|
||||
#include <QPixmap>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QHBoxLayout>
|
||||
#include <QString>
|
||||
|
||||
@@ -41,6 +43,8 @@
|
||||
#include <Gui/View3DInventorViewer.h>
|
||||
|
||||
#include "EditableDatumLabel.h"
|
||||
#include "Base/Console.h"
|
||||
#include "Gui/QuantitySpinBox.h"
|
||||
|
||||
|
||||
using namespace Gui;
|
||||
@@ -175,6 +179,8 @@ void EditableDatumLabel::startEdit(double val, QObject* eventFilteringObj, bool
|
||||
spinBox->setAutoNormalize(false);
|
||||
spinBox->setKeyboardTracking(true);
|
||||
spinBox->installEventFilter(this);
|
||||
spinBox->setAutoAdjustWidth(true);
|
||||
spinBox->setMaxExpectedDigits(16);
|
||||
|
||||
if (eventFilteringObj) {
|
||||
spinBox->installEventFilter(eventFilteringObj);
|
||||
@@ -185,10 +191,7 @@ void EditableDatumLabel::startEdit(double val, QObject* eventFilteringObj, bool
|
||||
}
|
||||
|
||||
spinBox->show();
|
||||
setSpinboxValue(val);
|
||||
// Note: adjustSize apparently uses the Min/Max values to set the size. So if we don't set them
|
||||
// to INT_MAX, the spinbox are much too big.
|
||||
spinBox->adjustSize();
|
||||
updateGeometry();
|
||||
setFocusToSpinbox();
|
||||
|
||||
const auto validateAndFinish = [this]() {
|
||||
@@ -217,6 +220,9 @@ void EditableDatumLabel::startEdit(double val, QObject* eventFilteringObj, bool
|
||||
};
|
||||
|
||||
connect(spinBox, qOverload<double>(&QuantitySpinBox::valueChanged), this, validateAndFinish);
|
||||
if (auto* edit = spinBox->findChild<QLineEdit*>()) {
|
||||
connect(edit, &QLineEdit::textChanged, this, &EditableDatumLabel::updateGeometry);
|
||||
}
|
||||
}
|
||||
|
||||
bool EditableDatumLabel::eventFilter(QObject* watched, QEvent* event)
|
||||
@@ -364,8 +370,9 @@ void EditableDatumLabel::positionSpinbox()
|
||||
|
||||
// Update lock icon position inside the spinbox if it exists and is visible
|
||||
if (lockIconLabel && lockIconLabel->isVisible()) {
|
||||
int iconSize = 14;
|
||||
int padding = 4;
|
||||
const QFontMetrics fm(spinBox->fontMetrics());
|
||||
int iconSize = fm.height();
|
||||
int padding = spinBox->getMargin();
|
||||
QSize spinboxSize = spinBox->size();
|
||||
lockIconLabel->setGeometry(
|
||||
spinboxSize.width() - iconSize - padding,
|
||||
@@ -424,6 +431,14 @@ void EditableDatumLabel::setPlacement(const Base::Placement& plc)
|
||||
label->norm.setValue(SbVec3f(float(RN.x), float(RN.y), float(RN.z)));
|
||||
}
|
||||
|
||||
void EditableDatumLabel::updateGeometry()
|
||||
{
|
||||
if (!spinBox) {
|
||||
return;
|
||||
}
|
||||
spinBox->adjustSize();
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void EditableDatumLabel::setColor(SbColor color)
|
||||
{
|
||||
@@ -510,56 +525,47 @@ void EditableDatumLabel::setSpinboxVisibleToMouse(bool val)
|
||||
|
||||
void EditableDatumLabel::setLockedAppearance(bool locked)
|
||||
{
|
||||
if (!locked) {
|
||||
this->hasFinishedEditing = false;
|
||||
}
|
||||
if (!spinBox) {
|
||||
return;
|
||||
}
|
||||
const QFontMetrics fm(spinBox->fontMetrics());
|
||||
int iconSize = fm.height();
|
||||
int padding = spinBox->getMargin();
|
||||
if (locked) {
|
||||
if (spinBox) {
|
||||
spinBox->addIconSpace(true);
|
||||
spinBox->adjustSize();
|
||||
// create lock icon label it it doesn't exist, if it does - show it
|
||||
if (!lockIconLabel) {
|
||||
lockIconLabel = new QLabel(spinBox);
|
||||
lockIconLabel->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
lockIconLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
|
||||
// create lock icon label it it doesn't exist, if it does - show it
|
||||
if (!lockIconLabel) {
|
||||
lockIconLabel = new QLabel(spinBox);
|
||||
lockIconLabel->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
lockIconLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
// load icon and scale it to fit in spinbox
|
||||
QPixmap lockIcon = Gui::BitmapFactory().pixmap("Constraint_Lock");
|
||||
QPixmap scaledIcon
|
||||
= lockIcon.scaled(iconSize, iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
lockIconLabel->setPixmap(scaledIcon);
|
||||
|
||||
// load icon and scale it to fit in spinbox
|
||||
QPixmap lockIcon = Gui::BitmapFactory().pixmap("Constraint_Lock");
|
||||
QPixmap scaledIcon
|
||||
= lockIcon.scaled(14, 14, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
lockIconLabel->setPixmap(scaledIcon);
|
||||
|
||||
// position lock icon inside the spinbox
|
||||
int iconSize = 14;
|
||||
int padding = 4;
|
||||
QSize spinboxSize = spinBox->size();
|
||||
lockIconLabel->setGeometry(
|
||||
spinboxSize.width() - iconSize - padding,
|
||||
(spinboxSize.height() - iconSize) / 2,
|
||||
iconSize,
|
||||
iconSize
|
||||
);
|
||||
// style spinbox and add padding for lock
|
||||
QString styleSheet = QString::fromLatin1(
|
||||
"QSpinBox { "
|
||||
"padding-right: %1px; "
|
||||
"}"
|
||||
)
|
||||
.arg(iconSize + padding + 2);
|
||||
|
||||
spinBox->setStyleSheet(styleSheet);
|
||||
}
|
||||
|
||||
lockIconLabel->show();
|
||||
// position lock icon inside the spinbox
|
||||
QSize spinboxSize = spinBox->size();
|
||||
lockIconLabel->setGeometry(
|
||||
spinboxSize.width() - iconSize - padding,
|
||||
(spinboxSize.height() - iconSize) / 2,
|
||||
iconSize,
|
||||
iconSize
|
||||
);
|
||||
}
|
||||
lockIconLabel->show();
|
||||
}
|
||||
else {
|
||||
this->hasFinishedEditing = false;
|
||||
|
||||
// if spinbox exists, reset its appearance
|
||||
if (spinBox) {
|
||||
spinBox->setStyleSheet(QString());
|
||||
|
||||
// hide lock icon if it exists for later reuse
|
||||
if (lockIconLabel) {
|
||||
lockIconLabel->hide();
|
||||
}
|
||||
// hide lock icon if it exists for later reuse
|
||||
if (lockIconLabel) {
|
||||
lockIconLabel->hide();
|
||||
spinBox->addIconSpace(false);
|
||||
spinBox->adjustSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ public:
|
||||
void setLockedAppearance(bool locked); ///< Sets visual appearance to indicate locked state
|
||||
///< (finished editing)
|
||||
void resetLockedState(); ///< Resets both hasFinishedEditing flag and locked appearance
|
||||
void updateGeometry();
|
||||
|
||||
Function getFunction();
|
||||
|
||||
|
||||
@@ -233,7 +233,7 @@ void EditorView::checkTimestamp()
|
||||
/**
|
||||
* Runs the action specified by \a pMsg.
|
||||
*/
|
||||
bool EditorView::onMsg(const char* pMsg, const char** /*ppReturn*/)
|
||||
bool EditorView::onMsg(const char* pMsg)
|
||||
{
|
||||
// don't allow any actions if the editor is being closed
|
||||
if (d->aboutToClose) {
|
||||
@@ -663,7 +663,7 @@ PythonEditorView::~PythonEditorView()
|
||||
/**
|
||||
* Runs the action specified by \a pMsg.
|
||||
*/
|
||||
bool PythonEditorView::onMsg(const char* pMsg, const char** ppReturn)
|
||||
bool PythonEditorView::onMsg(const char* pMsg)
|
||||
{
|
||||
if (strcmp(pMsg, "Run") == 0) {
|
||||
executeScript();
|
||||
@@ -677,7 +677,7 @@ bool PythonEditorView::onMsg(const char* pMsg, const char** ppReturn)
|
||||
toggleBreakpoint();
|
||||
return true;
|
||||
}
|
||||
return EditorView::onMsg(pMsg, ppReturn);
|
||||
return EditorView::onMsg(pMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -705,7 +705,7 @@ void PythonEditorView::executeScript()
|
||||
{
|
||||
// always save the macro when it is modified
|
||||
if (EditorView::onHasMsg("Save")) {
|
||||
EditorView::onMsg("Save", nullptr);
|
||||
EditorView::onMsg("Save");
|
||||
}
|
||||
try {
|
||||
getMainWindow()->setCursor(Qt::WaitCursor);
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
void onUpdate() override
|
||||
{}
|
||||
|
||||
bool onMsg(const char* pMsg, const char** ppReturn) override;
|
||||
bool onMsg(const char* pMsg) override;
|
||||
bool onHasMsg(const char* pMsg) const override;
|
||||
|
||||
bool canClose() override;
|
||||
@@ -135,7 +135,7 @@ public:
|
||||
PythonEditorView(PythonEditor* editor, QWidget* parent);
|
||||
~PythonEditorView() override;
|
||||
|
||||
bool onMsg(const char* pMsg, const char** ppReturn) override;
|
||||
bool onMsg(const char* pMsg) override;
|
||||
bool onHasMsg(const char* pMsg) const override;
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
@@ -75,11 +75,11 @@ void Gui::ExpressionBinding::setExpression(std::shared_ptr<Expression> expr)
|
||||
|
||||
lastExpression = getExpression();
|
||||
|
||||
bool transaction = !App::GetApplication().getActiveTransaction();
|
||||
bool transaction = docObj->getDocument()->getBookedTransactionID() == 0;
|
||||
if (transaction) {
|
||||
std::ostringstream ss;
|
||||
ss << (expr ? "Set" : "Discard") << " expression " << docObj->Label.getValue();
|
||||
App::GetApplication().setActiveTransaction(ss.str().c_str());
|
||||
docObj->getDocument()->openTransaction(ss.str().c_str());
|
||||
}
|
||||
|
||||
docObj->ExpressionEngine.setValue(path, expr);
|
||||
@@ -89,7 +89,7 @@ void Gui::ExpressionBinding::setExpression(std::shared_ptr<Expression> expr)
|
||||
}
|
||||
|
||||
if (transaction) {
|
||||
App::GetApplication().closeActiveTransaction();
|
||||
docObj->getDocument()->commitTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ void ExpressionBinding::bind(const App::ObjectIdentifier& _path)
|
||||
{
|
||||
const Property* prop = _path.getProperty();
|
||||
|
||||
Q_ASSERT(prop != nullptr);
|
||||
Q_ASSERT(prop);
|
||||
|
||||
path = prop->canonicalPath(_path);
|
||||
|
||||
@@ -133,7 +133,7 @@ std::shared_ptr<App::Expression> ExpressionBinding::getExpression() const
|
||||
{
|
||||
DocumentObject* docObj = path.getDocumentObject();
|
||||
|
||||
Q_ASSERT(isBound() && docObj != nullptr);
|
||||
Q_ASSERT(isBound() && docObj);
|
||||
|
||||
return docObj->getExpression(path).expression;
|
||||
}
|
||||
@@ -214,11 +214,11 @@ bool ExpressionBinding::apply(const std::string& propName)
|
||||
throw Base::RuntimeError("Document object not found.");
|
||||
}
|
||||
|
||||
bool transaction = !App::GetApplication().getActiveTransaction();
|
||||
bool transaction = docObj->getDocument()->getBookedTransactionID() == 0;
|
||||
if (transaction) {
|
||||
std::ostringstream ss;
|
||||
ss << "Set expression " << docObj->Label.getValue();
|
||||
App::GetApplication().setActiveTransaction(ss.str().c_str());
|
||||
docObj->getDocument()->openTransaction(ss.str().c_str());
|
||||
}
|
||||
Gui::Command::doCommand(
|
||||
Gui::Command::Doc,
|
||||
@@ -229,7 +229,7 @@ bool ExpressionBinding::apply(const std::string& propName)
|
||||
getEscapedExpressionString().c_str()
|
||||
);
|
||||
if (transaction) {
|
||||
App::GetApplication().closeActiveTransaction();
|
||||
docObj->getDocument()->commitTransaction();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -242,11 +242,11 @@ bool ExpressionBinding::apply(const std::string& propName)
|
||||
}
|
||||
|
||||
if (lastExpression) {
|
||||
bool transaction = !App::GetApplication().getActiveTransaction();
|
||||
bool transaction = docObj->getDocument()->getBookedTransactionID() == 0;
|
||||
if (transaction) {
|
||||
std::ostringstream ss;
|
||||
ss << "Discard expression " << docObj->Label.getValue();
|
||||
App::GetApplication().setActiveTransaction(ss.str().c_str());
|
||||
docObj->getDocument()->openTransaction(ss.str().c_str());
|
||||
}
|
||||
Gui::Command::doCommand(
|
||||
Gui::Command::Doc,
|
||||
@@ -256,7 +256,7 @@ bool ExpressionBinding::apply(const std::string& propName)
|
||||
path.toEscapedString().c_str()
|
||||
);
|
||||
if (transaction) {
|
||||
App::GetApplication().closeActiveTransaction();
|
||||
docObj->getDocument()->commitTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -471,7 +471,7 @@ QByteArray GraphvizView::exportGraph(const QString& format)
|
||||
return dotProc.readAll();
|
||||
}
|
||||
|
||||
bool GraphvizView::onMsg(const char* pMsg, const char**)
|
||||
bool GraphvizView::onMsg(const char* pMsg)
|
||||
{
|
||||
if (strcmp("Save", pMsg) == 0 || strcmp("SaveAs", pMsg) == 0) {
|
||||
QList<QPair<QString, QString>> formatMap;
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
QByteArray exportGraph(const QString& filter);
|
||||
|
||||
/// Message handler
|
||||
bool onMsg(const char* pMsg, const char** ppReturn) override;
|
||||
bool onMsg(const char* pMsg) override;
|
||||
/// Message handler test
|
||||
bool onHasMsg(const char* pMsg) const override;
|
||||
/** @name Printing */
|
||||
|
||||
@@ -311,9 +311,8 @@ void ImageView::print(QPrinter* printer)
|
||||
painter.drawPixmap(0, 0, pixmap);
|
||||
}
|
||||
|
||||
bool ImageView::onMsg(const char* pMsg, const char** ppReturn)
|
||||
bool ImageView::onMsg(const char* pMsg)
|
||||
{
|
||||
Q_UNUSED(ppReturn)
|
||||
if (strcmp("ViewFit", pMsg) == 0) {
|
||||
fitToWindow(true);
|
||||
return true;
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ public:
|
||||
}
|
||||
|
||||
/// Message handler
|
||||
bool onMsg(const char* pMsg, const char** ppReturn) override;
|
||||
bool onMsg(const char* pMsg) override;
|
||||
/// Message handler test
|
||||
bool onHasMsg(const char* pMsg) const override;
|
||||
|
||||
|
||||
@@ -315,7 +315,7 @@
|
||||
<location filename="../Dialogs/DlgCustomizeSpNavSettings.ui" line="318"/>
|
||||
<location filename="../Dialogs/DlgCustomizeSpNavSettings.ui" line="342"/>
|
||||
<source>Reverse</source>
|
||||
<translation>Omvendt</translation>
|
||||
<translation>Modsat</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -9279,7 +9279,7 @@ i den aktuelle kopi vil gå tabt.
|
||||
<message>
|
||||
<location filename="../CommandDoc.cpp" line="434"/>
|
||||
<source>No selection</source>
|
||||
<translation>Ingen valg</translation>
|
||||
<translation>Ingen valgt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../CommandDoc.cpp" line="435"/>
|
||||
|
||||
@@ -6387,7 +6387,7 @@ sur Alt + bouton droit de la souris OU les touches Pg.Suiv/Pg.Préc du clavier</
|
||||
<message>
|
||||
<location filename="../Widgets.cpp" line="409"/>
|
||||
<source>Press modifier keys</source>
|
||||
<translation>Appuyez sur les touches de modification</translation>
|
||||
<translation>Appuyer sur les touches de modificateur</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
+6343
-5873
File diff suppressed because it is too large
Load Diff
@@ -12161,7 +12161,7 @@ the region are non-opaque.</source>
|
||||
<message>
|
||||
<location filename="../PreferencePages/DlgSettingsUI.cpp" line="124"/>
|
||||
<source>No style sheet</source>
|
||||
<translation type="unfinished">No style sheet</translation>
|
||||
<translation>Немає таблиці стилів</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -14649,7 +14649,7 @@ This makes the docked panel stay transparent at all times.</translation>
|
||||
<message>
|
||||
<location filename="../Dialogs/DlgVersionMigrator.cpp" line="140"/>
|
||||
<source>Welcome to %1 %2.%3</source>
|
||||
<translation type="unfinished">Welcome to %1 %2.%3</translation>
|
||||
<translation>Ласкаво просимо до %1 %2.%3</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Dialogs/DlgVersionMigrator.cpp" line="143"/>
|
||||
|
||||
+1
-2
@@ -202,10 +202,9 @@ void MDIView::viewAll()
|
||||
{}
|
||||
|
||||
/// receive a message
|
||||
bool MDIView::onMsg(const char* pMsg, const char** ppReturn)
|
||||
bool MDIView::onMsg(const char* pMsg)
|
||||
{
|
||||
Q_UNUSED(pMsg);
|
||||
Q_UNUSED(ppReturn);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ public:
|
||||
void setWindowTitle(const QString& title);
|
||||
|
||||
/// Message handler
|
||||
bool onMsg(const char* pMsg, const char** ppReturn) override;
|
||||
bool onMsg(const char* pMsg) override;
|
||||
/// Message handler test
|
||||
bool onHasMsg(const char* pMsg) const override;
|
||||
/// overwrite when checking on close state
|
||||
|
||||
@@ -179,7 +179,6 @@ Py::Object MDIViewPy::redoActions(const Py::Tuple& args)
|
||||
|
||||
Py::Object MDIViewPy::sendMessage(const Py::Tuple& args)
|
||||
{
|
||||
const char** ppReturn = nullptr;
|
||||
char* psMsgStr;
|
||||
if (!PyArg_ParseTuple(args.ptr(), "s;Message string needed (string)", &psMsgStr)) {
|
||||
throw Py::Exception();
|
||||
@@ -188,7 +187,7 @@ Py::Object MDIViewPy::sendMessage(const Py::Tuple& args)
|
||||
try {
|
||||
bool ok = false;
|
||||
if (_view) {
|
||||
ok = _view->onMsg(psMsgStr, ppReturn);
|
||||
ok = _view->onMsg(psMsgStr);
|
||||
}
|
||||
return Py::Boolean(ok);
|
||||
}
|
||||
|
||||
@@ -212,16 +212,16 @@ PyObject* MDIViewPyWrap::getPyObject()
|
||||
return MDIView::getPyObject();
|
||||
}
|
||||
|
||||
bool MDIViewPyWrap::onMsg(const char* pMsg, const char** ppReturn)
|
||||
bool MDIViewPyWrap::onMsg(const char* pMsg)
|
||||
{
|
||||
try {
|
||||
if (ptr->onMsg(pMsg)) {
|
||||
return true;
|
||||
}
|
||||
return MDIView::onMsg(pMsg, ppReturn);
|
||||
return MDIView::onMsg(pMsg);
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
return MDIView::onMsg(pMsg, ppReturn);
|
||||
return MDIView::onMsg(pMsg);
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyGILStateLocker lock;
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
~MDIViewPyWrap() override;
|
||||
|
||||
/// Message handler
|
||||
bool onMsg(const char* pMsg, const char** ppReturn) override;
|
||||
bool onMsg(const char* pMsg) override;
|
||||
/// Message handler test
|
||||
bool onHasMsg(const char* pMsg) const override;
|
||||
/// overwrite when checking on close state
|
||||
|
||||
@@ -251,7 +251,7 @@ public:
|
||||
void setUserSchema(int userSchema)
|
||||
{
|
||||
App::Document* doc = App::GetApplication().getActiveDocument();
|
||||
if (doc != nullptr) {
|
||||
if (doc) {
|
||||
if (doc->UnitSystem.getValue() != userSchema) {
|
||||
doc->UnitSystem.setValue(userSchema);
|
||||
}
|
||||
@@ -275,7 +275,7 @@ private:
|
||||
bool ignore = hGrpu->GetBool("IgnoreProjectSchema", false);
|
||||
App::Document* doc = App::GetApplication().getActiveDocument();
|
||||
int userSchema = getWindowParameter()->GetInt("UserSchema", 0);
|
||||
if (doc != nullptr && !ignore) {
|
||||
if (doc && !ignore) {
|
||||
userSchema = doc->UnitSystem.getValue();
|
||||
}
|
||||
auto actions = menu()->actions();
|
||||
@@ -937,7 +937,7 @@ int MainWindow::confirmSave(App::Document* doc, QWidget* parent, bool addCheckbo
|
||||
const QList<QWidget*> listOfMDIs = this->windows();
|
||||
for (QWidget* widget : listOfMDIs) {
|
||||
auto mdiView = qobject_cast<MDIView*>(widget);
|
||||
if (mdiView != nullptr && mdiView->getAppDocument() == doc) {
|
||||
if (mdiView && mdiView->getAppDocument() == doc) {
|
||||
this->setActiveWindow(mdiView);
|
||||
}
|
||||
}
|
||||
@@ -974,7 +974,7 @@ bool MainWindow::closeAllDocuments(bool close)
|
||||
// moves the active document to the front
|
||||
MDIView* activeView = this->activeWindow();
|
||||
App::Document* activeDoc = (activeView ? activeView->getAppDocument() : nullptr);
|
||||
if (activeDoc != nullptr) {
|
||||
if (activeDoc) {
|
||||
for (auto it = ++docs.begin(); it != docs.end(); it++) {
|
||||
if (*it == activeDoc) {
|
||||
docs.erase(it);
|
||||
|
||||
@@ -1220,7 +1220,7 @@ bool NaviCubeImplementation::mouseReleased(short x, short y)
|
||||
// If the previous flat button animation is still active then apply the rotation to the
|
||||
// previous target orientation, otherwise apply the rotation to the current camera
|
||||
// orientation
|
||||
if (m_flatButtonAnimation != nullptr
|
||||
if (m_flatButtonAnimation
|
||||
&& m_flatButtonAnimation->state() == QAbstractAnimation::Running) {
|
||||
m_flatButtonTargetOrientation = rotation * m_flatButtonTargetOrientation;
|
||||
}
|
||||
|
||||
@@ -710,7 +710,7 @@ public:
|
||||
: my_base(ctx)
|
||||
{
|
||||
auto& ns = this->outermost_context().ns;
|
||||
ns.setRotationCenter(ns.getFocalPoint());
|
||||
ns.setRotationCenter(ns.viewer->getFocalPoint());
|
||||
ns.setViewingMode(NavigationStyle::DRAGGING);
|
||||
this->base_pos
|
||||
= static_cast<const NS::Event*>(this->triggering_event())->inventor_event->getPosition();
|
||||
|
||||
@@ -589,7 +589,7 @@ SbBool MayaGestureNavigationStyle::processSoEvent(const SoEvent* const ev)
|
||||
if (comboAfter & BUTTON1DOWN || comboAfter & BUTTON2DOWN) {
|
||||
// don't leave navigation till all buttons have been released
|
||||
if (comboAfter & BUTTON1DOWN && comboAfter & BUTTON2DOWN) {
|
||||
setRotationCenter(getFocalPoint());
|
||||
setRotationCenter(viewer->getFocalPoint());
|
||||
}
|
||||
else {
|
||||
saveCursorPosition(ev);
|
||||
|
||||
@@ -96,7 +96,7 @@ void NavigationAnimator::stop()
|
||||
*/
|
||||
bool NavigationAnimator::isAnimating() const
|
||||
{
|
||||
if (activeAnimation != nullptr) {
|
||||
if (activeAnimation) {
|
||||
return activeAnimation->state() == QAbstractAnimation::State::Running;
|
||||
}
|
||||
|
||||
|
||||
@@ -504,7 +504,7 @@ void NavigationStyle::lookAtPoint(const SbVec2s screenpos)
|
||||
void NavigationStyle::lookAtPoint(const SbVec3f& position)
|
||||
{
|
||||
this->rotationCenterFound = false;
|
||||
translateCamera(position - getFocalPoint());
|
||||
translateCamera(position - viewer->getFocalPoint());
|
||||
}
|
||||
|
||||
SoCamera* NavigationStyle::getCamera() const
|
||||
@@ -524,7 +524,7 @@ std::shared_ptr<NavigationAnimation> NavigationStyle::setCameraOrientation(
|
||||
|
||||
animator->stop();
|
||||
|
||||
const SbVec3f focalPoint = getFocalPoint();
|
||||
const SbVec3f focalPoint = viewer->getFocalPoint();
|
||||
SbVec3f translation(0, 0, 0);
|
||||
|
||||
if (moveToCenter) {
|
||||
@@ -618,6 +618,7 @@ void NavigationStyle::boxZoom(const SbBox2s& box)
|
||||
|
||||
doScale(cam, scaleFactor);
|
||||
}
|
||||
|
||||
void NavigationStyle::scale(float factor)
|
||||
{
|
||||
SoCamera* cam = viewer->getSoRenderManager()->getCamera();
|
||||
@@ -626,9 +627,7 @@ void NavigationStyle::scale(float factor)
|
||||
}
|
||||
|
||||
// Find the current center of the screen
|
||||
SbVec3f direction;
|
||||
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
|
||||
SbVec3f initCenter = cam->position.getValue() + cam->focalDistance.getValue() * direction;
|
||||
SbVec3f initCenter = viewer->getFocalPoint();
|
||||
|
||||
// Move the camera to the origin for scaling
|
||||
cam->position = cam->position.getValue() - initCenter;
|
||||
@@ -702,7 +701,7 @@ void NavigationStyle::findBoundingSphere()
|
||||
*/
|
||||
void NavigationStyle::reorientCamera(SoCamera* camera, const SbRotation& rotation)
|
||||
{
|
||||
reorientCamera(camera, rotation, getFocalPoint());
|
||||
reorientCamera(camera, rotation, viewer->getFocalPoint());
|
||||
}
|
||||
|
||||
/** Rotate the camera by the given amount, then reposition it so the rotation center stays in the
|
||||
@@ -751,7 +750,7 @@ void NavigationStyle::reorientCamera(
|
||||
#else
|
||||
constexpr float orthographicFocalDistance = 1;
|
||||
#endif
|
||||
camera->position = getFocalPoint() - orthographicFocalDistance * direction;
|
||||
camera->position = viewer->getFocalPoint() - orthographicFocalDistance * direction;
|
||||
camera->focalDistance = orthographicFocalDistance;
|
||||
}
|
||||
|
||||
@@ -1004,7 +1003,7 @@ void NavigationStyle::doZoom(SoCamera* camera, float logfactor, const SbVec2f& p
|
||||
// Change the position of the rotation center indicator after zooming at cursor
|
||||
// Rotation mode is WindowCenter
|
||||
if (!rotationCenterMode) {
|
||||
viewer->changeRotationCenterPosition(getFocalPoint());
|
||||
viewer->changeRotationCenterPosition(viewer->getFocalPoint());
|
||||
|
||||
#if (COIN_MAJOR_VERSION * 100 + COIN_MINOR_VERSION * 10 + COIN_MICRO_VERSION < 403)
|
||||
findBoundingSphere();
|
||||
@@ -1083,20 +1082,6 @@ void NavigationStyle::setRotationCenter(const SbVec3f& cnt)
|
||||
}
|
||||
}
|
||||
|
||||
SbVec3f NavigationStyle::getFocalPoint() const
|
||||
{
|
||||
SoCamera* cam = viewer->getSoRenderManager()->getCamera();
|
||||
if (!cam) {
|
||||
return {0, 0, 0};
|
||||
}
|
||||
|
||||
// Find global coordinates of focal point.
|
||||
SbVec3f direction;
|
||||
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
|
||||
SbVec3f focal = cam->position.getValue() + cam->focalDistance.getValue() * direction;
|
||||
return focal;
|
||||
}
|
||||
|
||||
/** Uses the sphere sheet projector to map the mouse position onto
|
||||
* a 3D point and find a rotation from this and the last calculated point.
|
||||
*/
|
||||
@@ -1296,7 +1281,7 @@ void NavigationStyle::saveCursorPosition(const SoEvent* const ev)
|
||||
|
||||
// mode is WindowCenter
|
||||
if (!this->rotationCenterMode) {
|
||||
setRotationCenter(getFocalPoint());
|
||||
setRotationCenter(viewer->getFocalPoint());
|
||||
}
|
||||
|
||||
// Option to get point on model (slow) or always on focal plane (fast)
|
||||
|
||||
@@ -166,7 +166,6 @@ public:
|
||||
void setRotationCenterMode(RotationCenterModes);
|
||||
RotationCenterModes getRotationCenterMode() const;
|
||||
void setRotationCenter(const SbVec3f& cnt);
|
||||
SbVec3f getFocalPoint() const;
|
||||
|
||||
SoCamera* getCamera() const;
|
||||
std::shared_ptr<NavigationAnimation> setCameraOrientation(
|
||||
|
||||
@@ -1244,16 +1244,16 @@ bool OverlayTabWidget::checkAutoHide() const
|
||||
}
|
||||
}
|
||||
|
||||
bool activeDocInEdit = Application::Instance->isInEdit(Application::Instance->activeDocument());
|
||||
if (autoMode == AutoMode::EditShow) {
|
||||
return !Application::Instance->editDocument()
|
||||
&& (!Control().taskPanel() || Control().taskPanel()->isEmpty(false));
|
||||
return !activeDocInEdit && (!Control().taskPanel() || Control().taskPanel()->isEmpty(false));
|
||||
}
|
||||
|
||||
if (autoMode == AutoMode::TaskShow) {
|
||||
return (!Control().taskPanel() || Control().taskPanel()->isEmpty());
|
||||
}
|
||||
|
||||
if (autoMode == AutoMode::EditHide && Application::Instance->editDocument()) {
|
||||
if (autoMode == AutoMode::EditHide && activeDocInEdit) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -351,7 +351,7 @@ void DlgSettingsLightSources::resetSettingsToDefaults()
|
||||
|
||||
void DlgSettingsLightSources::zoomIn() const
|
||||
{
|
||||
if (camera == nullptr) {
|
||||
if (!camera) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ void DlgSettingsLightSources::zoomIn() const
|
||||
|
||||
void DlgSettingsLightSources::zoomOut() const
|
||||
{
|
||||
if (camera == nullptr) {
|
||||
if (!camera) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -406,7 +406,7 @@ public:
|
||||
const auto PyW_uniqueName = QString::number(reinterpret_cast<quintptr>(pyobj));
|
||||
auto PyW_invalidator = findChild<QObject*>(PyW_uniqueName, Qt::FindDirectChildrenOnly);
|
||||
|
||||
if (PyW_invalidator == nullptr) {
|
||||
if (!PyW_invalidator) {
|
||||
PyW_invalidator = new QObject(this);
|
||||
PyW_invalidator->setObjectName(PyW_uniqueName);
|
||||
|
||||
@@ -420,7 +420,7 @@ public:
|
||||
auto destroyedFun = [pyobj]() {
|
||||
Base::PyGILStateLocker lock;
|
||||
|
||||
if (auto sbkPtr = reinterpret_cast<SbkObject*>(pyobj); sbkPtr != nullptr) {
|
||||
if (auto sbkPtr = reinterpret_cast<SbkObject*>(pyobj); sbkPtr) {
|
||||
Shiboken::Object::setValidCpp(sbkPtr, false);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -93,10 +93,5 @@ ActionGroup *ActionPanel::createGroup(const QPixmap &icon, const QString &title,
|
||||
return group;
|
||||
}
|
||||
|
||||
QSize ActionPanel::minimumSizeHint() const
|
||||
{
|
||||
return {200,150};
|
||||
}
|
||||
|
||||
} // namespace QSint
|
||||
|
||||
|
||||
@@ -89,12 +89,6 @@ public:
|
||||
*/
|
||||
void setScheme(ActionPanelScheme *scheme);
|
||||
|
||||
/**
|
||||
* @brief Returns the recommended minimum size for the panel.
|
||||
* @return The minimum size hint.
|
||||
*/
|
||||
QSize minimumSizeHint() const override;
|
||||
|
||||
protected:
|
||||
/** @brief The color scheme used by the panel. */
|
||||
ActionPanelScheme *myScheme;
|
||||
|
||||
+99
-26
@@ -66,6 +66,9 @@ public:
|
||||
, pendingEmit(false)
|
||||
, normalize(true)
|
||||
, checkRangeInExpression(false)
|
||||
, adjustableWidth(false)
|
||||
, maxExpectedDigits(4)
|
||||
, addIconSpace(false)
|
||||
, unitValue(0)
|
||||
, maximum(std::numeric_limits<double>::max())
|
||||
, minimum(-std::numeric_limits<double>::max())
|
||||
@@ -306,6 +309,9 @@ public:
|
||||
bool pendingEmit;
|
||||
bool normalize;
|
||||
bool checkRangeInExpression;
|
||||
bool adjustableWidth;
|
||||
int maxExpectedDigits;
|
||||
bool addIconSpace;
|
||||
QString validStr;
|
||||
Base::Quantity quantity;
|
||||
Base::Quantity cached;
|
||||
@@ -341,6 +347,9 @@ void QuantitySpinBox::bind(const App::ObjectIdentifier& _path)
|
||||
|
||||
void QuantitySpinBox::showIcon()
|
||||
{
|
||||
addIconSpace(true);
|
||||
|
||||
adjustSize();
|
||||
iconLabel->show();
|
||||
}
|
||||
|
||||
@@ -444,6 +453,19 @@ void QuantitySpinBox::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
QAbstractSpinBox::resizeEvent(event);
|
||||
resizeWidget();
|
||||
moveCursor();
|
||||
}
|
||||
|
||||
void QuantitySpinBox::moveCursor()
|
||||
{
|
||||
Q_D(QuantitySpinBox);
|
||||
QLineEdit* edit = lineEdit();
|
||||
int cursor = edit->cursorPosition();
|
||||
const int maxCursor = qMax(0, edit->displayText().size() - 1 /*space*/ - d->unitStr.size());
|
||||
|
||||
if (cursor > maxCursor) {
|
||||
edit->setCursorPosition(maxCursor);
|
||||
}
|
||||
}
|
||||
|
||||
void Gui::QuantitySpinBox::keyPressEvent(QKeyEvent* event)
|
||||
@@ -467,6 +489,9 @@ void Gui::QuantitySpinBox::keyPressEvent(QKeyEvent* event)
|
||||
if (isEnter) {
|
||||
returnPressed();
|
||||
}
|
||||
else {
|
||||
moveCursor();
|
||||
}
|
||||
}
|
||||
|
||||
void Gui::QuantitySpinBox::paintEvent(QPaintEvent*)
|
||||
@@ -504,7 +529,7 @@ void QuantitySpinBox::updateEdit(const QString& text)
|
||||
edit->setSelection(0, cursor);
|
||||
}
|
||||
else {
|
||||
edit->setCursorPosition(empty ? 0 : cursor);
|
||||
moveCursor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,6 +626,42 @@ void QuantitySpinBox::setAutoNormalize(bool normalize)
|
||||
d->normalize = normalize;
|
||||
}
|
||||
|
||||
bool QuantitySpinBox::autoAdjustWidth() const
|
||||
{
|
||||
Q_D(const QuantitySpinBox);
|
||||
return d->adjustableWidth;
|
||||
}
|
||||
|
||||
void QuantitySpinBox::setAutoAdjustWidth(bool adjust)
|
||||
{
|
||||
Q_D(QuantitySpinBox);
|
||||
d->adjustableWidth = adjust;
|
||||
}
|
||||
|
||||
bool QuantitySpinBox::isIconSpaceAdded() const
|
||||
{
|
||||
Q_D(const QuantitySpinBox);
|
||||
return d->addIconSpace;
|
||||
}
|
||||
|
||||
void QuantitySpinBox::addIconSpace(bool addIconSpace)
|
||||
{
|
||||
Q_D(QuantitySpinBox);
|
||||
d->addIconSpace = addIconSpace;
|
||||
}
|
||||
|
||||
int QuantitySpinBox::getMaxExpectedDigits()
|
||||
{
|
||||
Q_D(const QuantitySpinBox);
|
||||
return d->maxExpectedDigits;
|
||||
}
|
||||
|
||||
void QuantitySpinBox::setMaxExpectedDigits(int digits)
|
||||
{
|
||||
Q_D(QuantitySpinBox);
|
||||
d->maxExpectedDigits = digits;
|
||||
}
|
||||
|
||||
bool QuantitySpinBox::hasValidInput() const
|
||||
{
|
||||
Q_D(const QuantitySpinBox);
|
||||
@@ -611,7 +672,6 @@ bool QuantitySpinBox::hasValidInput() const
|
||||
void QuantitySpinBox::userInput(const QString& text)
|
||||
{
|
||||
Q_D(QuantitySpinBox);
|
||||
|
||||
d->pendingEmit = true;
|
||||
|
||||
QString tmp = text;
|
||||
@@ -911,44 +971,57 @@ QSize QuantitySpinBox::sizeForText(const QString& txt) const
|
||||
|
||||
QSize QuantitySpinBox::sizeHint() const
|
||||
{
|
||||
return sizeHintCalculator(lineEdit()->sizeHint().height());
|
||||
Q_D(const QuantitySpinBox);
|
||||
auto le = lineEdit();
|
||||
if (le && d->adjustableWidth) {
|
||||
// limit number of typed characters to keep unit visible
|
||||
le->setMaxLength(getMaxStrLength(d->maxExpectedDigits));
|
||||
}
|
||||
return sizeHintForDigits(d->maxExpectedDigits);
|
||||
}
|
||||
|
||||
QSize QuantitySpinBox::minimumSizeHint() const
|
||||
{
|
||||
return sizeHintCalculator(lineEdit()->minimumSizeHint().height());
|
||||
}
|
||||
|
||||
QSize QuantitySpinBox::sizeHintCalculator(int h) const
|
||||
QSize QuantitySpinBox::sizeHintForDigits(int digits) const
|
||||
{
|
||||
Q_D(const QuantitySpinBox);
|
||||
ensurePolished();
|
||||
|
||||
const int maxLen = getMaxStrLength(digits);
|
||||
int length = maxLen;
|
||||
|
||||
if (d->adjustableWidth) {
|
||||
int currenLen = qMax(lineEdit()->text().length(), 4);
|
||||
length = currenLen < maxLen ? currenLen : maxLen;
|
||||
}
|
||||
|
||||
QString longestString = QStringLiteral("8").repeated(length);
|
||||
|
||||
const QFontMetrics fm(fontMetrics());
|
||||
int w = 0;
|
||||
constexpr int maxStrLen = 9;
|
||||
|
||||
QString s;
|
||||
QString fixedContent = QLatin1String(" ");
|
||||
|
||||
Base::Quantity q(d->quantity);
|
||||
q.setValue(d->maximum);
|
||||
s = textFromValue(q);
|
||||
s.truncate(maxStrLen);
|
||||
s += fixedContent;
|
||||
w = qMax(w, QtTools::horizontalAdvance(fm, s));
|
||||
|
||||
w += 2; // cursor blinking space
|
||||
w += iconHeight;
|
||||
|
||||
int w = qMax(0, QtTools::horizontalAdvance(fm, longestString));
|
||||
w += 4; // cursor blinking space
|
||||
if (d->addIconSpace) {
|
||||
w += iconHeight;
|
||||
}
|
||||
QStyleOptionSpinBox opt;
|
||||
initStyleOption(&opt);
|
||||
QSize hint(w, h);
|
||||
QSize hint(w, lineEdit()->sizeHint().height());
|
||||
|
||||
QSize size = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this);
|
||||
return size;
|
||||
}
|
||||
|
||||
int QuantitySpinBox::getMaxStrLength(int digits) const
|
||||
{
|
||||
// Calculates the length of the longest string allowed
|
||||
Q_D(const QuantitySpinBox);
|
||||
ensurePolished();
|
||||
QString unit = QString::fromStdString(d->unit.getString());
|
||||
int decimals = App::GetApplication()
|
||||
.GetUserParameter()
|
||||
.GetGroup("BaseApp/Preferences/Units")
|
||||
->GetInt("Decimals", 2);
|
||||
return digits + 1 /*separator*/ + decimals + 1 /*space*/ + unit.length();
|
||||
}
|
||||
|
||||
void QuantitySpinBox::showEvent(QShowEvent* event)
|
||||
{
|
||||
Q_D(QuantitySpinBox);
|
||||
|
||||
@@ -54,6 +54,15 @@ class GuiExport QuantitySpinBox: public QAbstractSpinBox, public ExpressionSpinB
|
||||
Q_PROPERTY(
|
||||
bool autoNormalize READ autoNormalize WRITE setAutoNormalize
|
||||
) // clazy:exclude=qproperty-without-notify
|
||||
Q_PROPERTY(
|
||||
bool autoAdjustWidth READ autoAdjustWidth WRITE setAutoAdjustWidth
|
||||
) // clazy:exclude=qproperty-without-notify
|
||||
Q_PROPERTY(
|
||||
bool addIconSpace READ isIconSpaceAdded WRITE addIconSpace
|
||||
) // clazy:exclude=qproperty-without-notify
|
||||
Q_PROPERTY(
|
||||
int maxExpectedDigits READ getMaxExpectedDigits WRITE setMaxExpectedDigits
|
||||
) // clazy:exclude=qproperty-without-notify
|
||||
|
||||
public:
|
||||
explicit QuantitySpinBox(QWidget* parent = nullptr);
|
||||
@@ -99,6 +108,11 @@ public:
|
||||
/// Sets the value of the maximum property
|
||||
void setMaximum(double max);
|
||||
|
||||
/// Adjust how many digits in the integer part we should expect
|
||||
/// Affects minimum size if width is not adjustable
|
||||
void setMaxExpectedDigits(int digits);
|
||||
int getMaxExpectedDigits();
|
||||
|
||||
/// Gets the number of decimals
|
||||
int decimals() const;
|
||||
/// Sets the number of decimals
|
||||
@@ -109,6 +123,16 @@ public:
|
||||
/// Enables or disables automatic normalization on enter
|
||||
void setAutoNormalize(bool normalize);
|
||||
|
||||
/// Returns if automatic width adjustment is enabled for this input
|
||||
bool autoAdjustWidth() const;
|
||||
/// Enables or disables automatic width adjustement
|
||||
void setAutoAdjustWidth(bool adjust);
|
||||
|
||||
/// Returns if icon space is added for this input
|
||||
bool isIconSpaceAdded() const;
|
||||
/// Enables or disables icon space addition
|
||||
void addIconSpace(bool addIconSpace);
|
||||
|
||||
/// Sets a specific unit schema to handle quantities.
|
||||
/// The system-wide schema won't be used any more.
|
||||
void setSchema(int s);
|
||||
@@ -142,7 +166,6 @@ public:
|
||||
/// This is a helper function to determine the size this widget requires to fully display the text
|
||||
QSize sizeForText(const QString&) const;
|
||||
QSize sizeHint() const override;
|
||||
QSize minimumSizeHint() const override;
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
void setNumberExpression(App::NumberExpression*) override;
|
||||
@@ -175,13 +198,16 @@ protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
private:
|
||||
void moveCursor();
|
||||
void validateInput() override;
|
||||
void updateText(const Base::Quantity&);
|
||||
void updateEdit(const QString& text);
|
||||
void updateFromCache(bool notify, bool updateUnit = true);
|
||||
QString getUserString(const Base::Quantity& val, double& factor, QString& unitString) const;
|
||||
QString getUserString(const Base::Quantity& val) const;
|
||||
QSize sizeHintCalculator(int height) const;
|
||||
|
||||
QSize sizeHintForDigits(int digits) const;
|
||||
int getMaxStrLength(int digits) const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/** Gets emitted if the user has entered a VALID input
|
||||
|
||||
+477
-355
File diff suppressed because it is too large
Load Diff
@@ -225,7 +225,7 @@ class GuiExport SelectionObserver
|
||||
public:
|
||||
/** Constructor
|
||||
*
|
||||
* @param attach: whether to attach this observer on construction
|
||||
* @param attach: whether to attach this observer on construction
|
||||
* @param resolve: sub-object resolving mode.
|
||||
*/
|
||||
explicit SelectionObserver(bool attach = true, ResolveMode resolve = ResolveMode::OldStyleElement);
|
||||
@@ -264,6 +264,7 @@ private:
|
||||
std::string filterDocName;
|
||||
std::string filterObjName;
|
||||
ResolveMode resolve;
|
||||
const char* pDocumentScopeName {nullptr};
|
||||
bool blockedSelection;
|
||||
};
|
||||
|
||||
@@ -286,20 +287,6 @@ public:
|
||||
std::string notAllowedReason;
|
||||
};
|
||||
|
||||
/** SelectionGateFilterExternal
|
||||
* The selection gate disallows any external object
|
||||
*/
|
||||
class GuiExport SelectionGateFilterExternal: public SelectionGate
|
||||
{
|
||||
public:
|
||||
explicit SelectionGateFilterExternal(const char* docName, const char* objName = nullptr);
|
||||
bool allow(App::Document*, App::DocumentObject*, const char*) override;
|
||||
|
||||
private:
|
||||
std::string DocName;
|
||||
std::string ObjName;
|
||||
};
|
||||
|
||||
/** The Selection class
|
||||
* The selection singleton keeps track of the selection state of
|
||||
* the whole application. It gets messages from all entities which can
|
||||
@@ -382,7 +369,7 @@ public:
|
||||
/// of the active document is cleared.
|
||||
void clearSelection(const char* pDocName = nullptr, bool clearPreSelect = true);
|
||||
/// Clear the selection of all documents
|
||||
void clearCompleteSelection(bool clearPreSelect = true);
|
||||
void clearCompleteSelection(const char* pDocName = nullptr, bool clearPreSelect = true);
|
||||
/// Check if selected
|
||||
bool isSelected(
|
||||
const char* pDocName,
|
||||
@@ -397,7 +384,7 @@ public:
|
||||
ResolveMode resolve = ResolveMode::OldStyleElement
|
||||
) const;
|
||||
|
||||
const char* getSelectedElement(App::DocumentObject*, const char* pSubName) const;
|
||||
std::string getSelectedElement(App::DocumentObject*, const char* pSubName) const;
|
||||
|
||||
/// set the preselected object (mostly by the 3D view)
|
||||
int setPreselect(
|
||||
@@ -415,10 +402,19 @@ public:
|
||||
void setPreselectCoord(float x, float y, float z);
|
||||
/// returns the present preselection
|
||||
const SelectionChanges& getPreselection() const;
|
||||
/// add a SelectionGate to control what is selectable
|
||||
void addSelectionGate(Gui::SelectionGate* gate, ResolveMode resolve = ResolveMode::OldStyleElement);
|
||||
/// remove the active SelectionGate
|
||||
void rmvSelectionGate();
|
||||
/// add a SelectionGate to control what is selectable in a document's scope, by default the
|
||||
/// active document is selected
|
||||
// which is usually the intended behavior
|
||||
void addSelectionGate(
|
||||
Gui::SelectionGate* gate,
|
||||
ResolveMode resolve = ResolveMode::OldStyleElement,
|
||||
const char* pDocName = nullptr
|
||||
);
|
||||
/// remove the document's SelectionGate, by default the active document is selected, which is
|
||||
/// usually the intended behavior
|
||||
void rmvSelectionGate(const char* pDocName = nullptr);
|
||||
/// remove the document's SelectionGate (assumes valid pointer)
|
||||
void rmvSelectionGate(App::Document* doc);
|
||||
|
||||
int disableCommandLog();
|
||||
int enableCommandLog(bool silent = false);
|
||||
@@ -495,8 +491,9 @@ public:
|
||||
/** Set selection object visibility
|
||||
*
|
||||
* @param visible: see VisibleState
|
||||
* @param pDocName: name of the document that scopes the request, defaults to active document
|
||||
*/
|
||||
void setVisible(VisibleState visible);
|
||||
void setVisible(VisibleState visible, const char* pDocName = nullptr);
|
||||
|
||||
bool isClarifySelectionActive();
|
||||
void setClarifySelectionActive(bool active);
|
||||
@@ -580,7 +577,7 @@ public:
|
||||
std::vector<SelObj> getCompleteSelection(ResolveMode resolve = ResolveMode::OldStyleElement) const;
|
||||
|
||||
/// Check if there is any selection
|
||||
bool hasSelection() const;
|
||||
bool hasSelection(const char* pDocName = nullptr) const;
|
||||
|
||||
/** Check if there is any selection within a given document
|
||||
*
|
||||
@@ -594,7 +591,7 @@ public:
|
||||
* If \c resolve is false, then the match is only done with the top
|
||||
* level parent object.
|
||||
*/
|
||||
bool hasSelection(const char* doc, ResolveMode resolve = ResolveMode::OldStyleElement) const;
|
||||
bool hasSelection(const char* doc, ResolveMode resolve) const;
|
||||
|
||||
/** Check if there is any sub-element selection
|
||||
*
|
||||
@@ -611,10 +608,7 @@ public:
|
||||
bool hasPreselection() const;
|
||||
|
||||
/// Size of selected entities for all documents
|
||||
unsigned int size() const
|
||||
{
|
||||
return static_cast<unsigned int>(_SelList.size());
|
||||
}
|
||||
unsigned int size(const char* pDocName = nullptr) const;
|
||||
|
||||
/** @name Selection stack functions
|
||||
*
|
||||
@@ -623,16 +617,10 @@ public:
|
||||
*/
|
||||
//@{
|
||||
/// Return the current selection stack size
|
||||
std::size_t selStackBackSize() const
|
||||
{
|
||||
return _SelStackBack.size();
|
||||
}
|
||||
std::size_t selStackBackSize(const char* pDocName = nullptr) const;
|
||||
|
||||
/// Return the current forward selection stack size
|
||||
std::size_t selStackForwardSize() const
|
||||
{
|
||||
return _SelStackForward.size();
|
||||
}
|
||||
std::size_t selStackForwardSize(const char* pDocName = nullptr) const;
|
||||
|
||||
/** Obtain selected objects from stack
|
||||
*
|
||||
@@ -650,28 +638,31 @@ public:
|
||||
/** Go back selection history
|
||||
*
|
||||
* @param count: optional number of steps to go back
|
||||
* @param pDocName: the name of the document to index the context, defaults to active document
|
||||
*
|
||||
* This function pops the selection stack, and populate the current
|
||||
* selection with the content of the last pop'd entry
|
||||
*/
|
||||
void selStackGoBack(int count = 1);
|
||||
void selStackGoBack(int count = 1, const char* pDocName = nullptr);
|
||||
|
||||
/** Go forward selection history
|
||||
*
|
||||
* @param count: optional number of steps to go back
|
||||
* @param pDocName: the name of the document to index the context, defaults to active document
|
||||
*
|
||||
* This function pops the selection stack, and populate the current
|
||||
* selection with the content of the last pop'd entry
|
||||
*/
|
||||
void selStackGoForward(int count = 1);
|
||||
void selStackGoForward(int count = 1, const char* pDocName = nullptr);
|
||||
|
||||
/** Save the current selection on to the stack
|
||||
*
|
||||
* @param clearForward: whether to clear forward selection stack
|
||||
* @param overwrite: whether to overwrite the current top entry of the
|
||||
* stack instead of pushing a new entry.
|
||||
* @param pDocName: the name of the document to index the context, defaults to active document
|
||||
*/
|
||||
void selStackPush(bool clearForward = true, bool overwrite = false);
|
||||
void selStackPush(bool clearForward = true, bool overwrite = false, const char* pDocName = nullptr);
|
||||
//@}
|
||||
|
||||
/** @name Picked list functions
|
||||
@@ -682,11 +673,11 @@ public:
|
||||
*/
|
||||
//@{
|
||||
/// Check whether picked list is enabled
|
||||
bool needPickedList() const;
|
||||
bool needPickedList(const char* pDocName = nullptr) const;
|
||||
/// Turn on or off picked list
|
||||
void enablePickedList(bool);
|
||||
void enablePickedList(bool, const char* pDocName = nullptr);
|
||||
/// Check if there is any selection inside picked list
|
||||
bool hasPickedList() const;
|
||||
bool hasPickedList(const char* pDocName = nullptr) const;
|
||||
/// Return select objects inside picked list
|
||||
std::vector<SelectionSingleton::SelObj> getPickedList(const char* pDocName) const;
|
||||
/// Return selected object inside picked list grouped by top level parents
|
||||
@@ -708,9 +699,9 @@ public:
|
||||
GreedySelection
|
||||
};
|
||||
/// Changes the style of selection between greedy and normal.
|
||||
void setSelectionStyle(SelectionStyle selStyle);
|
||||
void setSelectionStyle(SelectionStyle selStyle, const char* pDocName = nullptr);
|
||||
/// Get the style of selection.
|
||||
SelectionStyle getSelectionStyle();
|
||||
SelectionStyle getSelectionStyle(const char* pDocName = nullptr);
|
||||
//@}
|
||||
|
||||
static SelectionSingleton& instance();
|
||||
@@ -756,24 +747,20 @@ protected:
|
||||
|
||||
/// Observer message from the App doc
|
||||
void slotDeletedObject(const App::DocumentObject&);
|
||||
void slotClosedDocument(const App::Document&);
|
||||
|
||||
/// helper to retrieve document by name
|
||||
App::Document* getDocument(const char* pDocName = nullptr) const;
|
||||
|
||||
void slotSelectionChanged(const SelectionChanges& msg);
|
||||
|
||||
SelectionChanges CurrentPreselection;
|
||||
|
||||
std::deque<SelectionChanges> NotificationQueue;
|
||||
bool Notifying = false;
|
||||
|
||||
void notify(SelectionChanges&& Chng);
|
||||
void notify(const SelectionChanges& Chng)
|
||||
{
|
||||
notify(SelectionChanges(Chng));
|
||||
}
|
||||
|
||||
struct _SelObj
|
||||
struct SelectionDescription
|
||||
{
|
||||
std::string DocName;
|
||||
std::string FeatName;
|
||||
@@ -792,34 +779,26 @@ protected:
|
||||
void log(bool remove = false, bool clearPreselect = true);
|
||||
std::string getSubString() const;
|
||||
};
|
||||
mutable std::list<_SelObj> _SelList;
|
||||
|
||||
mutable std::list<_SelObj> _PickedList;
|
||||
bool _needPickedList {false};
|
||||
|
||||
using SelStackItem = std::set<App::SubObjectT>;
|
||||
std::deque<SelStackItem> _SelStackBack;
|
||||
std::deque<SelStackItem> _SelStackForward;
|
||||
|
||||
int checkSelection(
|
||||
const char* pDocName,
|
||||
const char* pObjectName,
|
||||
const char* pSubName,
|
||||
ResolveMode resolve,
|
||||
_SelObj& sel,
|
||||
const std::list<_SelObj>* selList = nullptr
|
||||
SelectionDescription& sel,
|
||||
const std::list<SelectionDescription>* selList = nullptr
|
||||
) const;
|
||||
|
||||
std::vector<Gui::SelectionObject> getObjectList(
|
||||
const char* pDocName,
|
||||
Base::Type typeId,
|
||||
std::list<_SelObj>& objs,
|
||||
const std::list<SelectionDescription>& objs,
|
||||
ResolveMode resolve,
|
||||
bool single = false
|
||||
) const;
|
||||
|
||||
static App::DocumentObject* getObjectOfType(
|
||||
_SelObj& sel,
|
||||
const SelectionDescription& sel,
|
||||
Base::Type type,
|
||||
ResolveMode resolve,
|
||||
const char** subelement = nullptr
|
||||
@@ -831,21 +810,56 @@ protected:
|
||||
ResolveMode resolve = ResolveMode::OldStyleElement
|
||||
) const;
|
||||
|
||||
using SelStackItem = std::set<App::SubObjectT>;
|
||||
// Each document has a description context
|
||||
struct SelectionInfo
|
||||
{
|
||||
Gui::SelectionGate* gate {nullptr};
|
||||
ResolveMode resolveMode {ResolveMode::OldStyleElement};
|
||||
|
||||
std::list<SelectionDescription> selList;
|
||||
std::list<SelectionDescription> pickedList;
|
||||
bool needPickedList {false};
|
||||
|
||||
std::deque<SelStackItem> selStackBack;
|
||||
std::deque<SelStackItem> selStackForward;
|
||||
|
||||
SelectionStyle selectionStyle {SelectionStyle::NormalSelection};
|
||||
};
|
||||
struct SelectionContext
|
||||
{
|
||||
SelectionInfo* info;
|
||||
std::string docName;
|
||||
};
|
||||
struct SelectionConstContext
|
||||
{
|
||||
const SelectionInfo* info;
|
||||
std::string docName;
|
||||
};
|
||||
|
||||
// Returns a selection context or nullptr if the document is not found
|
||||
SelectionContext getSelectionContext(const char* pDocName);
|
||||
SelectionConstContext getSelectionContext(const char* pDocName) const;
|
||||
|
||||
static SelectionSingleton* _pcSingleton;
|
||||
|
||||
|
||||
std::map<App::Document*, SelectionInfo> docSelectionContext;
|
||||
|
||||
// Preselection helpers, it's a mess, needs clarifying -theo-vt
|
||||
std::string DocName;
|
||||
std::string FeatName;
|
||||
std::string SubName;
|
||||
float hx, hy, hz;
|
||||
float hx {0.0f}, hy {0.0f}, hz {0.0f};
|
||||
SelectionChanges CurrentPreselection;
|
||||
|
||||
Gui::SelectionGate* ActiveGate;
|
||||
ResolveMode gateResolve;
|
||||
|
||||
int logDisabled = 0;
|
||||
bool logHasSelection = false;
|
||||
bool clarifySelectionActive = false;
|
||||
int logDisabled {0};
|
||||
bool logHasSelection {false};
|
||||
bool clarifySelectionActive {false};
|
||||
|
||||
SelectionStyle selectionStyle;
|
||||
std::deque<SelectionChanges> NotificationQueue;
|
||||
bool Notifying {false};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user