Compare commits
210 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 | |||
| 5ae85148cc | |||
| 1182be91bc | |||
| cd5876dc54 | |||
| ca4660167e | |||
| a0c97a102e | |||
| 90c83636cb | |||
| e561950ff8 | |||
| e7e5e39e7b | |||
| d842e4151c | |||
| 320bd61156 | |||
| fed11359d9 | |||
| fdf0013dcb | |||
| bf98221885 | |||
| d214306d1b | |||
| a0af0d2b85 | |||
| ee27de857a | |||
| 4afbadec04 | |||
| cdd9d685fe | |||
| a34bad7da4 | |||
| e20b42e484 | |||
| 79923c16f3 | |||
| 004927e16e | |||
| e8d9f30c10 | |||
| f06dfa5af8 | |||
| dc1e96c46a | |||
| 96afba8351 | |||
| 72b9a91337 | |||
| c468a622e6 | |||
| edde9fb18e | |||
| 5754c66a55 | |||
| 146ed0d704 | |||
| e807d2d421 | |||
| 0e83500531 | |||
| 143c1737af | |||
| f7939beee0 | |||
| c6aeec9146 | |||
| 9e065cdb7f | |||
| b55dd47120 | |||
| 11dd9d8870 | |||
| 88940fb375 | |||
| aa5feb3df0 | |||
| dee9d5a498 | |||
| 8c18208c97 | |||
| d68b0c087e | |||
| 35b77bc069 | |||
| 0702616895 | |||
| f36aeef878 | |||
| 5e7bbe15e3 | |||
| 687c75e5bc | |||
| 36dd4b983c | |||
| cee71e1fb1 | |||
| baed171ec5 | |||
| ecb188fe4c | |||
| 1027be1efa | |||
| d2b84db105 | |||
| c263d8f51c | |||
| 0f0d2c849f | |||
| 025d4a9f5c | |||
| 8140957413 | |||
| 6ad2206595 | |||
| dff7fc6ca8 | |||
| 1671ecb902 | |||
| 8a17ca36a4 | |||
| b8929f279f | |||
| 07540ad3ba | |||
| c387a4b5c9 | |||
| 21ef29bb82 | |||
| 8cb06956d0 | |||
| 17651fbfa4 | |||
| 87651cdd4c | |||
| d34969f7d6 | |||
| 60ebafd0b3 | |||
| 15dd009f13 | |||
| dbb6cbe335 | |||
| 2dcb495a67 | |||
| a102c8a359 | |||
| 8fda540e1a | |||
| bbd1266398 | |||
| 087522528b | |||
| 18cf24e1d9 | |||
| f0a70d5468 | |||
| 26357db694 | |||
| 83d14b785e | |||
| bc0d94d7a7 | |||
| 840122a358 | |||
| a18f77f3b8 | |||
| eb522b52dc | |||
| 04e0518051 | |||
| dac4062d54 | |||
| 949c5421cf | |||
| 912fd47b5f | |||
| b098f3ad44 | |||
| f65dda922f | |||
| 4c6eed1e63 | |||
| 6238fe1081 | |||
| b20a6acc26 | |||
| c6b99363d6 | |||
| 26435d03fd | |||
| 3c56c164ee | |||
| 6ea74652c9 | |||
| 2db12d8a66 | |||
| 64a4cee116 | |||
| 1e982cae1f | |||
| 1ed0e153dc | |||
| b9b8d4023f | |||
| eb0d7ba6a5 | |||
| eaf76e4d0a | |||
| 659780c31b | |||
| 7be33f496e | |||
| 9972df7c1d | |||
| 9c9bf18478 | |||
| c1ca8a79c9 | |||
| c9518817c6 | |||
| ba24ab8a49 | |||
| b0e11e286b | |||
| 6d3f786b38 | |||
| 1eb6968265 |
@@ -58,7 +58,7 @@ jobs:
|
||||
logdir: /tmp/log/
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -68,6 +68,12 @@ runs:
|
||||
testCommand: ${{ inputs.builddir }}/tests/Gui_tests_run --gtest_output=json:${{ inputs.reportdir }}gui_gtest_results.json
|
||||
testLogFile: ${{ inputs.reportdir }}gui_gtest_test_log.txt
|
||||
testName: Gui
|
||||
- name: C++ Qt tests
|
||||
id: qttests
|
||||
uses: ./.github/workflows/actions/runCPPTests/runQtTests
|
||||
with:
|
||||
builddir: ${{ inputs.builddir }}
|
||||
testLogFile: ${{ inputs.reportdir }}qt_ctest_log.txt
|
||||
- name: C++ Material tests
|
||||
id: material
|
||||
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
# SPDX-FileNotice: Part of the FreeCAD project.
|
||||
|
||||
name: runQtTests
|
||||
description: "Run all QtTest-based C++ tests (those labelled 'Qt' in CTest), generate log and report"
|
||||
|
||||
inputs:
|
||||
builddir:
|
||||
description: "Build directory containing CTestTestfile.cmake"
|
||||
required: true
|
||||
testLogFile:
|
||||
description: "Path for the command-line output of the tests"
|
||||
required: true
|
||||
outputs:
|
||||
reportText:
|
||||
description: "Report text"
|
||||
value: ${{ steps.report.outputs.reportText }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Run Qt tests via CTest
|
||||
shell: bash -l {0}
|
||||
env:
|
||||
BUILD_DIR: ${{ inputs.builddir }}
|
||||
TEST_LOG_FILE: ${{ inputs.testLogFile }}
|
||||
run: |
|
||||
set -o pipefail
|
||||
ctest --test-dir "$BUILD_DIR" -L Qt --output-on-failure | tee -a "$TEST_LOG_FILE"
|
||||
- name: Parse test results
|
||||
if: always()
|
||||
id: report
|
||||
shell: bash -l {0}
|
||||
env:
|
||||
TEST_LOG_FILE: ${{ inputs.testLogFile }}
|
||||
run: |
|
||||
result=$(grep -E "(Test #|tests passed|tests failed|Total Test time)" "$TEST_LOG_FILE" || true)
|
||||
if grep -qE "[1-9][0-9]* tests failed" "$TEST_LOG_FILE"
|
||||
then
|
||||
reportText="<details><summary>:fire: QtTest C++ tests failed</summary>\n"
|
||||
else
|
||||
reportText="<details><summary>:heavy_check_mark: QtTest C++ tests succeeded</summary>\n"
|
||||
fi
|
||||
reportText+="\n"
|
||||
reportText+="Results\n"
|
||||
reportText+="\n"
|
||||
reportText+='```\n'
|
||||
reportText+="$result\n"
|
||||
reportText+='```\n'
|
||||
reportText+="</details>\n"
|
||||
reportText+="\n"
|
||||
echo "reportText<<EOD" >> $GITHUB_OUTPUT
|
||||
echo -e "$reportText" >> $GITHUB_OUTPUT
|
||||
echo "EOD" >> $GITHUB_OUTPUT
|
||||
echo -e "$reportText"
|
||||
@@ -0,0 +1,62 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
# SPDX-FileNotice: Part of the FreeCAD project.
|
||||
|
||||
name: runSingleQtTest
|
||||
description: "Run a single QtTest-based C++ test executable, generate log and report"
|
||||
|
||||
inputs:
|
||||
testCommand:
|
||||
description: "Test executable path"
|
||||
required: true
|
||||
testLogFile:
|
||||
description: "Path for the command-line output of the tests"
|
||||
required: true
|
||||
testName:
|
||||
description: "A descriptive name for the test suite"
|
||||
required: true
|
||||
outputs:
|
||||
reportText:
|
||||
description: "Report text"
|
||||
value: ${{ steps.report.outputs.reportText }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Run QtTest tests
|
||||
shell: bash -l {0}
|
||||
env:
|
||||
QT_QPA_PLATFORM: offscreen
|
||||
TEST_COMMAND: ${{ inputs.testCommand }}
|
||||
TEST_LOG_FILE: ${{ inputs.testLogFile }}
|
||||
run: |
|
||||
set -o pipefail
|
||||
$TEST_COMMAND | tee -a "$TEST_LOG_FILE"
|
||||
- name: Parse test results
|
||||
if: always()
|
||||
id: report
|
||||
shell: bash -l {0}
|
||||
env:
|
||||
TEST_LOG_FILE: ${{ inputs.testLogFile }}
|
||||
TEST_NAME: ${{ inputs.testName }}
|
||||
run: |
|
||||
# This bonkers sed regex is to try to find the start and end of the QtTest output block, which is not really
|
||||
# designed to be machine-readable. Why did they have to choose asterisks?!?!
|
||||
result=$(sed -n '/^\*\*\*\*\*\*\*\*\* Start/,/^\*\*\*\*\*\*\*\*\* Finished/p' "$TEST_LOG_FILE" || true)
|
||||
if grep -qE "^FAIL!" "$TEST_LOG_FILE"
|
||||
then
|
||||
reportText="<details><summary>:fire: QtTest C++ test suite '${TEST_NAME}' failed</summary>\n"
|
||||
else
|
||||
reportText="<details><summary>:heavy_check_mark: QtTest C++ test suite '${TEST_NAME}' succeeded</summary>\n"
|
||||
fi
|
||||
reportText+="\n"
|
||||
reportText+="Results\n"
|
||||
reportText+="\n"
|
||||
reportText+='```\n'
|
||||
reportText+="$result\n"
|
||||
reportText+='```\n'
|
||||
reportText+="</details>\n"
|
||||
reportText+="\n"
|
||||
echo "reportText<<EOD" >> $GITHUB_OUTPUT
|
||||
echo -e "$reportText" >> $GITHUB_OUTPUT
|
||||
echo "EOD" >> $GITHUB_OUTPUT
|
||||
echo -e "$reportText"
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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,14 +43,17 @@ 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"
|
||||
|
||||
- name: Trigger notes updater workflow (only for weekly)
|
||||
if: startsWith(steps.get_tag.outputs.build_tag, 'weekly-')
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
env:
|
||||
WEEKLY_TAG: ${{ steps.get_tag.outputs.build_tag }}
|
||||
with:
|
||||
@@ -105,7 +112,7 @@ jobs:
|
||||
environment: weekly-build
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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@67526e7bd8100b870f10b1c120780a8375777b43 # v3.25.5
|
||||
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@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
|
||||
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@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.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 }}
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Inject compare link into weekly release notes
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
env:
|
||||
# Pass manual inputs via env for convenience
|
||||
CURRENT_TAG: ${{ github.event.inputs.current_tag }}
|
||||
|
||||
+3
-1
@@ -62,7 +62,9 @@ The FreeCAD Contribution Process is expressed here with the following specific g
|
||||
1. the PR description MUST contain proper attribution as the first line, for example: "This is work of XYZ cherry-picked from <link>";
|
||||
2. all commits MUST have proper authorship, i.e. be authored by the original author and committed by the author of the PR;
|
||||
3. if changes to cherry-picked commits are necessary they SHOULD be done as follow-up commits. If it is not possible to do so, then the modified commits MUST contain a `Co-Authored-By` trailer in their commit message.
|
||||
14. A “Valid PR” is one which satisfies the above requirements.
|
||||
14. Contributions must meet existing quality standards. Raw AI output is not accepted under any circumstances. AI may be used only as an assistive tool; in all cases, the resulting content must be reviewed, validated, and justifiable by the contributor. The contributor should be able to explain design and code decisions, answer reviewers’ questions, and ensure that AI use does not waste reviewers’ time during review. That being said, the use of AI is not recommended under any circumstances or in any manner.
|
||||
15. The contributor provides reasonable assurance that the contribution does not infringe third-party copyrights or license terms.
|
||||
16. A “Valid PR” is one which satisfies the above requirements.
|
||||
|
||||
## 6. Process
|
||||
|
||||
|
||||
+41
-9
@@ -1,7 +1,11 @@
|
||||
# The Qt for Python project officially recommends using pip to install PySide, so we expect to find PySide in the
|
||||
# site-packages directory. The library will be called "PySide6.abi3.*", and there will be an "include" directory inside
|
||||
# the site-packages/PySide6. Over time some distros may provide custom versions, so we also support using a more normal
|
||||
# cMake find_package() call
|
||||
# The Qt for Python project officially recommends using pip to install PySide,
|
||||
# so we expect to find PySide in the site-packages directory.
|
||||
# The library will be called "PySide6.abi3.*", and there will
|
||||
# be an "include" directory inside the site-packages/PySide6.
|
||||
# Over time some distros may provide custom versions, so we also support
|
||||
# using a more normal cMake find_package() call
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package(PySide6 CONFIG QUIET)
|
||||
|
||||
@@ -9,12 +13,40 @@ if(NOT PySide6_FOUND)
|
||||
find_pip_package(PySide6)
|
||||
endif()
|
||||
|
||||
if(NOT PySide6_INCLUDE_DIRS AND TARGET PySide6::pyside6)
|
||||
get_property(PySide6_INCLUDE_DIRS TARGET PySide6::pyside6 PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
|
||||
endif()
|
||||
if(PySide6_FOUND)
|
||||
# verify PySide6 version matches Qt6 version (major.minor)
|
||||
if(PySide6_VERSION AND Qt6_VERSION)
|
||||
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)" _qt6_major_minor "${Qt6_VERSION}")
|
||||
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)" _pyside6_major_minor "${PySide6_VERSION}")
|
||||
|
||||
# Also provide the old-style variables so we don't have to update everything yet
|
||||
if (PySide6_FOUND)
|
||||
message(STATUS "Qt version: ${Qt6_VERSION}")
|
||||
message(STATUS "PySide version: ${PySide6_VERSION}")
|
||||
|
||||
if(NOT _qt6_major_minor STREQUAL _pyside6_major_minor)
|
||||
message(FATAL_ERROR
|
||||
" --------------------------------------------------------
|
||||
Qt/PySide version mismatch!
|
||||
cmake found Qt: ${Qt6_VERSION}
|
||||
cmake found PySide: ${PySide6_VERSION}
|
||||
major.minor versions of Qt and PySide must match to avoid errors.
|
||||
Ensure CMAKE_PREFIX_PATH points to matching Qt and PySide6 installations.
|
||||
--------------------------------------------------------"
|
||||
)
|
||||
endif()
|
||||
|
||||
message(STATUS "PySide/Qt version check passed (${_pyside6_major_minor})")
|
||||
endif()
|
||||
|
||||
if(NOT PySide6_INCLUDE_DIRS AND TARGET PySide6::pyside6)
|
||||
get_property(PySide6_INCLUDE_DIRS TARGET PySide6::pyside6 PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
|
||||
endif()
|
||||
|
||||
find_package_handle_standard_args(PySide6
|
||||
REQUIRED_VARS PySide6_INCLUDE_DIRS
|
||||
VERSION_VAR PySide6_VERSION
|
||||
)
|
||||
|
||||
# Also provide the old-style variables so we don't have to update everything yet
|
||||
set(PYSIDE_INCLUDE_DIR ${PySide6_INCLUDE_DIRS})
|
||||
set(PYSIDE_LIBRARY ${PySide6_LIBRARIES})
|
||||
set(PYSIDE_FOUND TRUE)
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
# PYSIDE_RCC_EXECUTABLE - Location of PYSIDE6RCC executable
|
||||
# PYSIDE_TOOLS_FOUND - PYSIDE6 utilities found.
|
||||
|
||||
if (TARGET Qt6::uic)
|
||||
if(TARGET Qt6::uic)
|
||||
get_target_property(PYSIDE6_UIC_EXECUTABLE Qt6::uic LOCATION)
|
||||
set(UICOPTIONS "--generator=python")
|
||||
endif()
|
||||
if (TARGET Qt6::rcc)
|
||||
if(TARGET Qt6::rcc)
|
||||
get_target_property(PYSIDE6_RCC_EXECUTABLE Qt6::rcc LOCATION)
|
||||
set(RCCOPTIONS "--generator=python" "--compress-algo=zlib" "--compress=1")
|
||||
endif()
|
||||
@@ -18,15 +18,15 @@ set(PySideTools_VERSION 6)
|
||||
|
||||
if(PYSIDE_RCC_EXECUTABLE AND PYSIDE_UIC_EXECUTABLE)
|
||||
set(PYSIDE_TOOLS_FOUND TRUE)
|
||||
if (NOT PYSIDE6Tools_FIND_QUIETLY)
|
||||
if(NOT PYSIDE6Tools_FIND_QUIETLY)
|
||||
message(STATUS "Found PYSIDE6 tools: ${PYSIDE_UIC_EXECUTABLE}, ${PYSIDE_RCC_EXECUTABLE}")
|
||||
endif ()
|
||||
endif()
|
||||
else()
|
||||
if(PYSIDE6Tools_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "PYSIDE6 tools could not be found, but are required.")
|
||||
else()
|
||||
if (NOT PYSIDE6Tools_FIND_QUIETLY)
|
||||
if(NOT PYSIDE6Tools_FIND_QUIETLY)
|
||||
message(STATUS "PYSIDE6 tools: not found.")
|
||||
endif ()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -10,14 +10,14 @@ if(NOT Shiboken6_FOUND)
|
||||
endif()
|
||||
if(NOT Shiboken6_INCLUDE_DIR)
|
||||
find_pip_package(Shiboken6)
|
||||
if (Shiboken6_FOUND)
|
||||
if(Shiboken6_FOUND)
|
||||
set(SHIBOKEN_LIBRARY ${Shiboken6_LIBRARIES})
|
||||
set(SHIBOKEN_MAJOR_VERSION 6)
|
||||
set(SHIBOKEN_FOUND ON)
|
||||
endif()
|
||||
# The include directory we actually want is part of shiboken6-generator
|
||||
find_pip_package(shiboken6_generator)
|
||||
if (shiboken6_generator_FOUND)
|
||||
if(shiboken6_generator_FOUND)
|
||||
set(SHIBOKEN_INCLUDE_DIR ${shiboken6_generator_INCLUDE_DIRS})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -7,6 +7,22 @@ macro(SetupFreetype)
|
||||
message("===============================================================\n"
|
||||
"FreeType2 not found. Part module will lack of makeWireString().\n"
|
||||
"===============================================================\n")
|
||||
else()
|
||||
# find_package(harfbuzz CONFIG) fails on windows
|
||||
# lets do it the complicated way instead.
|
||||
find_path(HARFBUZZ_INCLUDE_DIR hb.h PATH_SUFFIXES harfbuzz)
|
||||
find_library(HARFBUZZ_LIBRARY NAMES harfbuzz)
|
||||
if(HARFBUZZ_INCLUDE_DIR AND HARFBUZZ_LIBRARY)
|
||||
if(NOT TARGET harfbuzz::harfbuzz)
|
||||
add_library(harfbuzz::harfbuzz UNKNOWN IMPORTED)
|
||||
set_target_properties(harfbuzz::harfbuzz PROPERTIES
|
||||
IMPORTED_LOCATION "${HARFBUZZ_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${HARFBUZZ_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "HarfBuzz not found")
|
||||
endif()
|
||||
endif(NOT FREETYPE_FOUND)
|
||||
endif(FREECAD_USE_FREETYPE)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ macro(SetupShibokenAndPyside)
|
||||
if(DEFINED MACPORTS_PREFIX)
|
||||
find_package(Shiboken REQUIRED HINTS "${PYTHON_LIBRARY_DIR}/cmake")
|
||||
find_package(PySide REQUIRED HINTS "${PYTHON_LIBRARY_DIR}/cmake")
|
||||
endif(DEFINED MACPORTS_PREFIX)
|
||||
endif()
|
||||
|
||||
if(FREECAD_QT_MAJOR_VERSION EQUAL 5)
|
||||
set(SHIBOKEN_MAJOR_VERSION 2)
|
||||
@@ -21,16 +21,16 @@ macro(SetupShibokenAndPyside)
|
||||
# Shiboken2Config.cmake may explicitly set CMAKE_BUILD_TYPE to Release which causes
|
||||
# CMake to fail to create Makefiles for a debug build.
|
||||
# So as a workaround we save and restore the value after checking for Shiboken2.
|
||||
set (SAVE_BUILD_TYPE ${CMAKE_BUILD_TYPE})
|
||||
set(SAVE_BUILD_TYPE ${CMAKE_BUILD_TYPE})
|
||||
find_package(Shiboken${SHIBOKEN_MAJOR_VERSION} QUIET)
|
||||
set (CMAKE_BUILD_TYPE ${SAVE_BUILD_TYPE})
|
||||
if (Shiboken${SHIBOKEN_MAJOR_VERSION}_FOUND)
|
||||
set(CMAKE_BUILD_TYPE ${SAVE_BUILD_TYPE})
|
||||
if(Shiboken${SHIBOKEN_MAJOR_VERSION}_FOUND)
|
||||
# Shiboken config file was found but it may use the wrong Python version
|
||||
# Try to get the matching config suffix and repeat finding the package
|
||||
set(SHIBOKEN_PATTERN .cpython-${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR})
|
||||
|
||||
file(GLOB SHIBOKEN_CONFIG "${Shiboken${SHIBOKEN_MAJOR_VERSION}_DIR}/Shiboken${SHIBOKEN_MAJOR_VERSION}Config${SHIBOKEN_PATTERN}*.cmake")
|
||||
if (SHIBOKEN_CONFIG)
|
||||
if(SHIBOKEN_CONFIG)
|
||||
get_filename_component(SHIBOKEN_CONFIG_SUFFIX ${SHIBOKEN_CONFIG} NAME)
|
||||
string(SUBSTRING ${SHIBOKEN_CONFIG_SUFFIX} 15 -1 SHIBOKEN_CONFIG_SUFFIX)
|
||||
string(REPLACE ".cmake" "" PYTHON_CONFIG_SUFFIX ${SHIBOKEN_CONFIG_SUFFIX})
|
||||
@@ -47,11 +47,11 @@ macro(SetupShibokenAndPyside)
|
||||
# to dance to be compatible with the old (<5.12) and the new versions (>=5.12)
|
||||
if(NOT SHIBOKEN_INCLUDE_DIR AND TARGET Shiboken${SHIBOKEN_MAJOR_VERSION}::libshiboken)
|
||||
get_property(SHIBOKEN_INCLUDE_DIR TARGET Shiboken${SHIBOKEN_MAJOR_VERSION}::libshiboken PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
|
||||
endif(NOT SHIBOKEN_INCLUDE_DIR AND TARGET Shiboken${SHIBOKEN_MAJOR_VERSION}::libshiboken)
|
||||
endif()
|
||||
|
||||
if(NOT SHIBOKEN_INCLUDE_DIR)
|
||||
find_pip_package(Shiboken${SHIBOKEN_MAJOR_VERSION})
|
||||
if (Shiboken${SHIBOKEN_MAJOR_VERSION}_FOUND)
|
||||
if(Shiboken${SHIBOKEN_MAJOR_VERSION}_FOUND)
|
||||
set(SHIBOKEN_INCLUDE_DIR ${Shiboken${SHIBOKEN_MAJOR_VERSION}_INCLUDE_DIRS})
|
||||
set(SHIBOKEN_LIBRARY ${Shiboken${SHIBOKEN_MAJOR_VERSION}_LIBRARIES})
|
||||
endif()
|
||||
@@ -63,11 +63,11 @@ macro(SetupShibokenAndPyside)
|
||||
# Our internal FindPySide6.cmake file already provides these for PySide6
|
||||
if(NOT PYSIDE_INCLUDE_DIR AND TARGET PySide${PYSIDE_MAJOR_VERSION}::pyside${PYSIDE_MAJOR_VERSION})
|
||||
get_property(PYSIDE_INCLUDE_DIR TARGET PySide${PYSIDE_MAJOR_VERSION}::pyside${PYSIDE_MAJOR_VERSION} PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
|
||||
endif(NOT PYSIDE_INCLUDE_DIR AND TARGET PySide${PYSIDE_MAJOR_VERSION}::pyside${PYSIDE_MAJOR_VERSION})
|
||||
endif()
|
||||
|
||||
if(NOT PYSIDE_INCLUDE_DIR)
|
||||
find_pip_package(PySide${PYSIDE_MAJOR_VERSION})
|
||||
if (PySide${PYSIDE_MAJOR_VERSION}_FOUND)
|
||||
if(PySide${PYSIDE_MAJOR_VERSION}_FOUND)
|
||||
set(PYSIDE_INCLUDE_DIR ${PySide${PYSIDE_MAJOR_VERSION}_INCLUDE_DIRS})
|
||||
set(PYSIDE_LIBRARY ${PySide${PYSIDE_MAJOR_VERSION}_LIBRARIES})
|
||||
endif()
|
||||
@@ -175,22 +175,22 @@ macro(SetupShibokenAndPyside)
|
||||
message(STATUS "PySide ${PySide_VERSION} Python module found at ${PRINT_OUTPUT}.\n")
|
||||
endif()
|
||||
|
||||
endmacro(SetupShibokenAndPyside)
|
||||
endmacro()
|
||||
|
||||
|
||||
MACRO(PYSIDE_WRAP_RC outfiles)
|
||||
if (NOT PYSIDE_RCC_EXECUTABLE)
|
||||
macro(PYSIDE_WRAP_RC outfiles)
|
||||
if(NOT PYSIDE_RCC_EXECUTABLE)
|
||||
message(FATAL_ERROR "Qt rcc is required for generating ${ARGN}")
|
||||
endif()
|
||||
FOREACH(it ${ARGN})
|
||||
GET_FILENAME_COMPONENT(outfile ${it} NAME_WE)
|
||||
GET_FILENAME_COMPONENT(infile ${it} ABSOLUTE)
|
||||
SET(outfile "${CMAKE_CURRENT_BINARY_DIR}/${outfile}_rc.py")
|
||||
foreach(it ${ARGN})
|
||||
get_filename_component(outfile ${it} NAME_WE)
|
||||
get_filename_component(infile ${it} ABSOLUTE)
|
||||
set(outfile "${CMAKE_CURRENT_BINARY_DIR}/${outfile}_rc.py")
|
||||
#ADD_CUSTOM_TARGET(${it} ALL
|
||||
# DEPENDS ${outfile}
|
||||
#)
|
||||
if(WIN32 OR APPLE)
|
||||
ADD_CUSTOM_COMMAND(OUTPUT ${outfile}
|
||||
add_custom_command(OUTPUT ${outfile}
|
||||
COMMAND ${PYSIDE_RCC_EXECUTABLE} ${RCCOPTIONS} ${infile} -o ${outfile}
|
||||
MAIN_DEPENDENCY ${infile}
|
||||
)
|
||||
@@ -198,7 +198,7 @@ MACRO(PYSIDE_WRAP_RC outfiles)
|
||||
# Especially on Open Build Service we don't want changing date like
|
||||
# pyside-rcc generates in comments at beginning, which is why
|
||||
# we follow the tool command with in-place sed.
|
||||
ADD_CUSTOM_COMMAND(OUTPUT "${outfile}"
|
||||
add_custom_command(OUTPUT "${outfile}"
|
||||
COMMAND "${PYSIDE_RCC_EXECUTABLE}" ${RCCOPTIONS} "${infile}" ${PY_ATTRIBUTE} -o "${outfile}"
|
||||
# The line below sometimes catches unwanted lines too - but there is no date in the file
|
||||
# anymore with Qt5 RCC, so commenting it out for now...
|
||||
@@ -207,5 +207,5 @@ MACRO(PYSIDE_WRAP_RC outfiles)
|
||||
)
|
||||
endif()
|
||||
list(APPEND ${outfiles} ${outfile})
|
||||
ENDFOREACH(it)
|
||||
ENDMACRO (PYSIDE_WRAP_RC)
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
Binary file not shown.
@@ -57,8 +57,6 @@
|
||||
test: xvfb-run /usr/bin/FreeCAD -t TestPartGui
|
||||
/TestPartDesignGui :
|
||||
test: xvfb-run /usr/bin/FreeCAD -t TestPartDesignGui
|
||||
/TestSketcherGui :
|
||||
test: xvfb-run /usr/bin/FreeCAD -t TestSketcherGui
|
||||
/TestTechDrawGui :
|
||||
test: xvfb-run /usr/bin/FreeCAD -t TestTechDrawGui
|
||||
/Workbench :
|
||||
|
||||
@@ -31,4 +31,4 @@ else
|
||||
MAIN="$HERE/usr/bin/freecad"
|
||||
fi
|
||||
|
||||
"${MAIN}" "$@"
|
||||
exec "${MAIN}" "$@"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = "*"
|
||||
|
||||
+100
-2
@@ -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
|
||||
{
|
||||
@@ -1476,6 +1485,93 @@ void Application::addExportType(const char* filter, const char* moduleName)
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
// To enable changing languages while the program is running, cache the translatable export type
|
||||
// entries so that their addition can be "replayed" when the language changes (after removing
|
||||
// the originals).
|
||||
|
||||
struct TranslatableTypeCacheEntry {
|
||||
std::string description;
|
||||
const std::vector<std::string> extensions;
|
||||
std::string moduleName;
|
||||
};
|
||||
|
||||
class TranslatableTypeCache {
|
||||
public:
|
||||
TranslatableTypeCache() = default;
|
||||
void addCacheEntry(TranslatableTypeCacheEntry entry) {
|
||||
_cache.push_back(std::move(entry));
|
||||
}
|
||||
std::vector<TranslatableTypeCacheEntry> getCache() const {
|
||||
return _cache;
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
_cache.clear();
|
||||
}
|
||||
private:
|
||||
std::vector<TranslatableTypeCacheEntry> _cache;
|
||||
};
|
||||
|
||||
TranslatableTypeCache translatableExportTypeCache;
|
||||
|
||||
// Given a description string and a list of extensions, construct a type string that Qt's file
|
||||
// dialogs will recognize
|
||||
void appendTypeString(std::string &description, const std::vector<std::string> &extensions) {
|
||||
description = fmt::format("{} (*.{})", description, fmt::join(extensions, " *."));
|
||||
}
|
||||
}
|
||||
|
||||
void Application::addTranslatableExportType(const std::string &description,
|
||||
const std::vector<std::string> &extensions,
|
||||
const std::string &moduleName)
|
||||
{
|
||||
assert(!extensions.empty()); // Programming error, there must be extensions
|
||||
|
||||
// Branding: replace "FreeCAD" in a file type description with the branded application name
|
||||
auto replaceFreeCAD =
|
||||
[](std::string& s)
|
||||
{
|
||||
constexpr std::string_view freecad = "FreeCAD";
|
||||
if (auto pos = s.find(freecad); pos != std::string::npos) {
|
||||
s.replace(pos, freecad.size(), getExecutableName());
|
||||
return true; // Contained the app name
|
||||
}
|
||||
return false; // Did NOT contain the app name
|
||||
};
|
||||
|
||||
translatableExportTypeCache.addCacheEntry({description, extensions, moduleName});
|
||||
auto translatedDescription = QCoreApplication::translate("FileFormat", description.c_str()).toStdString();
|
||||
bool containsAppName = replaceFreeCAD(translatedDescription); // Run *AFTER* translation
|
||||
appendTypeString(translatedDescription, extensions);
|
||||
|
||||
FileTypeItem item;
|
||||
item.filter = translatedDescription;
|
||||
item.module = moduleName;
|
||||
item.types = extensions;
|
||||
item.translatable = true;
|
||||
|
||||
if (containsAppName) {
|
||||
// put to the front of the array
|
||||
_mExportTypes.insert(_mExportTypes.begin(),std::move(item));
|
||||
}
|
||||
else {
|
||||
_mExportTypes.push_back(std::move(item));
|
||||
}
|
||||
}
|
||||
|
||||
void Application::retranslateExportTypes()
|
||||
{
|
||||
auto cache = translatableExportTypeCache.getCache();
|
||||
translatableExportTypeCache.clear();
|
||||
std::erase_if(_mExportTypes, [](const FileTypeItem& item) {
|
||||
return item.translatable;
|
||||
});
|
||||
for (const auto &cacheEntry : translatableExportTypeCache.getCache()) {
|
||||
addTranslatableExportType(cacheEntry.description, cacheEntry.extensions, cacheEntry.moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::changeExportModule(const char* filter, const char* oldModuleName, const char* newModuleName)
|
||||
{
|
||||
for (auto& it : _mExportTypes) {
|
||||
@@ -2405,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
|
||||
@@ -3526,7 +3624,7 @@ void Application::getVerboseCommonInfo(QTextStream& str, const std::map<std::str
|
||||
const QString deskSess =
|
||||
QProcessEnvironment::systemEnvironment().value(QStringLiteral("DESKTOP_SESSION"),
|
||||
QString());
|
||||
|
||||
|
||||
const QString major = getValueOrEmpty(mConfig, "BuildVersionMajor");
|
||||
const QString minor = getValueOrEmpty(mConfig, "BuildVersionMinor");
|
||||
const QString point = getValueOrEmpty(mConfig, "BuildVersionPoint");
|
||||
|
||||
+69
-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
|
||||
@@ -603,6 +622,22 @@ public:
|
||||
*/
|
||||
void addExportType(const char* filter, const char* moduleName);
|
||||
|
||||
/**
|
||||
* @brief Register an export filetype with a translatable description
|
||||
*
|
||||
* @param[in] description A translatable string describing the file type. Must not contain the
|
||||
* list of extensions.
|
||||
* @param[in] extensions A list of supported extensions. Do not include the "*.", only the
|
||||
* extension itself. For example, "txt", not "*.txt".
|
||||
* @param[in] moduleName The name of the module handling the export.
|
||||
*/
|
||||
void addTranslatableExportType(const std::string &description,
|
||||
const std::vector<std::string> &extensions,
|
||||
const std::string &moduleName);
|
||||
|
||||
/// Intended to be called when the language is changed, this retranslates the export type.
|
||||
void retranslateExportTypes();
|
||||
|
||||
/**
|
||||
* @copydoc changeImportModule
|
||||
*/
|
||||
@@ -995,6 +1030,7 @@ private:
|
||||
std::string filter;
|
||||
std::string module;
|
||||
std::vector<std::string> types;
|
||||
bool translatable = false;
|
||||
};
|
||||
|
||||
// open ending information
|
||||
@@ -1023,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
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,12 @@ PyMethodDef ApplicationPy::Methods[] = {
|
||||
(PyCFunction)ApplicationPy::sAddExportType,
|
||||
METH_VARARGS,
|
||||
"Register filetype for export"},
|
||||
{"addTranslatableExportType",
|
||||
(PyCFunction)ApplicationPy::sAddTranslatableExportType,
|
||||
METH_VARARGS,
|
||||
"addTranslatableExportType(description:str, extensions:list[str], module_name:str)\n\n"
|
||||
"Register filetype with translatable description for export. Description should be a\n"
|
||||
"string registered with the translation system using the 'FileFormat' context."},
|
||||
{"changeExportModule",
|
||||
(PyCFunction)ApplicationPy::sChangeExportModule,
|
||||
METH_VARARGS,
|
||||
@@ -236,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."},
|
||||
@@ -751,6 +755,47 @@ PyObject* ApplicationPy::sAddExportType(PyObject* /*self*/, PyObject* args)
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* ApplicationPy::sAddTranslatableExportType(PyObject* /*self*/, PyObject* args)
|
||||
{
|
||||
char *description {};
|
||||
PyObject *pyExtensions {};
|
||||
char *moduleName {};
|
||||
|
||||
if (!PyArg_ParseTuple(args, "sOs", &description, &pyExtensions, &moduleName)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!PyList_Check(pyExtensions)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Expected a list of strings as second argument");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py_ssize_t n = PyList_Size(pyExtensions);
|
||||
|
||||
std::vector<std::string> extensions;
|
||||
for (Py_ssize_t i = 0; i < n; ++i) {
|
||||
PyObject *item = PyList_GetItem(pyExtensions, i);
|
||||
|
||||
if (!PyUnicode_Check(item)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Extensions list elements must be strings");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *value = PyUnicode_AsUTF8(item);
|
||||
if (!value) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extensions.emplace_back(value);
|
||||
}
|
||||
|
||||
GetApplication().addTranslatableExportType(description, extensions, moduleName);
|
||||
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* ApplicationPy::sChangeExportModule(PyObject* /*self*/, PyObject* args)
|
||||
{
|
||||
char *key {};
|
||||
@@ -1154,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;
|
||||
@@ -1176,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);
|
||||
@@ -1198,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;
|
||||
|
||||
@@ -47,6 +47,7 @@ public:
|
||||
static PyObject* sChangeImportModule (PyObject *self, PyObject *args);
|
||||
static PyObject* sGetImportType (PyObject *self, PyObject *args);
|
||||
static PyObject* sAddExportType (PyObject *self, PyObject *args);
|
||||
static PyObject* sAddTranslatableExportType (PyObject *self, PyObject *args);
|
||||
static PyObject* sChangeExportModule (PyObject *self, PyObject *args);
|
||||
static PyObject* sGetExportType (PyObject *self, PyObject *args);
|
||||
static PyObject* sGetResourcePath (PyObject *self, PyObject *args);
|
||||
|
||||
+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
|
||||
{
|
||||
|
||||
@@ -211,6 +211,9 @@ EXPO [eE][-+]?[0-9]+
|
||||
"h" COUNTCHARS; yylval.quantity.scaler = Quantity::Hour; yylval.quantity.unitStr = yytext; return UNIT; // hour
|
||||
|
||||
"A" COUNTCHARS; yylval.quantity.scaler = Quantity::Ampere; yylval.quantity.unitStr = yytext; return UNIT; // Ampere (internal standard electric current)
|
||||
"nA" COUNTCHARS; yylval.quantity.scaler = Quantity::NanoAmpere; yylval.quantity.unitStr = yytext; return UNIT; // nano Ampere
|
||||
"uA" COUNTCHARS; yylval.quantity.scaler = Quantity::MicroAmpere; yylval.quantity.unitStr = yytext; return UNIT; // micro Ampere
|
||||
"\xC2\xB5""A" COUNTCHARS; yylval.quantity.scaler = Quantity::MicroAmpere; yylval.quantity.unitStr = yytext; return UNIT; // micro Ampere
|
||||
"mA" COUNTCHARS; yylval.quantity.scaler = Quantity::MilliAmpere; yylval.quantity.unitStr = yytext; return UNIT; // milli Ampere
|
||||
"kA" COUNTCHARS; yylval.quantity.scaler = Quantity::KiloAmpere; yylval.quantity.unitStr = yytext; return UNIT; // kilo Ampere
|
||||
"MA" COUNTCHARS; yylval.quantity.scaler = Quantity::MegaAmpere; yylval.quantity.unitStr = yytext; return UNIT; // mega Ampere
|
||||
@@ -221,6 +224,9 @@ EXPO [eE][-+]?[0-9]+
|
||||
"uK" COUNTCHARS; yylval.quantity.scaler = Quantity::MicroKelvin; yylval.quantity.unitStr = yytext; return UNIT; // micro Kelvin
|
||||
|
||||
"mol" COUNTCHARS; yylval.quantity.scaler = Quantity::Mole; yylval.quantity.unitStr = yytext; return UNIT; // Mole (internal standard amount of substance)
|
||||
"nmol" COUNTCHARS; yylval.quantity.scaler = Quantity::NanoMole; yylval.quantity.unitStr = yytext; return UNIT; // nano Mole
|
||||
"\xC2\xB5mol" COUNTCHARS; yylval.quantity.scaler = Quantity::MicroMole; yylval.quantity.unitStr = yytext; return UNIT; // micro Mole
|
||||
"umol" COUNTCHARS; yylval.quantity.scaler = Quantity::MicroMole; yylval.quantity.unitStr = yytext; return UNIT; // micro Mole
|
||||
"mmol" COUNTCHARS; yylval.quantity.scaler = Quantity::MilliMole; yylval.quantity.unitStr = yytext; return UNIT; // milli Mole
|
||||
|
||||
"cd" COUNTCHARS; yylval.quantity.scaler = Quantity::Candela; yylval.quantity.unitStr = yytext; return UNIT; // Candela (internal standard luminous intensity)
|
||||
@@ -269,6 +275,9 @@ EXPO [eE][-+]?[0-9]+
|
||||
"Mpsi" COUNTCHARS; yylval.quantity.scaler = Quantity::MPSI; yylval.quantity.unitStr = yytext; return UNIT; // 1000 ksi
|
||||
|
||||
"W" COUNTCHARS; yylval.quantity.scaler = Quantity::Watt; yylval.quantity.unitStr = yytext; return UNIT; // Watt (kg*m^2/s^3)
|
||||
"nW" COUNTCHARS; yylval.quantity.scaler = Quantity::NanoWatt; yylval.quantity.unitStr = yytext; return UNIT; // nano Watt
|
||||
"uW" COUNTCHARS; yylval.quantity.scaler = Quantity::MicroWatt; yylval.quantity.unitStr = yytext; return UNIT; // micro Watt
|
||||
"\xC2\xB5W" COUNTCHARS; yylval.quantity.scaler = Quantity::MicroWatt; yylval.quantity.unitStr = yytext; return UNIT; // micro Watt
|
||||
"mW" COUNTCHARS; yylval.quantity.scaler = Quantity::MilliWatt; yylval.quantity.unitStr = yytext; return UNIT; // milli Watt
|
||||
"kW" COUNTCHARS; yylval.quantity.scaler = Quantity::KiloWatt; yylval.quantity.unitStr = yytext; return UNIT; // kilo Watt
|
||||
"VA" COUNTCHARS; yylval.quantity.scaler = Quantity::VoltAmpere; yylval.quantity.unitStr = yytext; return UNIT; // VoltAmpere (kg*m^2/s^3)
|
||||
|
||||
+6215
-6349
File diff suppressed because it is too large
Load Diff
+10
-178
@@ -70,7 +70,6 @@
|
||||
|
||||
|
||||
/* First part of user prologue. */
|
||||
#line 26 "Expression.y"
|
||||
|
||||
#define YYSTYPE App::ExpressionParser::semantic_type
|
||||
|
||||
@@ -79,7 +78,6 @@ std::stack<FunctionExpression::Function> functions; /**< Function
|
||||
#define yyparse ExpressionParser_yyparse
|
||||
#define yyerror ExpressionParser_yyerror
|
||||
|
||||
#line 80 "Expression.tab.c"
|
||||
|
||||
# ifndef YY_CAST
|
||||
# ifdef __cplusplus
|
||||
@@ -555,14 +553,14 @@ static const yytype_int8 yytranslate[] =
|
||||
/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
|
||||
static const yytype_uint8 yyrline[] =
|
||||
{
|
||||
0, 81, 81, 82, 85, 86, 89, 90, 91, 92,
|
||||
93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
|
||||
103, 104, 105, 106, 107, 108, 109, 110, 111, 114,
|
||||
115, 116, 117, 119, 120, 121, 122, 123, 124, 127,
|
||||
131, 132, 134, 135, 136, 137, 138, 139, 140, 143,
|
||||
144, 148, 149, 153, 154, 158, 163, 168, 174, 181,
|
||||
188, 195, 199, 200, 201, 202, 203, 204, 205, 206,
|
||||
210, 211, 212, 216, 217, 221, 222
|
||||
0, 83, 83, 84, 87, 88, 91, 92, 93, 94,
|
||||
95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
|
||||
105, 106, 107, 108, 109, 110, 111, 112, 113, 116,
|
||||
117, 118, 119, 121, 122, 123, 124, 125, 126, 129,
|
||||
133, 134, 136, 137, 138, 139, 140, 141, 142, 145,
|
||||
146, 150, 151, 155, 156, 160, 165, 170, 176, 183,
|
||||
190, 197, 201, 202, 203, 204, 205, 206, 207, 208,
|
||||
212, 213, 214, 218, 219, 223, 224
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -1007,45 +1005,31 @@ yydestruct (const char *yymsg,
|
||||
switch (yykind)
|
||||
{
|
||||
case YYSYMBOL_exp: /* exp */
|
||||
#line 73 "Expression.y"
|
||||
{ delete ((*yyvaluep).expr); }
|
||||
#line 1010 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case YYSYMBOL_num: /* num */
|
||||
#line 73 "Expression.y"
|
||||
{ delete ((*yyvaluep).expr); }
|
||||
#line 1016 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case YYSYMBOL_args: /* args */
|
||||
#line 75 "Expression.y"
|
||||
{ std::vector<Expression*>::const_iterator i = ((*yyvaluep).arguments).begin(); while (i != ((*yyvaluep).arguments).end()) { delete *i; ++i; } }
|
||||
#line 1022 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case YYSYMBOL_range: /* range */
|
||||
#line 73 "Expression.y"
|
||||
{ delete ((*yyvaluep).expr); }
|
||||
#line 1028 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case YYSYMBOL_unit_exp: /* unit_exp */
|
||||
#line 73 "Expression.y"
|
||||
{ delete ((*yyvaluep).expr); }
|
||||
#line 1034 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case YYSYMBOL_indexer: /* indexer */
|
||||
#line 74 "Expression.y"
|
||||
{ delete ((*yyvaluep).component); }
|
||||
#line 1040 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case YYSYMBOL_indexable: /* indexable */
|
||||
#line 73 "Expression.y"
|
||||
{ delete ((*yyvaluep).expr); }
|
||||
#line 1046 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1313,356 +1297,243 @@ yyreduce:
|
||||
switch (yyn)
|
||||
{
|
||||
case 2: /* input: exp */
|
||||
#line 81 "Expression.y"
|
||||
{ ScanResult = std::unique_ptr<Expression>(yyvsp[0].expr); valueExpression = true; }
|
||||
#line 1316 "Expression.tab.c"
|
||||
{ ScanResult = std::unique_ptr<Expression>((yyvsp[0].expr)); valueExpression = true; }
|
||||
break;
|
||||
|
||||
case 3: /* input: unit_exp */
|
||||
#line 82 "Expression.y"
|
||||
{ ScanResult = std::unique_ptr<Expression>(yyvsp[0].expr); unitExpression = true; }
|
||||
#line 1322 "Expression.tab.c"
|
||||
{ ScanResult = std::unique_ptr<Expression>((yyvsp[0].expr)); unitExpression = true; }
|
||||
break;
|
||||
|
||||
case 4: /* unit_num: num unit_exp */
|
||||
#line 85 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-1].expr), OperatorExpression::UNIT, (yyvsp[0].expr)); }
|
||||
#line 1328 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 5: /* unit_num: num us_building_unit num us_building_unit */
|
||||
#line 86 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, new OperatorExpression(DocumentObject, (yyvsp[-3].expr), OperatorExpression::UNIT, (yyvsp[-2].expr)), OperatorExpression::ADD, new OperatorExpression(DocumentObject, (yyvsp[-1].expr), OperatorExpression::UNIT, (yyvsp[0].expr)));}
|
||||
#line 1334 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 6: /* exp: num */
|
||||
#line 89 "Expression.y"
|
||||
{ (yyval.expr) = (yyvsp[0].expr); }
|
||||
#line 1340 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 7: /* exp: unit_num */
|
||||
#line 90 "Expression.y"
|
||||
{ (yyval.expr) = (yyvsp[0].expr); }
|
||||
#line 1346 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 8: /* exp: STRING */
|
||||
#line 91 "Expression.y"
|
||||
{ (yyval.expr) = new StringExpression(DocumentObject, (yyvsp[0].string)); }
|
||||
#line 1352 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 9: /* exp: identifier */
|
||||
#line 92 "Expression.y"
|
||||
{ (yyval.expr) = new VariableExpression(DocumentObject, (yyvsp[0].path)); }
|
||||
#line 1358 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 10: /* exp: MINUSSIGN exp */
|
||||
#line 93 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[0].expr), OperatorExpression::NEG, new NumberExpression(DocumentObject, Quantity(-1))); }
|
||||
#line 1364 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 11: /* exp: '+' exp */
|
||||
#line 94 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[0].expr), OperatorExpression::POS, new NumberExpression(DocumentObject, Quantity(1))); }
|
||||
#line 1370 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 12: /* exp: exp '+' exp */
|
||||
#line 95 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::ADD, (yyvsp[0].expr)); }
|
||||
#line 1376 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 13: /* exp: exp MINUSSIGN exp */
|
||||
#line 96 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::SUB, (yyvsp[0].expr)); }
|
||||
#line 1382 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 14: /* exp: exp '*' exp */
|
||||
#line 97 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::MUL, (yyvsp[0].expr)); }
|
||||
#line 1388 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 15: /* exp: exp '/' exp */
|
||||
#line 98 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::DIV, (yyvsp[0].expr)); }
|
||||
#line 1394 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 16: /* exp: exp '%' exp */
|
||||
#line 99 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::MOD, (yyvsp[0].expr)); }
|
||||
#line 1400 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 17: /* exp: exp '/' unit_exp */
|
||||
#line 100 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::DIV, (yyvsp[0].expr)); }
|
||||
#line 1406 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 18: /* exp: exp '^' exp */
|
||||
#line 101 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::POW, (yyvsp[0].expr)); }
|
||||
#line 1412 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 19: /* exp: exp EQ exp */
|
||||
#line 102 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::EQ, (yyvsp[0].expr)); }
|
||||
#line 1418 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 20: /* exp: exp NEQ exp */
|
||||
#line 103 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::NEQ, (yyvsp[0].expr)); }
|
||||
#line 1424 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 21: /* exp: exp LT exp */
|
||||
#line 104 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::LT, (yyvsp[0].expr)); }
|
||||
#line 1430 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 22: /* exp: exp GT exp */
|
||||
#line 105 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::GT, (yyvsp[0].expr)); }
|
||||
#line 1436 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 23: /* exp: exp GTE exp */
|
||||
#line 106 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::GTE, (yyvsp[0].expr)); }
|
||||
#line 1442 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 24: /* exp: exp LTE exp */
|
||||
#line 107 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::LTE, (yyvsp[0].expr)); }
|
||||
#line 1448 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 25: /* exp: indexable */
|
||||
#line 108 "Expression.y"
|
||||
{ (yyval.expr) = (yyvsp[0].expr); }
|
||||
#line 1454 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 26: /* exp: FUNC args ')' */
|
||||
#line 109 "Expression.y"
|
||||
{ (yyval.expr) = new FunctionExpression(DocumentObject, (yyvsp[-2].func).first, std::move((yyvsp[-2].func).second), (yyvsp[-1].arguments));}
|
||||
#line 1460 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 27: /* exp: exp '?' exp ':' exp */
|
||||
#line 110 "Expression.y"
|
||||
{ (yyval.expr) = new ConditionalExpression(DocumentObject, (yyvsp[-4].expr), (yyvsp[-2].expr), (yyvsp[0].expr)); }
|
||||
#line 1466 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 28: /* exp: '(' exp ')' */
|
||||
#line 111 "Expression.y"
|
||||
{ (yyval.expr) = (yyvsp[-1].expr); }
|
||||
#line 1472 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 29: /* num: ONE */
|
||||
#line 114 "Expression.y"
|
||||
{ (yyval.expr) = new NumberExpression(DocumentObject, Quantity((yyvsp[0].fvalue))); }
|
||||
#line 1478 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 30: /* num: NUM */
|
||||
#line 115 "Expression.y"
|
||||
{ (yyval.expr) = new NumberExpression(DocumentObject, Quantity((yyvsp[0].fvalue))); }
|
||||
#line 1484 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 31: /* num: INTEGER */
|
||||
#line 116 "Expression.y"
|
||||
{ (yyval.expr) = new NumberExpression(DocumentObject, Quantity((double)(yyvsp[0].ivalue))); }
|
||||
#line 1490 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 32: /* num: CONSTANT */
|
||||
#line 117 "Expression.y"
|
||||
{ (yyval.expr) = new ConstantExpression(DocumentObject, (yyvsp[0].constant).name, Quantity((yyvsp[0].constant).fvalue)); }
|
||||
#line 1496 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 33: /* args: exp */
|
||||
#line 119 "Expression.y"
|
||||
{ (yyval.arguments).push_back((yyvsp[0].expr)); }
|
||||
#line 1502 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 34: /* args: range */
|
||||
#line 120 "Expression.y"
|
||||
{ (yyval.arguments).push_back((yyvsp[0].expr)); }
|
||||
#line 1508 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 35: /* args: args ',' exp */
|
||||
#line 121 "Expression.y"
|
||||
{ (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); }
|
||||
#line 1514 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 36: /* args: args ';' exp */
|
||||
#line 122 "Expression.y"
|
||||
{ (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); }
|
||||
#line 1520 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 37: /* args: args ',' range */
|
||||
#line 123 "Expression.y"
|
||||
{ (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); }
|
||||
#line 1526 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 38: /* args: args ';' range */
|
||||
#line 124 "Expression.y"
|
||||
{ (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); }
|
||||
#line 1532 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 39: /* range: id_or_cell ':' id_or_cell */
|
||||
#line 127 "Expression.y"
|
||||
{ (yyval.expr) = new RangeExpression(DocumentObject, (yyvsp[-2].string), (yyvsp[0].string)); }
|
||||
#line 1538 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 40: /* us_building_unit: USUNIT */
|
||||
#line 131 "Expression.y"
|
||||
{ (yyval.expr) = new UnitExpression(DocumentObject, (yyvsp[0].quantity).scaler, (yyvsp[0].quantity).unitStr ); }
|
||||
#line 1544 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 41: /* other_unit: UNIT */
|
||||
#line 132 "Expression.y"
|
||||
{ (yyval.expr) = new UnitExpression(DocumentObject, (yyvsp[0].quantity).scaler, (yyvsp[0].quantity).unitStr ); }
|
||||
#line 1550 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 42: /* unit_exp: other_unit */
|
||||
#line 134 "Expression.y"
|
||||
{ (yyval.expr) = (yyvsp[0].expr); }
|
||||
#line 1556 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 43: /* unit_exp: us_building_unit */
|
||||
#line 135 "Expression.y"
|
||||
{ (yyval.expr) = (yyvsp[0].expr); }
|
||||
#line 1562 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 44: /* unit_exp: unit_exp '/' unit_exp */
|
||||
#line 136 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::DIV, (yyvsp[0].expr)); }
|
||||
#line 1568 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 45: /* unit_exp: unit_exp '*' unit_exp */
|
||||
#line 137 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::MUL, (yyvsp[0].expr)); }
|
||||
#line 1574 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 46: /* unit_exp: unit_exp '^' integer */
|
||||
#line 138 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::POW, new NumberExpression(DocumentObject, Quantity((double)(yyvsp[0].ivalue)))); }
|
||||
#line 1580 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 47: /* unit_exp: unit_exp '^' MINUSSIGN integer */
|
||||
#line 139 "Expression.y"
|
||||
{ (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-3].expr), OperatorExpression::POW, new OperatorExpression(DocumentObject, new NumberExpression(DocumentObject, Quantity((double)(yyvsp[0].ivalue))), OperatorExpression::NEG, new NumberExpression(DocumentObject, Quantity(-1)))); }
|
||||
#line 1586 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 48: /* unit_exp: '(' unit_exp ')' */
|
||||
#line 140 "Expression.y"
|
||||
{ (yyval.expr) = (yyvsp[-1].expr); }
|
||||
#line 1592 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 49: /* integer: INTEGER */
|
||||
#line 143 "Expression.y"
|
||||
{ (yyval.ivalue) = (yyvsp[0].ivalue); }
|
||||
#line 1598 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 50: /* integer: ONE */
|
||||
#line 144 "Expression.y"
|
||||
{ (yyval.ivalue) = (yyvsp[0].fvalue); }
|
||||
#line 1604 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 51: /* id_or_cell: IDENTIFIER */
|
||||
#line 148 "Expression.y"
|
||||
{ (yyval.string) = std::move((yyvsp[0].string)); }
|
||||
#line 1610 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 52: /* id_or_cell: CELLADDRESS */
|
||||
#line 149 "Expression.y"
|
||||
{ (yyval.string) = std::move((yyvsp[0].string)); }
|
||||
#line 1616 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 53: /* identifier: id_or_cell */
|
||||
#line 153 "Expression.y"
|
||||
{ (yyval.path) = ObjectIdentifier(DocumentObject); (yyval.path) << ObjectIdentifier::SimpleComponent((yyvsp[0].string)); }
|
||||
#line 1622 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 54: /* identifier: iden */
|
||||
#line 154 "Expression.y"
|
||||
{ (yyval.path) = std::move((yyvsp[0].path)); }
|
||||
#line 1628 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 55: /* iden: '.' STRING '.' id_or_cell */
|
||||
#line 158 "Expression.y"
|
||||
{ /* Path to property of a sub-object of the current object*/
|
||||
(yyval.path) = ObjectIdentifier(DocumentObject,true);
|
||||
(yyval.path).setDocumentObjectName(DocumentObject,false,ObjectIdentifier::String(std::move((yyvsp[-2].string)),true),true);
|
||||
(yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[0].string)));
|
||||
}
|
||||
#line 1638 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 56: /* iden: '.' id_or_cell */
|
||||
#line 163 "Expression.y"
|
||||
{ /* Path to property of the current document object */
|
||||
(yyval.path) = ObjectIdentifier(DocumentObject,true);
|
||||
(yyval.path).setDocumentObjectName(DocumentObject);
|
||||
(yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[0].string)));
|
||||
}
|
||||
#line 1648 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 57: /* iden: object '.' STRING '.' id_or_cell */
|
||||
#line 168 "Expression.y"
|
||||
{ /* Path to property of a sub-object */
|
||||
(yyval.path) = ObjectIdentifier(DocumentObject);
|
||||
(yyval.path).setDocumentObjectName(std::move((yyvsp[-4].string_or_identifier)), true, ObjectIdentifier::String(std::move((yyvsp[-2].string)),true),true);
|
||||
(yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[0].string)));
|
||||
(yyval.path).resolveAmbiguity();
|
||||
}
|
||||
#line 1659 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 58: /* iden: object '.' id_or_cell */
|
||||
#line 174 "Expression.y"
|
||||
{ /* Path to property of a given document object */
|
||||
(yyval.path) = ObjectIdentifier(DocumentObject);
|
||||
(yyvsp[-2].string_or_identifier).checkImport(DocumentObject);
|
||||
@@ -1670,11 +1541,9 @@ yyreduce:
|
||||
(yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[0].string)));
|
||||
(yyval.path).resolveAmbiguity();
|
||||
}
|
||||
#line 1671 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 59: /* iden: document '#' object '.' id_or_cell */
|
||||
#line 181 "Expression.y"
|
||||
{ /* Path to property from an external document, within a named document object */
|
||||
(yyval.path) = ObjectIdentifier(DocumentObject);
|
||||
(yyval.path).setDocumentName(std::move((yyvsp[-4].string_or_identifier)), true);
|
||||
@@ -1682,118 +1551,82 @@ yyreduce:
|
||||
(yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[0].string)));
|
||||
(yyval.path).resolveAmbiguity();
|
||||
}
|
||||
#line 1683 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 60: /* iden: document '#' object '.' STRING '.' id_or_cell */
|
||||
#line 189 "Expression.y"
|
||||
{ (yyval.path) = ObjectIdentifier(DocumentObject);
|
||||
(yyval.path).setDocumentName(std::move((yyvsp[-6].string_or_identifier)), true);
|
||||
(yyval.path).setDocumentObjectName(std::move((yyvsp[-4].string_or_identifier)), true, ObjectIdentifier::String(std::move((yyvsp[-2].string)),true));
|
||||
(yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[0].string)));
|
||||
(yyval.path).resolveAmbiguity();
|
||||
}
|
||||
#line 1694 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 61: /* iden: iden '.' IDENTIFIER */
|
||||
#line 195 "Expression.y"
|
||||
{ (yyval.path)= std::move((yyvsp[-2].path)); (yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[0].string))); }
|
||||
#line 1700 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 62: /* indexer: '[' exp ']' */
|
||||
#line 199 "Expression.y"
|
||||
{ (yyval.component) = Expression::createComponent((yyvsp[-1].expr)); }
|
||||
#line 1706 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 63: /* indexer: '[' exp ':' ']' */
|
||||
#line 200 "Expression.y"
|
||||
{ (yyval.component) = Expression::createComponent((yyvsp[-2].expr),0,0,true); }
|
||||
#line 1712 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 64: /* indexer: '[' ':' exp ']' */
|
||||
#line 201 "Expression.y"
|
||||
{ (yyval.component) = Expression::createComponent(0,(yyvsp[-1].expr)); }
|
||||
#line 1718 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 65: /* indexer: '[' ':' ':' exp ']' */
|
||||
#line 202 "Expression.y"
|
||||
{ (yyval.component) = Expression::createComponent(0,0,(yyvsp[-1].expr)); }
|
||||
#line 1724 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 66: /* indexer: '[' exp ':' exp ']' */
|
||||
#line 203 "Expression.y"
|
||||
{ (yyval.component) = Expression::createComponent((yyvsp[-3].expr),(yyvsp[-1].expr));}
|
||||
#line 1730 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 67: /* indexer: '[' exp ':' ':' exp ']' */
|
||||
#line 204 "Expression.y"
|
||||
{ (yyval.component) = Expression::createComponent((yyvsp[-4].expr),0,(yyvsp[-1].expr)); }
|
||||
#line 1736 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 68: /* indexer: '[' ':' exp ':' exp ']' */
|
||||
#line 205 "Expression.y"
|
||||
{ (yyval.component) = Expression::createComponent(0,(yyvsp[-3].expr),(yyvsp[-1].expr)); }
|
||||
#line 1742 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 69: /* indexer: '[' exp ':' exp ':' exp ']' */
|
||||
#line 206 "Expression.y"
|
||||
{ (yyval.component) = Expression::createComponent((yyvsp[-5].expr),(yyvsp[-3].expr),(yyvsp[-1].expr));}
|
||||
#line 1748 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 70: /* indexable: identifier indexer */
|
||||
#line 210 "Expression.y"
|
||||
{ (yyval.expr) = new VariableExpression(DocumentObject,(yyvsp[-1].path)); (yyval.expr)->addComponent((yyvsp[0].component)); }
|
||||
#line 1754 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 71: /* indexable: indexable indexer */
|
||||
#line 211 "Expression.y"
|
||||
{ (yyvsp[-1].expr)->addComponent(std::move((yyvsp[0].component))); (yyval.expr) = (yyvsp[-1].expr); }
|
||||
#line 1760 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 72: /* indexable: indexable '.' IDENTIFIER */
|
||||
#line 212 "Expression.y"
|
||||
{ (yyvsp[-2].expr)->addComponent(Expression::createComponent((yyvsp[0].string))); (yyval.expr) = (yyvsp[-2].expr); }
|
||||
#line 1766 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 73: /* document: STRING */
|
||||
#line 216 "Expression.y"
|
||||
{ (yyval.string_or_identifier) = ObjectIdentifier::String(std::move((yyvsp[0].string)), true); }
|
||||
#line 1772 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 74: /* document: IDENTIFIER */
|
||||
#line 217 "Expression.y"
|
||||
{ (yyval.string_or_identifier) = ObjectIdentifier::String(std::move((yyvsp[0].string)), false, true);}
|
||||
#line 1778 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 75: /* object: STRING */
|
||||
#line 221 "Expression.y"
|
||||
{ (yyval.string_or_identifier) = ObjectIdentifier::String(std::move((yyvsp[0].string)), true); }
|
||||
#line 1784 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
case 76: /* object: id_or_cell */
|
||||
#line 222 "Expression.y"
|
||||
{ (yyval.string_or_identifier) = ObjectIdentifier::String(std::move((yyvsp[0].string)), false);}
|
||||
#line 1790 "Expression.tab.c"
|
||||
break;
|
||||
|
||||
|
||||
#line 1794 "Expression.tab.c"
|
||||
|
||||
default: break;
|
||||
}
|
||||
@@ -1986,5 +1819,4 @@ yyreturnlab:
|
||||
return yyresult;
|
||||
}
|
||||
|
||||
#line 225 "Expression.y"
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
// clang-format off
|
||||
/* A Bison parser, made by GNU Bison 3.8.2. */
|
||||
|
||||
/* Bison interface for Yacc-like parsers in C
|
||||
@@ -37,7 +38,8 @@
|
||||
especially those whose name start with YY_ or yy_. They are
|
||||
private implementation details that can be changed or removed. */
|
||||
|
||||
#pragma once
|
||||
#ifndef YY_YY_EXPRESSION_TAB_H_INCLUDED
|
||||
# define YY_YY_EXPRESSION_TAB_H_INCLUDED
|
||||
/* Debug traces. */
|
||||
#ifndef YYDEBUG
|
||||
# define YYDEBUG 0
|
||||
@@ -90,3 +92,6 @@ extern YYSTYPE yylval;
|
||||
|
||||
|
||||
int yyparse (void);
|
||||
|
||||
|
||||
#endif /* !YY_YY_EXPRESSION_TAB_H_INCLUDED */
|
||||
|
||||
@@ -456,6 +456,8 @@ App.Units.Minute = App.Units.Quantity('min')
|
||||
App.Units.Hour = App.Units.Quantity('h')
|
||||
|
||||
App.Units.Ampere = App.Units.Quantity('A')
|
||||
App.Units.NanoAmpere = App.Units.Quantity('nA')
|
||||
App.Units.MicroAmpere = App.Units.Quantity('uA')
|
||||
App.Units.MilliAmpere = App.Units.Quantity('mA')
|
||||
App.Units.KiloAmpere = App.Units.Quantity('kA')
|
||||
App.Units.MegaAmpere = App.Units.Quantity('MA')
|
||||
@@ -464,8 +466,10 @@ App.Units.Kelvin = App.Units.Quantity('K')
|
||||
App.Units.MilliKelvin = App.Units.Quantity('mK')
|
||||
App.Units.MicroKelvin = App.Units.Quantity('uK')
|
||||
|
||||
App.Units.MilliMole = App.Units.Quantity('mmol')
|
||||
App.Units.Mole = App.Units.Quantity('mol')
|
||||
App.Units.NanoMole = App.Units.Quantity('nmol')
|
||||
App.Units.MicroMole = App.Units.Quantity('umol')
|
||||
App.Units.MilliMole = App.Units.Quantity('mmol')
|
||||
|
||||
App.Units.Candela = App.Units.Quantity('cd')
|
||||
|
||||
@@ -511,6 +515,8 @@ App.Units.KSI = App.Units.Quantity('ksi')
|
||||
App.Units.MPSI = App.Units.Quantity('Mpsi')
|
||||
|
||||
App.Units.Watt = App.Units.Quantity('W')
|
||||
App.Units.NanoWatt = App.Units.Quantity('nW')
|
||||
App.Units.MicroWatt = App.Units.Quantity('uW')
|
||||
App.Units.MilliWatt = App.Units.Quantity('mW')
|
||||
App.Units.KiloWatt = App.Units.Quantity('kW')
|
||||
App.Units.VoltAmpere = App.Units.Quantity('VA')
|
||||
|
||||
@@ -328,7 +328,7 @@ Base::Placement GeoFeature::getGlobalPlacement(App::DocumentObject* targetObj,
|
||||
Base::Placement GeoFeature::getGlobalPlacement(App::DocumentObject* targetObj,
|
||||
App::PropertyXLinkSub* prop)
|
||||
{
|
||||
if (!targetObj || !prop) {
|
||||
if (!prop) {
|
||||
return Base::Placement();
|
||||
}
|
||||
|
||||
@@ -357,3 +357,4 @@ Base::Placement GeoFeature::getGlobalPlacement(const DocumentObject* obj)
|
||||
return placementProperty->getValue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1097,6 +1097,16 @@ public:
|
||||
guard.tryInvoke();
|
||||
}
|
||||
|
||||
template<std::predicate<const T&> F>
|
||||
int removeIf(F f) {
|
||||
ListT vals = _lValueList;
|
||||
size_t removed = std::erase_if(vals, f);
|
||||
if (removed > 0) {
|
||||
setValues(std::move(vals));
|
||||
}
|
||||
return static_cast<int>(removed);
|
||||
}
|
||||
|
||||
protected:
|
||||
void setPyValues(const std::vector<PyObject*>& vals, const std::vector<int>& indices) override
|
||||
{
|
||||
|
||||
@@ -295,7 +295,7 @@ public:
|
||||
|
||||
/// Helper function to return linked objects using an std::inserter
|
||||
template<class T>
|
||||
void getLinkedObjects(T& inserter, bool all = false) const
|
||||
void getLinkedObjects(T inserter, bool all = false) const
|
||||
{
|
||||
std::vector<App::DocumentObject*> ret;
|
||||
getLinks(ret, all);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
<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 type="unfinished">Stores the last user choice of whether to apply CopyOnChange setup to all links
|
||||
that reference the same configurable object</translation>
|
||||
<translation>Stórálann sé rogha dheireanach an úsáideora maidir le cibé acu an gcuirfear socrú CopyOnChange i bhfeidhm ar gach nasc a thagraíonn don réad inchumraithe céanna</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -35,9 +34,9 @@ that reference the same configurable object</translation>
|
||||
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.
|
||||
</source>
|
||||
<translation type="unfinished">
|
||||
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.
|
||||
<translation>
|
||||
Moltar don úsáideoir cliceáil ar dheis ar fhréamh an doiciméid agus Marcáil le hathríomh a roghnú.
|
||||
Ansin, ba chóir don úsáideoir cliceáil ar an gcnaipe Athnuachan sa phríomhbharra uirlisí.
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
@@ -46,32 +45,32 @@ The user should then click the Refresh button in the main toolbar.
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="265"/>
|
||||
<source>X-axis</source>
|
||||
<translation type="unfinished">X-axis</translation>
|
||||
<translation>Ais-X</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="266"/>
|
||||
<source>Y-axis</source>
|
||||
<translation type="unfinished">Y-axis</translation>
|
||||
<translation>Ais-Y</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="267"/>
|
||||
<source>Z-axis</source>
|
||||
<translation type="unfinished">Z-axis</translation>
|
||||
<translation>Ais-Z</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="268"/>
|
||||
<source>XY-plane</source>
|
||||
<translation type="unfinished">XY-plane</translation>
|
||||
<translation>Plána XY</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="269"/>
|
||||
<source>XZ-plane</source>
|
||||
<translation type="unfinished">XZ-plane</translation>
|
||||
<translation>XZ-eitleán</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="270"/>
|
||||
<source>YZ-plane</source>
|
||||
<translation type="unfinished">YZ-plane</translation>
|
||||
<translation>YZ-eitleán</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../Datums.cpp" line="271"/>
|
||||
|
||||
@@ -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>>
|
||||
|
||||
@@ -359,6 +359,8 @@ const Quantity Quantity::Minute ( 60.0 , Unit::
|
||||
const Quantity Quantity::Hour ( 3600.0 , Unit::TimeSpan );
|
||||
|
||||
const Quantity Quantity::Ampere ( 1.0 , Unit::ElectricCurrent );
|
||||
const Quantity Quantity::NanoAmpere ( 1.0e-9 , Unit::ElectricCurrent );
|
||||
const Quantity Quantity::MicroAmpere ( 1.0e-6 , Unit::ElectricCurrent );
|
||||
const Quantity Quantity::MilliAmpere ( 0.001 , Unit::ElectricCurrent );
|
||||
const Quantity Quantity::KiloAmpere ( 1000.0 , Unit::ElectricCurrent );
|
||||
const Quantity Quantity::MegaAmpere ( 1.0e6 , Unit::ElectricCurrent );
|
||||
@@ -367,6 +369,8 @@ const Quantity Quantity::Kelvin ( 1.0 , Unit::
|
||||
const Quantity Quantity::MilliKelvin ( 0.001 , Unit::Temperature );
|
||||
const Quantity Quantity::MicroKelvin ( 0.000001 , Unit::Temperature );
|
||||
|
||||
const Quantity Quantity::NanoMole ( 1e-9 , Unit::AmountOfSubstance );
|
||||
const Quantity Quantity::MicroMole ( 1e-6 , Unit::AmountOfSubstance );
|
||||
const Quantity Quantity::MilliMole ( 0.001 , Unit::AmountOfSubstance );
|
||||
const Quantity Quantity::Mole ( 1.0 , Unit::AmountOfSubstance );
|
||||
|
||||
@@ -417,6 +421,8 @@ const Quantity Quantity::KSI ( psi * 1000 , Unit::
|
||||
const Quantity Quantity::MPSI ( psi * 1000000 , Unit::Pressure );
|
||||
|
||||
const Quantity Quantity::Watt ( 1e+6 , Unit::Power ); // Watt (kg*m^2/s^3)
|
||||
const Quantity Quantity::NanoWatt ( 1e-3 , Unit::Power );
|
||||
const Quantity Quantity::MicroWatt ( 1.0 , Unit::Power );
|
||||
const Quantity Quantity::MilliWatt ( 1e+3 , Unit::Power );
|
||||
const Quantity Quantity::KiloWatt ( 1e+9 , Unit::Power );
|
||||
const Quantity Quantity::VoltAmpere ( 1e+6 , Unit::Power ); // VoltAmpere (kg*m^2/s^3)
|
||||
|
||||
@@ -224,6 +224,8 @@ public:
|
||||
static const Quantity Hour;
|
||||
|
||||
static const Quantity Ampere;
|
||||
static const Quantity NanoAmpere;
|
||||
static const Quantity MicroAmpere;
|
||||
static const Quantity MilliAmpere;
|
||||
static const Quantity KiloAmpere;
|
||||
static const Quantity MegaAmpere;
|
||||
@@ -232,6 +234,8 @@ public:
|
||||
static const Quantity MilliKelvin;
|
||||
static const Quantity MicroKelvin;
|
||||
|
||||
static const Quantity NanoMole;
|
||||
static const Quantity MicroMole;
|
||||
static const Quantity MilliMole;
|
||||
static const Quantity Mole;
|
||||
|
||||
@@ -282,6 +286,8 @@ public:
|
||||
static const Quantity MPSI;
|
||||
|
||||
static const Quantity Watt;
|
||||
static const Quantity NanoWatt;
|
||||
static const Quantity MicroWatt;
|
||||
static const Quantity MilliWatt;
|
||||
static const Quantity KiloWatt;
|
||||
static const Quantity VoltAmpere;
|
||||
|
||||
@@ -93,6 +93,9 @@ CGRP '\,'[0-9][0-9][0-9]
|
||||
"h" yylval = Quantity::Hour; return UNIT; // hour
|
||||
|
||||
"A" yylval = Quantity::Ampere; return UNIT; // Ampere (internal standard electric current)
|
||||
"nA" yylval = Quantity::NanoAmpere; return UNIT; // nano Ampere
|
||||
"uA" yylval = Quantity::MicroAmpere; return UNIT; // micro Ampere
|
||||
"\xC2\xB5""A" yylval = Quantity::MicroAmpere; return UNIT; // micro Ampere
|
||||
"mA" yylval = Quantity::MilliAmpere; return UNIT; // milli Ampere
|
||||
"kA" yylval = Quantity::KiloAmpere; return UNIT; // kilo Ampere
|
||||
"MA" yylval = Quantity::MegaAmpere; return UNIT; // mega Ampere
|
||||
@@ -103,6 +106,9 @@ CGRP '\,'[0-9][0-9][0-9]
|
||||
"uK" yylval = Quantity::MicroKelvin; return UNIT; // micro Kelvin
|
||||
|
||||
"mol" yylval = Quantity::Mole; return UNIT; // Mole (internal standard amount of substance)
|
||||
"nmol" yylval = Quantity::NanoMole; return UNIT; // Nano Mole
|
||||
"umol" yylval = Quantity::MicroMole; return UNIT; // Micro Mole
|
||||
"\xC2\xB5mol" yylval = Quantity::MicroMole; return UNIT; // Micro Mole
|
||||
"mmol" yylval = Quantity::MilliMole; return UNIT; // Milli Mole
|
||||
|
||||
"cd" yylval = Quantity::Candela; return UNIT; // Candela (internal standard luminous intensity)
|
||||
@@ -151,6 +157,9 @@ CGRP '\,'[0-9][0-9][0-9]
|
||||
"Mpsi" yylval = Quantity::MPSI; return UNIT; // 1000 ksi
|
||||
|
||||
"W" yylval = Quantity::Watt; return UNIT; // Watt (kg*m^2/s^3)
|
||||
"nW" yylval = Quantity::NanoWatt; return UNIT; // milli Watt
|
||||
"uW" yylval = Quantity::MicroWatt; return UNIT; // micro Watt
|
||||
"\xC2\xB5W" yylval = Quantity::MicroWatt; return UNIT; // micro Watt
|
||||
"mW" yylval = Quantity::MilliWatt; return UNIT; // milli Watt
|
||||
"kW" yylval = Quantity::KiloWatt; return UNIT; // kilo Watt
|
||||
"VA" yylval = Quantity::VoltAmpere; return UNIT; // VoltAmpere (kg*m^2/s^3)
|
||||
|
||||
+330
-457
File diff suppressed because it is too large
Load Diff
@@ -70,13 +70,11 @@
|
||||
|
||||
|
||||
/* First part of user prologue. */
|
||||
#line 26 "Quantity.y"
|
||||
|
||||
#define YYSTYPE Quantity
|
||||
#define yyparse Quantity_yyparse
|
||||
#define yyerror Quantity_yyerror
|
||||
|
||||
#line 77 "Quantity.tab.c"
|
||||
|
||||
# ifndef YY_CAST
|
||||
# ifdef __cplusplus
|
||||
@@ -1225,223 +1223,150 @@ yyreduce:
|
||||
switch (yyn)
|
||||
{
|
||||
case 2: /* input: %empty */
|
||||
#line 45 "Quantity.y"
|
||||
{ QuantResult = Quantity(std::numeric_limits<double>::min()); /* empty input */ }
|
||||
#line 1228 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 3: /* input: num */
|
||||
#line 46 "Quantity.y"
|
||||
{ QuantResult = yyvsp[0]; }
|
||||
#line 1234 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 4: /* input: unit */
|
||||
#line 47 "Quantity.y"
|
||||
{ QuantResult = yyvsp[0]; }
|
||||
#line 1240 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 5: /* input: quantity */
|
||||
#line 48 "Quantity.y"
|
||||
{ QuantResult = yyvsp[0]; }
|
||||
#line 1246 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 6: /* input: quantity quantity */
|
||||
#line 49 "Quantity.y"
|
||||
{ QuantResult = yyvsp[-1] + yyvsp[0]; }
|
||||
#line 1252 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 7: /* input: quantity quantity quantity */
|
||||
#line 50 "Quantity.y"
|
||||
{ QuantResult = yyvsp[-2] + yyvsp[-1] + yyvsp[0]; }
|
||||
#line 1258 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 8: /* num: NUM */
|
||||
#line 52 "Quantity.y"
|
||||
{ yyval = yyvsp[0]; }
|
||||
#line 1264 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 9: /* num: ONE */
|
||||
#line 53 "Quantity.y"
|
||||
{ yyval = yyvsp[0]; }
|
||||
#line 1270 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 10: /* num: num '+' num */
|
||||
#line 54 "Quantity.y"
|
||||
{ yyval = Quantity(yyvsp[-2].getValue() + yyvsp[0].getValue()); }
|
||||
#line 1276 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 11: /* num: num MINUSSIGN num */
|
||||
#line 55 "Quantity.y"
|
||||
{ yyval = Quantity(yyvsp[-2].getValue() - yyvsp[0].getValue()); }
|
||||
#line 1282 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 12: /* num: num '*' num */
|
||||
#line 56 "Quantity.y"
|
||||
{ yyval = Quantity(yyvsp[-2].getValue() * yyvsp[0].getValue()); }
|
||||
#line 1288 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 13: /* num: num '/' num */
|
||||
#line 57 "Quantity.y"
|
||||
{ yyval = Quantity(yyvsp[-2].getValue() / yyvsp[0].getValue()); }
|
||||
#line 1294 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 14: /* num: MINUSSIGN num */
|
||||
#line 58 "Quantity.y"
|
||||
{ yyval = Quantity(-yyvsp[0].getValue()); }
|
||||
#line 1300 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 15: /* num: num '^' num */
|
||||
#line 59 "Quantity.y"
|
||||
{ yyval = Quantity(pow(yyvsp[-2].getValue(), yyvsp[0].getValue())); }
|
||||
#line 1306 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 16: /* num: '(' num ')' */
|
||||
#line 60 "Quantity.y"
|
||||
{ yyval = yyvsp[-1]; }
|
||||
#line 1312 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 17: /* num: ACOS '(' num ')' */
|
||||
#line 61 "Quantity.y"
|
||||
{ yyval = Quantity(acos(yyvsp[-1].getValue())); }
|
||||
#line 1318 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 18: /* num: ASIN '(' num ')' */
|
||||
#line 62 "Quantity.y"
|
||||
{ yyval = Quantity(asin(yyvsp[-1].getValue())); }
|
||||
#line 1324 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 19: /* num: ATAN '(' num ')' */
|
||||
#line 63 "Quantity.y"
|
||||
{ yyval = Quantity(atan(yyvsp[-1].getValue())); }
|
||||
#line 1330 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 20: /* num: ABS '(' num ')' */
|
||||
#line 64 "Quantity.y"
|
||||
{ yyval = Quantity(fabs(yyvsp[-1].getValue())); }
|
||||
#line 1336 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 21: /* num: EXP '(' num ')' */
|
||||
#line 65 "Quantity.y"
|
||||
{ yyval = Quantity(exp(yyvsp[-1].getValue())); }
|
||||
#line 1342 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 22: /* num: LOG '(' num ')' */
|
||||
#line 66 "Quantity.y"
|
||||
{ yyval = Quantity(log(yyvsp[-1].getValue())); }
|
||||
#line 1348 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 23: /* num: LOG10 '(' num ')' */
|
||||
#line 67 "Quantity.y"
|
||||
{ yyval = Quantity(log10(yyvsp[-1].getValue())); }
|
||||
#line 1354 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 24: /* num: SIN '(' num ')' */
|
||||
#line 68 "Quantity.y"
|
||||
{ yyval = Quantity(sin(yyvsp[-1].getValue())); }
|
||||
#line 1360 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 25: /* num: SINH '(' num ')' */
|
||||
#line 69 "Quantity.y"
|
||||
{ yyval = Quantity(sinh(yyvsp[-1].getValue())); }
|
||||
#line 1366 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 26: /* num: TAN '(' num ')' */
|
||||
#line 70 "Quantity.y"
|
||||
{ yyval = Quantity(tan(yyvsp[-1].getValue())); }
|
||||
#line 1372 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 27: /* num: TANH '(' num ')' */
|
||||
#line 71 "Quantity.y"
|
||||
{ yyval = Quantity(tanh(yyvsp[-1].getValue())); }
|
||||
#line 1378 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 28: /* num: SQRT '(' num ')' */
|
||||
#line 72 "Quantity.y"
|
||||
{ yyval = Quantity(sqrt(yyvsp[-1].getValue())); }
|
||||
#line 1384 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 29: /* num: COS '(' num ')' */
|
||||
#line 73 "Quantity.y"
|
||||
{ yyval = Quantity(cos(yyvsp[-1].getValue())); }
|
||||
#line 1390 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 30: /* unit: UNIT */
|
||||
#line 76 "Quantity.y"
|
||||
{ yyval = yyvsp[0]; }
|
||||
#line 1396 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 31: /* unit: ONE '/' unit */
|
||||
#line 77 "Quantity.y"
|
||||
{ yyval = Quantity(1.0)/yyvsp[0]; }
|
||||
#line 1402 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 32: /* unit: unit '*' unit */
|
||||
#line 78 "Quantity.y"
|
||||
{ yyval = yyvsp[-2] * yyvsp[0]; }
|
||||
#line 1408 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 33: /* unit: unit '/' unit */
|
||||
#line 79 "Quantity.y"
|
||||
{ yyval = yyvsp[-2] / yyvsp[0]; }
|
||||
#line 1414 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 34: /* unit: unit '^' num */
|
||||
#line 80 "Quantity.y"
|
||||
{ yyval = yyvsp[-2].pow (yyvsp[0]); }
|
||||
#line 1420 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 35: /* unit: '(' unit ')' */
|
||||
#line 81 "Quantity.y"
|
||||
{ yyval = yyvsp[-1]; }
|
||||
#line 1426 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 36: /* quantity: num unit */
|
||||
#line 83 "Quantity.y"
|
||||
{ yyval = yyvsp[-1]*yyvsp[0]; }
|
||||
#line 1432 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
case 37: /* quantity: num '/' unit */
|
||||
#line 84 "Quantity.y"
|
||||
{ yyval = Quantity(yyvsp[-2])/yyvsp[0]; }
|
||||
#line 1438 "Quantity.tab.c"
|
||||
break;
|
||||
|
||||
|
||||
#line 1442 "Quantity.tab.c"
|
||||
|
||||
default: break;
|
||||
}
|
||||
@@ -1633,5 +1558,3 @@ yyreturnlab:
|
||||
|
||||
return yyresult;
|
||||
}
|
||||
|
||||
#line 87 "Quantity.y"
|
||||
|
||||
@@ -6,52 +6,52 @@
|
||||
<message>
|
||||
<location filename="../../UnitsSchemasData.h" line="90"/>
|
||||
<source>Standard (mm, kg, s, °)</source>
|
||||
<translation type="unfinished">Standard (mm, kg, s, °)</translation>
|
||||
<translation>Caighdeánach (mm, kg, s, °)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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="593"/>
|
||||
<source>US customary (in, lb)</source>
|
||||
<translation type="unfinished">US customary (in, lb)</translation>
|
||||
<translation>Gnáthnós SAM (in, lb)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../UnitsSchemasData.h" line="642"/>
|
||||
<source>Imperial for Civil Eng (ft, lb, mph)</source>
|
||||
<translation type="unfinished">Imperial for Civil Eng (ft, lb, mph)</translation>
|
||||
<translation>Impiriúil d'Innealtóireacht Shibhialta (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>Deachúil impiriúil (orlach, punt)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../UnitsSchemasData.h" line="554"/>
|
||||
<source>Building Euro (cm, m², m³)</source>
|
||||
<translation type="unfinished">Building Euro (cm, m², m³)</translation>
|
||||
<translation>Foirgneamh Euro (cm, m², m³)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>Foirgneamh SAM (troigh-orlach, troigh chearnach, cft)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>Páirteanna beaga méadracha & CNC (mm, mm/nóim)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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="62"/>
|
||||
<source>Meter decimal (m, m², m³)</source>
|
||||
<translation type="unfinished">Meter decimal (m, m², m³)</translation>
|
||||
<translation>Deicheamh méadar (m, m², m³)</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -102,6 +102,7 @@ constexpr auto unitSpecs = std::to_array<UnitSpec>({
|
||||
{ "VacuumPermittivity" , { -3, -1, 4, 2 } },
|
||||
{ "Velocity" , { 1, 0, -1 } },
|
||||
{ "Volume" , { 3 } },
|
||||
{ "Concentration" , { -3, 0, 0, 0, 0, 1 } },
|
||||
{ "VolumeChargeDensity" , { -3, 0, 1, 1 } },
|
||||
{ "VolumeFlowRate" , { 3, 0, -1 } },
|
||||
{ "VolumetricThermalExpansionCoefficient" , { 0, 0, 0, 0, -1 } },
|
||||
@@ -327,6 +328,7 @@ constexpr Unit Unit::Angle = make("Angle"
|
||||
constexpr Unit Unit::Acceleration = make("Acceleration" );
|
||||
constexpr Unit Unit::AngleOfFriction = make("Angle" );
|
||||
constexpr Unit Unit::Area = make("Area" );
|
||||
constexpr Unit Unit::Concentration = make("Concentration" );
|
||||
constexpr Unit Unit::CompressiveStrength = make("Pressure" );
|
||||
constexpr Unit Unit::CurrentDensity = make("CurrentDensity" );
|
||||
constexpr Unit Unit::Density = make("Density" );
|
||||
|
||||
@@ -120,6 +120,7 @@ public:
|
||||
static const Unit AngleOfFriction;
|
||||
static const Unit Area;
|
||||
static const Unit CompressiveStrength;
|
||||
static const Unit Concentration;
|
||||
static const Unit CurrentDensity;
|
||||
static const Unit Density;
|
||||
static const Unit DissipationRate;
|
||||
|
||||
@@ -112,6 +112,12 @@ inline const UnitsSchemaSpec s3
|
||||
{ "Angle", {
|
||||
{ 0 , "°" , 1.0 }}
|
||||
},
|
||||
{ "ElectricCurrent", {
|
||||
{ 1e-6 , "nA" , 1e-9 },
|
||||
{ 1e-3 , "\xC2\xB5""A", 1e-6 },
|
||||
{ 1 , "mA" , 1e-3 },
|
||||
{ 0 , "A" , 1.0 }}
|
||||
},
|
||||
{ "Mass", {
|
||||
{ 1e-6 , "\xC2\xB5g" , 1e-9 },
|
||||
{ 1e-3 , "mg" , 1e-6 },
|
||||
@@ -124,6 +130,17 @@ inline const UnitsSchemaSpec s3
|
||||
{ 1.0 , "kg/cm^3" , 1e-3 },
|
||||
{ 0 , "kg/mm^3" , 1.0 }}
|
||||
},
|
||||
{ "Concentration", {
|
||||
{ 1e-9 , "\xC2\xB5mol/l", 1e-12 },
|
||||
{ 1e-6 , "mmol/l" , 1e-9 },
|
||||
{ 0 , "mol/l" , 1e-6 }}
|
||||
},
|
||||
{ "AmountOfSubstance", {
|
||||
{ 1e-6 , "nmol" , 1e-9 },
|
||||
{ 1e-3 , "\xC2\xB5mol", 1e-6 },
|
||||
{ 1 , "mmol" , 1e-3 },
|
||||
{ 0 , "mol" , 1.0 }}
|
||||
},
|
||||
{ "ThermalConductivity", {
|
||||
{ 1e6 , "W/m/K" , 1e3 },
|
||||
{ 0 , "W/mm/K" , 1e6 }}
|
||||
@@ -175,6 +192,8 @@ inline const UnitsSchemaSpec s3
|
||||
{ 0 , "MN" , 1e9 }}
|
||||
},
|
||||
{ "Power", {
|
||||
{ 1 , "nW" , 1e-3 },
|
||||
{ 1e3 , "\xC2\xB5W" , 1 },
|
||||
{ 1e6 , "mW" , 1e3 },
|
||||
{ 1e9 , "W" , 1e6 },
|
||||
{ 0 , "kW" , 1e9 }}
|
||||
|
||||
@@ -175,5 +175,5 @@ void* XStrMemoryManager::allocate(XMLSize_t size)
|
||||
|
||||
void XStrMemoryManager::deallocate(void* p)
|
||||
{
|
||||
::operator delete(p);
|
||||
::operator delete(p, static_cast<std::align_val_t>(alignof(XMLCh)));
|
||||
}
|
||||
|
||||
@@ -1426,7 +1426,9 @@ HTML_EXTRA_STYLESHEET = @CMAKE_CURRENT_SOURCE_DIR@/templates/customdoxygen.css
|
||||
# files will be copied as-is; there are no commands or markers available.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_FILES = @CMAKE_CURRENT_SOURCE_DIR@/templates/doxy-boot.js
|
||||
HTML_EXTRA_FILES = @CMAKE_CURRENT_SOURCE_DIR@/templates/doxy-boot.js \
|
||||
@CMAKE_CURRENT_SOURCE_DIR@/templates/Roboto-Regular.woff2 \
|
||||
@CMAKE_CURRENT_SOURCE_DIR@/templates/FiraSans-Regular.woff2
|
||||
|
||||
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
|
||||
# should be rendered with a dark or light theme.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -3,7 +3,7 @@
|
||||
@font-face {
|
||||
font-family: 'Fira Sans';
|
||||
src: url('/fonts/FiraSans-Regular.eot');
|
||||
src: local('☺'), url('/fonts/FiraSans-Regular.woff') format('woff'), url('/fonts/FiraSans-Regular.ttf') format('truetype'), url('/fonts/FiraSans-Regular.svg') format('svg');
|
||||
src: local('☺'), url('FiraSans-Regular.woff2') format('woff2'), url('/fonts/FiraSans-Regular.ttf') format('truetype'), url('/fonts/FiraSans-Regular.svg') format('svg');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
src: url('/fonts/Roboto.eot');
|
||||
src: local('☺'), url('/fonts/Roboto.woff') format('woff'), url('/fonts/Roboto.ttf') format('truetype'), url('/fonts/Roboto.svg') format('svg');
|
||||
src: local('☺'), url('Roboto-Regular.woff2') format('woff2'), url('/fonts/Roboto.ttf') format('truetype'), url('/fonts/Roboto.svg') format('svg');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -370,3 +370,13 @@ body, table, div, p, dl {
|
||||
.sm.sm-dox {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.textblock,
|
||||
.group-page div.contents {
|
||||
max-width: 80ch;
|
||||
margin-left: 0;
|
||||
margin-right: auto;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
+173
-243
@@ -1,8 +1,19 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var path = window.location.pathname || window.location.href || '';
|
||||
var filename = path.split('/').pop() || '';
|
||||
|
||||
$(document).ready(function () {
|
||||
var isGroup = filename.indexOf('group__') !== -1
|
||||
|
||||
if (isGroup) {
|
||||
document.body.classList.add('group-page');
|
||||
}
|
||||
});
|
||||
|
||||
$( document ).ready(function() {
|
||||
|
||||
/*
|
||||
$("div.headertitle").addClass("page-header");
|
||||
$("div.title").addClass("h1");
|
||||
|
||||
@@ -59,14 +70,15 @@ $(document).ready(function () {
|
||||
$('div.fragment.well div.line:first').css('margin-top', '2px');
|
||||
$('div.fragment.well div.line:last').css('margin-bottom', '2px');
|
||||
|
||||
$('table.doxtable').removeClass('doxtable').addClass('table table-striped table-bordered').each(function(){
|
||||
$(this).prepend('<thead></thead>');
|
||||
$(this).find('tbody > tr:first').prependTo($(this).find('thead'));
|
||||
$('table.doxtable').removeClass('doxtable').addClass('table table-striped table-bordered').each(function(){
|
||||
$(this).prepend('<thead></thead>');
|
||||
$(this).find('tbody > tr:first').prependTo($(this).find('thead'));
|
||||
|
||||
$(this).find('td > span.success').parent().addClass('success');
|
||||
$(this).find('td > span.warning').parent().addClass('warning');
|
||||
$(this).find('td > span.danger').parent().addClass('danger');
|
||||
});
|
||||
$(this).find('td > span.success').parent().addClass('success');
|
||||
$(this).find('td > span.warning').parent().addClass('warning');
|
||||
$(this).find('td > span.danger').parent().addClass('danger');
|
||||
});
|
||||
*/
|
||||
|
||||
// source code github links
|
||||
$('li:contains("FreeCAD/src/")').each(function(){
|
||||
@@ -76,6 +88,7 @@ $(document).ready(function () {
|
||||
$(this).html(lk);
|
||||
});
|
||||
|
||||
/*
|
||||
$(".memdoc pre.fragment").contents().unwrap();
|
||||
|
||||
if($('div.fragment.well div.ttc').length > 0)
|
||||
@@ -93,31 +106,32 @@ $(document).ready(function () {
|
||||
$(this).siblings('.memTemplItemLeft').attr('align', 'left');
|
||||
});
|
||||
|
||||
function getOriginalWidthOfImg(img_element) {
|
||||
var t = new Image();
|
||||
t.src = (img_element.getAttribute ? img_element.getAttribute<const char*>("src") : false) || img_element.src;
|
||||
return t.width;
|
||||
}
|
||||
function getOriginalWidthOfImg(img_element) {
|
||||
var t = new Image();
|
||||
t.src = (img_element.getAttribute ? img_element.getAttribute<const char*>("src") : false) || img_element.src;
|
||||
return t.width;
|
||||
}
|
||||
|
||||
$('div.dyncontent').find('img').each(function(){
|
||||
if(getOriginalWidthOfImg($(this)[0]) > $('#content>div.container').width())
|
||||
$(this).css('width', '100%');
|
||||
});
|
||||
$('div.dyncontent').find('img').each(function(){
|
||||
if(getOriginalWidthOfImg($(this)[0]) > $('#content>div.container').width())
|
||||
$(this).css('width', '100%');
|
||||
});
|
||||
*/
|
||||
|
||||
/* responsive search box */
|
||||
/*
|
||||
$('#MSearchBox').parent().remove();
|
||||
|
||||
/* responsive search box */
|
||||
$('#MSearchBox').parent().remove();
|
||||
var nav_container = $('<div class="row"></div>');
|
||||
$('#navrow1').parent().prepend(nav_container);
|
||||
|
||||
var nav_container = $('<div class="row"></div>');
|
||||
$('#navrow1').parent().prepend(nav_container);
|
||||
|
||||
var left_nav = $('<div class="col-md-9"></div>');
|
||||
for (i = 0; i < 6; i++) {
|
||||
var navrow = $('#navrow' + i + ' > ul.tablist').detach();
|
||||
left_nav.append(navrow);
|
||||
$('#navrow' + i).remove();
|
||||
}
|
||||
var right_nav = $('<div class="col-md-3"></div>').append('\
|
||||
var left_nav = $('<div class="col-md-9"></div>');
|
||||
for (i = 0; i < 6; i++) {
|
||||
var navrow = $('#navrow' + i + ' > ul.tablist').detach();
|
||||
left_nav.append(navrow);
|
||||
$('#navrow' + i).remove();
|
||||
}
|
||||
var right_nav = $('<div class="col-md-3"></div>').append('\
|
||||
<div id="search-box" class="input-group">\
|
||||
<div class="input-group-btn">\
|
||||
<button aria-expanded="false" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">\
|
||||
@@ -129,243 +143,159 @@ $(document).ready(function () {
|
||||
<button id="search-close" type="button" class="close" aria-label="Close"><span aria-hidden="true">×</span></button>\
|
||||
<input id="search-field" class="form-control" accesskey="S" onkeydown="searchBox.OnSearchFieldChange(event);" placeholder="Search ..." type="text">\
|
||||
</div>');
|
||||
$(nav_container).append(left_nav);
|
||||
$(nav_container).append(right_nav);
|
||||
$(nav_container).append(left_nav);
|
||||
$(nav_container).append(right_nav);
|
||||
|
||||
$('#MSearchSelectWindow .SelectionMark').remove();
|
||||
var search_selectors = $('#MSearchSelectWindow .SelectItem');
|
||||
for (var i = 0; i < search_selectors.length; i += 1) {
|
||||
var element_a = $('<a href="#"></a>').text($(search_selectors[i]).text());
|
||||
$('#MSearchSelectWindow .SelectionMark').remove();
|
||||
var search_selectors = $('#MSearchSelectWindow .SelectItem');
|
||||
for (var i = 0; i < search_selectors.length; i += 1) {
|
||||
var element_a = $('<a href="#"></a>').text($(search_selectors[i]).text());
|
||||
|
||||
element_a.click(function(){
|
||||
$('#search-box .dropdown-menu li').removeClass('active');
|
||||
$(this).parent().addClass('active');
|
||||
searchBox.OnSelectItem($('#search-box li a').index(this));
|
||||
searchBox.Search();
|
||||
return false;
|
||||
element_a.click(function(){
|
||||
$('#search-box .dropdown-menu li').removeClass('active');
|
||||
$(this).parent().addClass('active');
|
||||
searchBox.OnSelectItem($('#search-box li a').index(this));
|
||||
searchBox.Search();
|
||||
return false;
|
||||
});
|
||||
|
||||
var element = $('<li></li>').append(element_a);
|
||||
$('#search-box .dropdown-menu').append(element);
|
||||
}
|
||||
$('#MSearchSelectWindow').remove();
|
||||
|
||||
$('#search-box .close').click(function (){
|
||||
searchBox.CloseResultsWindow();
|
||||
});
|
||||
|
||||
var element = $('<li></li>').append(element_a);
|
||||
$('#search-box .dropdown-menu').append(element);
|
||||
}
|
||||
$('#MSearchSelectWindow').remove();
|
||||
$('wrapper').append('<div id="MSearchClose"></div>');
|
||||
$('wrapper').append('<div id="MSearchBox"></div>');
|
||||
$('wrapper').append('<div id="MSearchSelectWindow"></div>');
|
||||
|
||||
$('#search-box .close').click(function (){
|
||||
searchBox.CloseResultsWindow();
|
||||
});
|
||||
searchBox.searchLabel = '';
|
||||
searchBox.DOMSearchField = function() {
|
||||
return document.getElementById("search-field");
|
||||
}
|
||||
searchBox.DOMSearchClose = function(){
|
||||
return document.getElementById("search-close");
|
||||
}
|
||||
*/
|
||||
|
||||
$('wrapper').append('<div id="MSearchClose"></div>');
|
||||
$('wrapper').append('<div id="MSearchBox"></div>');
|
||||
$('wrapper').append('<div id="MSearchSelectWindow"></div>');
|
||||
|
||||
searchBox.searchLabel = '';
|
||||
searchBox.DOMSearchField = function() {
|
||||
return document.getElementById("search-field");
|
||||
}
|
||||
searchBox.DOMSearchClose = function(){
|
||||
return document.getElementById("search-close");
|
||||
}
|
||||
|
||||
/* search results */
|
||||
var results_iframe = $('#MSearchResults').detach();
|
||||
$('#MSearchResultsWindow')
|
||||
.attr('id', 'search-results-window')
|
||||
.addClass('panel panel-default')
|
||||
.append(
|
||||
'<div class="panel-heading">\
|
||||
/* search results */
|
||||
/*
|
||||
var results_iframe = $('#MSearchResults').detach();
|
||||
$('#MSearchResultsWindow')
|
||||
.attr('id', 'search-results-window')
|
||||
.addClass('panel panel-default')
|
||||
.append(
|
||||
'<div class="panel-heading">\
|
||||
<h3 class="panel-title">Search Results</h3>\
|
||||
</div>\
|
||||
<div class="panel-body"></div>'
|
||||
);
|
||||
$('#search-results-window .panel-body').append(results_iframe);
|
||||
);
|
||||
$('#search-results-window .panel-body').append(results_iframe);
|
||||
|
||||
searchBox.DOMPopupSearchResultsWindow = function() {
|
||||
return document.getElementById("search-results-window");
|
||||
}
|
||||
|
||||
function update_search_results_window() {
|
||||
$('#search-results-window').removeClass('panel-default panel-success panel-warning panel-danger')
|
||||
var status = $('#MSearchResults').contents().find('.SRStatus:visible');
|
||||
if (status.length > 0) {
|
||||
switch(status.attr('id')) {
|
||||
case 'Loading':
|
||||
case 'Searching':
|
||||
$('#search-results-window').addClass('panel-warning');
|
||||
break;
|
||||
case 'NoMatches':
|
||||
$('#search-results-window').addClass('panel-danger');
|
||||
break;
|
||||
default:
|
||||
$('#search-results-window').addClass('panel-default');
|
||||
}
|
||||
} else {
|
||||
$('#search-results-window').addClass('panel-success');
|
||||
searchBox.DOMPopupSearchResultsWindow = function() {
|
||||
return document.getElementById("search-results-window");
|
||||
}
|
||||
}
|
||||
$('#MSearchResults').load(function() {
|
||||
$('#MSearchResults').contents().find('link[href="search.css"]').attr('href','../doxygen.css');
|
||||
$('#MSearchResults').contents().find('head').append(
|
||||
'<link href="../customdoxygen.css" rel="stylesheet" type="text/css">');
|
||||
|
||||
update_search_results_window();
|
||||
function update_search_results_window() {
|
||||
$('#search-results-window').removeClass('panel-default panel-success panel-warning panel-danger')
|
||||
var status = $('#MSearchResults').contents().find('.SRStatus:visible');
|
||||
if (status.length > 0) {
|
||||
switch(status.attr('id')) {
|
||||
case 'Loading':
|
||||
case 'Searching':
|
||||
$('#search-results-window').addClass('panel-warning');
|
||||
break;
|
||||
case 'NoMatches':
|
||||
$('#search-results-window').addClass('panel-danger');
|
||||
break;
|
||||
default:
|
||||
$('#search-results-window').addClass('panel-default');
|
||||
}
|
||||
} else {
|
||||
$('#search-results-window').addClass('panel-success');
|
||||
}
|
||||
}
|
||||
$('#MSearchResults').load(function() {
|
||||
$('#MSearchResults').contents().find('link[href="search.css"]').attr('href','../doxygen.css');
|
||||
$('#MSearchResults').contents().find('head').append(
|
||||
'<link href="../customdoxygen.css" rel="stylesheet" type="text/css">');
|
||||
|
||||
// detect status changes (only for search with external search backend)
|
||||
var observer = new MutationObserver(function(mutations) {
|
||||
update_search_results_window();
|
||||
update_search_results_window();
|
||||
|
||||
// detect status changes (only for search with external search backend)
|
||||
var observer = new MutationObserver(function(mutations) {
|
||||
update_search_results_window();
|
||||
});
|
||||
var config = { attributes: true};
|
||||
|
||||
var targets = $('#MSearchResults').contents().find('.SRStatus');
|
||||
for (i = 0; i < targets.length; i++) {
|
||||
observer.observe(targets[i], config);
|
||||
}
|
||||
});
|
||||
var config = { attributes: true};
|
||||
*/
|
||||
|
||||
var targets = $('#MSearchResults').contents().find('.SRStatus');
|
||||
for (i = 0; i < targets.length; i++) {
|
||||
observer.observe(targets[i], config);
|
||||
/* enumerations */
|
||||
/*
|
||||
$('table.fieldtable').removeClass('fieldtable').addClass('table table-striped table-bordered').each(function(){
|
||||
$(this).prepend('<thead></thead>');
|
||||
$(this).find('tbody > tr:first').prependTo($(this).find('thead'));
|
||||
|
||||
$(this).find('td > span.success').parent().addClass('success');
|
||||
$(this).find('td > span.warning').parent().addClass('warning');
|
||||
$(this).find('td > span.danger').parent().addClass('danger');
|
||||
});
|
||||
*/
|
||||
|
||||
/* todo list */
|
||||
/*
|
||||
var todoelements = $('.contents > .textblock > dl.reflist > dt, .contents > .textblock > dl.reflist > dd');
|
||||
for (var i = 0; i < todoelements.length; i += 2) {
|
||||
$('.contents > .textblock').append(
|
||||
'<div class="panel panel-default active">'
|
||||
+ "<div class=\"panel-heading todoname\">" + $(todoelements[i]).html() + "</div>"
|
||||
+ "<div class=\"panel-body\">" + $(todoelements[i+1]).html() + "</div>"
|
||||
+ '</div>');
|
||||
}
|
||||
});
|
||||
$('.contents > .textblock > dl').remove();
|
||||
|
||||
|
||||
/* enumerations */
|
||||
$('table.fieldtable').removeClass('fieldtable').addClass('table table-striped table-bordered').each(function(){
|
||||
$(this).prepend('<thead></thead>');
|
||||
$(this).find('tbody > tr:first').prependTo($(this).find('thead'));
|
||||
|
||||
$(this).find('td > span.success').parent().addClass('success');
|
||||
$(this).find('td > span.warning').parent().addClass('warning');
|
||||
$(this).find('td > span.danger').parent().addClass('danger');
|
||||
});
|
||||
|
||||
/* todo list */
|
||||
var todoelements = $('.contents > .textblock > dl.reflist > dt, .contents > .textblock > dl.reflist > dd');
|
||||
for (var i = 0; i < todoelements.length; i += 2) {
|
||||
$('.contents > .textblock').append(
|
||||
'<div class="panel panel-default active">'
|
||||
+ "<div class=\"panel-heading todoname\">" + $(todoelements[i]).html() + "</div>"
|
||||
+ "<div class=\"panel-body\">" + $(todoelements[i+1]).html() + "</div>"
|
||||
+ '</div>');
|
||||
}
|
||||
$('.contents > .textblock > dl').remove();
|
||||
|
||||
|
||||
$(".memitem").removeClass('memitem');
|
||||
$(".memitem").removeClass('memitem');
|
||||
$(".memproto").removeClass('memproto');
|
||||
$(".memdoc").removeClass('memdoc');
|
||||
$("span.mlabel").removeClass('mlabel');
|
||||
$("table.memberdecls").removeClass('memberdecls');
|
||||
$("span.mlabel").removeClass('mlabel');
|
||||
$("table.memberdecls").removeClass('memberdecls');
|
||||
$("[class^=memitem]").removeClass('memitem');
|
||||
$("span.mlabels").removeClass('mlabels');
|
||||
$("table.mlabels").removeClass('mlabels');
|
||||
$("td.mlabels-right").removeClass('mlabels-right');
|
||||
$(".navpath").removeClass('navpath');
|
||||
$("li.navelem").removeClass('navelem');
|
||||
$("a.el").removeClass('el');
|
||||
$("div.ah").removeClass('ah');
|
||||
$("div.header").removeClass("header");
|
||||
$(".navpath").removeClass('navpath');
|
||||
$("li.navelem").removeClass('navelem');
|
||||
$("a.el").removeClass('el');
|
||||
$("div.ah").removeClass('ah');
|
||||
$("div.header").removeClass("header");
|
||||
|
||||
$('.mdescLeft').each(function(){
|
||||
if($(this).html()==" ") {
|
||||
$(this).siblings('.mdescRight').attr('colspan', 2);
|
||||
$(this).remove();
|
||||
}
|
||||
});
|
||||
$('td.memItemLeft').each(function(){
|
||||
if($(this).siblings('.memItemRight').html()=="") {
|
||||
$(this).attr('colspan', 2);
|
||||
$(this).siblings('.memItemRight').remove();
|
||||
}
|
||||
});
|
||||
$('td.memTemplItemLeft').each(function(){
|
||||
if($(this).siblings('.memTemplItemRight').html()=="") {
|
||||
$(this).attr('colspan', 2);
|
||||
$(this).siblings('.memTemplItemRight').remove();
|
||||
}
|
||||
});
|
||||
searchBox.CloseResultsWindow();
|
||||
$('.mdescLeft').each(function(){
|
||||
if($(this).html()==" ") {
|
||||
$(this).siblings('.mdescRight').attr('colspan', 2);
|
||||
$(this).remove();
|
||||
}
|
||||
});
|
||||
$('td.memItemLeft').each(function(){
|
||||
if($(this).siblings('.memItemRight').html()=="") {
|
||||
$(this).attr('colspan', 2);
|
||||
$(this).siblings('.memItemRight').remove();
|
||||
}
|
||||
});
|
||||
$('td.memTemplItemLeft').each(function(){
|
||||
if($(this).siblings('.memTemplItemRight').html()=="") {
|
||||
$(this).attr('colspan', 2);
|
||||
$(this).siblings('.memTemplItemRight').remove();
|
||||
}
|
||||
});
|
||||
searchBox.CloseResultsWindow();
|
||||
*/
|
||||
});
|
||||
|
||||
// contents of dynsections.js which seems to be missing from std doxygen js files...
|
||||
function toggleVisibility(linkObj)
|
||||
{
|
||||
var base = $(linkObj).attr('id');
|
||||
var summary = $('#'+base+'-summary');
|
||||
var content = $('#'+base+'-content');
|
||||
var trigger = $('#'+base+'-trigger');
|
||||
var src=$(trigger).attr('src');
|
||||
if (content.is(':visible')===true) {
|
||||
content.hide();
|
||||
summary.show();
|
||||
$(linkObj).addClass('closed').removeClass('opened');
|
||||
$(trigger).attr('src',src.substring(0,src.length-8)+'closed.png');
|
||||
} else {
|
||||
content.show();
|
||||
summary.hide();
|
||||
$(linkObj).removeClass('closed').addClass('opened');
|
||||
$(trigger).attr('src',src.substring(0,src.length-10)+'open.png');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function updateStripes()
|
||||
{
|
||||
$('table.directory tr').
|
||||
removeClass('even').filter(':visible:even').addClass('even');
|
||||
}
|
||||
function toggleLevel(level)
|
||||
{
|
||||
$('table.directory tr').each(function(){
|
||||
var l = this.id.split('_').length-1;
|
||||
var i = $('#img'+this.id.substring(3));
|
||||
var a = $('#arr'+this.id.substring(3));
|
||||
if (l<level+1) {
|
||||
i.attr('src','ftv2folderopen.png');
|
||||
a.attr('src','ftv2mnode.png');
|
||||
$(this).show();
|
||||
} else if (l==level+1) {
|
||||
i.attr('src','ftv2folderclosed.png');
|
||||
a.attr('src','ftv2pnode.png');
|
||||
$(this).show();
|
||||
} else {
|
||||
$(this).hide();
|
||||
}
|
||||
});
|
||||
updateStripes();
|
||||
}
|
||||
function toggleFolder(id)
|
||||
{
|
||||
//The clicked row
|
||||
var currentRow = $('#row_'+id);
|
||||
var currentRowImages = currentRow.find("img");
|
||||
//All rows after the clicked row
|
||||
var rows = currentRow.nextAll("tr");
|
||||
//Only match elements AFTER this one (can't hide elements before)
|
||||
var childRows = rows.filter(function() {
|
||||
var re = new RegExp('^row_'+id+'\\d+_$', "i"); //only one sub
|
||||
return this.id.match(re);
|
||||
});
|
||||
//First row is visible we are HIDING
|
||||
if (childRows.filter(':first').is(':visible')===true) {
|
||||
currentRowImages.filter("[id^=arr]").attr('src', 'ftv2pnode.png');
|
||||
currentRowImages.filter("[id^=img]").attr('src', 'ftv2folderclosed.png');
|
||||
rows.filter("[id^=row_"+id+"]").hide();
|
||||
} else { //We are SHOWING
|
||||
//All sub images
|
||||
var childImages = childRows.find("img");
|
||||
var childImg = childImages.filter("[id^=img]");
|
||||
var childArr = childImages.filter("[id^=arr]");
|
||||
currentRow.find("[id^=arr]").attr('src', 'ftv2mnode.png'); //open row
|
||||
currentRow.find("[id^=img]").attr('src', 'ftv2folderopen.png'); //open row
|
||||
childImg.attr('src','ftv2folderclosed.png'); //children closed
|
||||
childArr.attr('src','ftv2pnode.png'); //children closed
|
||||
childRows.show(); //show all children
|
||||
}
|
||||
updateStripes();
|
||||
}
|
||||
function toggleInherit(id)
|
||||
{
|
||||
var rows = $('tr.inherit.'+id);
|
||||
var img = $('tr.inherit_header.'+id+' img');
|
||||
var src = $(img).attr('src');
|
||||
if (rows.filter(':first').is(':visible')===true) {
|
||||
rows.css('display','none');
|
||||
$(img).attr('src',src.substring(0,src.length-8)+'closed.png');
|
||||
} else {
|
||||
rows.css('display','table-row'); // using show() causes jump in firefox
|
||||
$(img).attr('src',src.substring(0,src.length-10)+'open.png');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,17 +21,70 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include <FCConfig.h>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <App/Application.h>
|
||||
|
||||
#include "GuiNativeEventLinux.h"
|
||||
|
||||
#include "GuiApplicationNativeEventAware.h"
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Parameter.h>
|
||||
#include <QMainWindow>
|
||||
|
||||
#include <QSocketNotifier>
|
||||
|
||||
#include <spnav.h>
|
||||
|
||||
// Cached per-axis deadzone values, auto-updated via Observer when user.cfg changes.
|
||||
class Gui::DeadzoneCache: public ParameterGrp::ObserverType
|
||||
{
|
||||
public:
|
||||
static constexpr std::array<const char*, 6> keys = {
|
||||
"PanLRDeadzone",
|
||||
"PanUDDeadzone",
|
||||
"ZoomDeadzone",
|
||||
"TiltDeadzone",
|
||||
"RollDeadzone",
|
||||
"SpinDeadzone",
|
||||
};
|
||||
|
||||
std::array<int, 6> values {};
|
||||
|
||||
explicit DeadzoneCache(ParameterGrp::handle hGrp)
|
||||
: hGrp(std::move(hGrp))
|
||||
{
|
||||
loadAll();
|
||||
this->hGrp->Attach(this);
|
||||
}
|
||||
|
||||
~DeadzoneCache() override
|
||||
{
|
||||
hGrp->Detach(this);
|
||||
}
|
||||
|
||||
void OnChange(ParameterGrp::SubjectType& /*rCaller*/, ParameterGrp::MessageType reason) override
|
||||
{
|
||||
for (size_t i = 0; i < keys.size(); i++) {
|
||||
if (std::strcmp(reason, keys[i]) == 0) {
|
||||
values[i] = static_cast<int>(hGrp->GetInt(keys[i], 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void loadAll()
|
||||
{
|
||||
for (size_t i = 0; i < keys.size(); i++) {
|
||||
values[i] = static_cast<int>(hGrp->GetInt(keys[i], 0));
|
||||
}
|
||||
}
|
||||
|
||||
ParameterGrp::handle hGrp;
|
||||
};
|
||||
|
||||
Gui::GuiNativeEvent::GuiNativeEvent(Gui::GUIApplicationNativeEventAware* app)
|
||||
: GuiAbstractNativeEvent(app)
|
||||
{}
|
||||
@@ -60,6 +113,9 @@ void Gui::GuiNativeEvent::initSpaceball(QMainWindow* window)
|
||||
QSocketNotifier* SpacenavNotifier
|
||||
= new QSocketNotifier(spnav_fd(), QSocketNotifier::Read, this);
|
||||
connect(SpacenavNotifier, SIGNAL(activated(int)), this, SLOT(pollSpacenav()));
|
||||
dzCache = std::make_unique<DeadzoneCache>(
|
||||
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Spaceball/Motion")
|
||||
);
|
||||
mainApp->setSpaceballPresent(true);
|
||||
}
|
||||
}
|
||||
@@ -67,6 +123,7 @@ void Gui::GuiNativeEvent::initSpaceball(QMainWindow* window)
|
||||
void Gui::GuiNativeEvent::pollSpacenav()
|
||||
{
|
||||
spnav_event ev;
|
||||
bool hasMotion = false;
|
||||
while (spnav_poll_event(&ev)) {
|
||||
switch (ev.type) {
|
||||
case SPNAV_EVENT_MOTION: {
|
||||
@@ -76,7 +133,7 @@ void Gui::GuiNativeEvent::pollSpacenav()
|
||||
motionDataArray[3] = -ev.motion.rx;
|
||||
motionDataArray[4] = -ev.motion.rz;
|
||||
motionDataArray[5] = -ev.motion.ry;
|
||||
mainApp->postMotionEvent(motionDataArray);
|
||||
hasMotion = true;
|
||||
break;
|
||||
}
|
||||
case SPNAV_EVENT_BUTTON: {
|
||||
@@ -85,6 +142,19 @@ void Gui::GuiNativeEvent::pollSpacenav()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasMotion) {
|
||||
// Per-axis deadzone: zero out axes below their individual threshold.
|
||||
// Values cached and auto-updated via Observer when user.cfg changes.
|
||||
if (dzCache) {
|
||||
for (size_t i = 0; i < dzCache->values.size(); i++) {
|
||||
int dz = dzCache->values[i];
|
||||
if (dz > 0 && std::abs(motionDataArray[i]) < dz) {
|
||||
motionDataArray[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
mainApp->postMotionEvent(motionDataArray);
|
||||
}
|
||||
}
|
||||
|
||||
#include "3Dconnexion/moc_GuiNativeEventLinux.cpp"
|
||||
|
||||
@@ -23,12 +23,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "GuiAbstractNativeEvent.h"
|
||||
#include <memory>
|
||||
|
||||
class QMainWindow;
|
||||
|
||||
namespace Gui
|
||||
{
|
||||
class GUIApplicationNativeEventAware;
|
||||
class DeadzoneCache;
|
||||
|
||||
class GuiNativeEvent: public GuiAbstractNativeEvent
|
||||
{
|
||||
@@ -42,6 +44,9 @@ private:
|
||||
GuiNativeEvent();
|
||||
GuiNativeEvent(const GuiNativeEvent&);
|
||||
GuiNativeEvent& operator=(const GuiNativeEvent&);
|
||||
|
||||
std::unique_ptr<DeadzoneCache> dzCache;
|
||||
|
||||
private Q_SLOTS:
|
||||
void pollSpacenav();
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -804,6 +804,9 @@ bool DlgAddProperty::clearBoundProperty()
|
||||
|
||||
if (App::Property* prop = propertyItem->getFirstProperty()) {
|
||||
propertyItem->unbind();
|
||||
if (auto* eb = dynamic_cast<ExpressionBinding*>(editor.get())) {
|
||||
eb->unbind();
|
||||
}
|
||||
propertyItem->removeProperty(prop);
|
||||
container->removeDynamicProperty(prop->getName());
|
||||
closeTransaction(TransactionOption::Abort);
|
||||
@@ -813,7 +816,7 @@ bool DlgAddProperty::clearBoundProperty()
|
||||
|
||||
bool DlgAddProperty::clear(FieldChange fieldChange)
|
||||
{
|
||||
if (propertyItem == nullptr) {
|
||||
if (!propertyItem) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -887,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;
|
||||
}
|
||||
|
||||
@@ -917,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)
|
||||
@@ -990,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;
|
||||
}
|
||||
|
||||
@@ -1016,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;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user