mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-26 03:44:14 +02:00
Compare commits
226 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| da1d7df662 | |||
| 617619c92c | |||
| 61fa130d6d | |||
| c9899372d3 | |||
| e6f1a18844 | |||
| b8b9fa086a | |||
| 34270a961d | |||
| 48715051bb | |||
| efab108709 | |||
| ea6f0f893a | |||
| bfe2aa26a0 | |||
| 4bde81937d | |||
| 91116f7044 | |||
| 8246c8aaba | |||
| 8ca62c4a17 | |||
| 0394503c0b | |||
| e5935c75d1 | |||
| 2e49f4588a | |||
| ccf545d30c | |||
| 26d7c03a76 | |||
| bf23967470 | |||
| 5e898c4661 | |||
| 79251c8ed6 | |||
| 03b552c601 | |||
| 1195474f88 | |||
| 7bfa2a56df | |||
| 47558f8b0a | |||
| e44219778e | |||
| 8d175754cd | |||
| 9d590eaa6f | |||
| 759b197dc2 | |||
| e542724953 | |||
| 8af4bc8eee | |||
| 45afc7e6aa | |||
| ff25a77159 | |||
| 4edd4932bb | |||
| baf95338a3 | |||
| 56464629bf | |||
| b7f8086166 | |||
| c536dcf30f | |||
| 5db77fe40c | |||
| bf816cbc44 | |||
| 315889baf4 | |||
| 2f6fb7808a | |||
| f951c20586 | |||
| 033c3fc112 | |||
| 480d2757e7 | |||
| 92db329f40 | |||
| c488d8437e | |||
| 7f7185172f | |||
| dbe2bcb599 | |||
| ab159404a3 | |||
| bc79a5df7a | |||
| 43da912aad | |||
| 0fdcd1e1e8 | |||
| f172e0740b | |||
| 5263d78dce | |||
| 265ec12351 | |||
| 4572fca31f | |||
| b8df298953 | |||
| 9d81a9788f | |||
| 2803dcf8b8 | |||
| 5f83679756 | |||
| bb1f457763 | |||
| e2e0df784b | |||
| cfc64dfad0 | |||
| eb095f9a10 | |||
| e9ee8b600b | |||
| 4b981668ca | |||
| 91380b0695 | |||
| dc1f0ffa4d | |||
| 5b9da15982 | |||
| e18d5b1aa0 | |||
| f8634a83fc | |||
| 99fa186d30 | |||
| c8ff06459f | |||
| d5c75ad19d | |||
| 17eac03ca3 | |||
| e90cb66686 | |||
| 610001a847 | |||
| 2419faf931 | |||
| ddbd1d8d75 | |||
| afb7442e8a | |||
| 1780fde458 | |||
| f0b16fe650 | |||
| d988054aca | |||
| 1743f342ce | |||
| 2c69bf0dad | |||
| 8bda0a1821 | |||
| fe6191f26d | |||
| 3077527601 | |||
| 8a71649e87 | |||
| 46aed59b8b | |||
| 5acf201067 | |||
| 4c7d4e7c53 | |||
| f0348a7cd8 | |||
| 6326cb3789 | |||
| 39ac5716c7 | |||
| f83ed508e3 | |||
| ab88937ac9 | |||
| 1cf58e3caf | |||
| 26f6f6d36e | |||
| 293dc92b41 | |||
| 6d05bc2f21 | |||
| 7ba295a339 | |||
| 458c9921c2 | |||
| deba8d0e4e | |||
| 2e1ba46430 | |||
| 67b1665b36 | |||
| 61a509e5d8 | |||
| 2186d2733f | |||
| b7efbdc323 | |||
| b00e053dab | |||
| b0172f4dd7 | |||
| 1de9f7eaf0 | |||
| d2a921ea80 | |||
| a4d23fe312 | |||
| 8cfb777fe1 | |||
| 8d08c3fd56 | |||
| 766a6b981d | |||
| 8b40134a47 | |||
| cc032c9c76 | |||
| 92b2608bfb | |||
| af87d28e33 | |||
| c5d714089f | |||
| 206c37f620 | |||
| c14d6a6dd6 | |||
| e32e5595c0 | |||
| 1684d2cfe6 | |||
| 6f66745ce3 | |||
| 762bd7febf | |||
| 162d43e1c9 | |||
| 10cc162c4e | |||
| 3a3182862c | |||
| 0d432f6159 | |||
| 45458c2f2a | |||
| 472786fd23 | |||
| 0732bf64a7 | |||
| 2d889568a5 | |||
| 55250a6de9 | |||
| 325b895d0b | |||
| 6ad2a63575 | |||
| 99dac17327 | |||
| 13e245b104 | |||
| 6bf6a17dc9 | |||
| 550e085b82 | |||
| 9fd951152a | |||
| 7debaa5504 | |||
| 6c76b1f6a8 | |||
| 31ab7538a4 | |||
| b0b5345e15 | |||
| c25c400c93 | |||
| d9638ad746 | |||
| ea8eeb02c3 | |||
| 5e4c423a7d | |||
| 7f75500023 | |||
| cdf5cad1e5 | |||
| 3a3f23b7a8 | |||
| 1f7107a7b6 | |||
| 7cb1e7394e | |||
| b91aaacad9 | |||
| 1f3c28992c | |||
| 5e02111600 | |||
| a3d348fdcd | |||
| c1d892c496 | |||
| 5b33c044c5 | |||
| c7ed3229d0 | |||
| 79cd91bd1c | |||
| b7fc0c79cb | |||
| fca1e0993b | |||
| 9f283322a2 | |||
| 09983efe05 | |||
| 76ff69e912 | |||
| 779602372a | |||
| d95e744494 | |||
| 1317f137c0 | |||
| 201bd4c5f6 | |||
| c12137c5a0 | |||
| 5adf61936b | |||
| 39b1e836bf | |||
| f076df77da | |||
| b3de01d171 | |||
| 90950075bf | |||
| a668ccfa90 | |||
| ad200e28c7 | |||
| 3424ba50bd | |||
| 79da321ddc | |||
| 293e61abeb | |||
| 438e2ade3a | |||
| bfee5b1cdb | |||
| 3eafe840f1 | |||
| e38493c308 | |||
| 5abf890b24 | |||
| 037d5a13cd | |||
| cd7388985e | |||
| 5ba08284f5 | |||
| 4a95efbfe6 | |||
| bab5bf58f8 | |||
| fca945f90e | |||
| 615e40dee2 | |||
| 4bd9b6b212 | |||
| d2b2a2eadb | |||
| e5e2f2efbd | |||
| 5c39518921 | |||
| 6d2995ad68 | |||
| 1198cf73a9 | |||
| e8f80697f3 | |||
| 62ad49a6d3 | |||
| 41e83bbc09 | |||
| b6e43f78d0 | |||
| 016bb3636e | |||
| 57d0b4b5a3 | |||
| 99151b9c04 | |||
| 43e4603c55 | |||
| 3cf5cb945e | |||
| b3a4a41ad9 | |||
| 979df376d1 | |||
| 484ab9ed87 | |||
| ee36073428 | |||
| ab61e00cdf | |||
| fb51489436 | |||
| 1216855cfa | |||
| 32dd686144 | |||
| 29ba7c9636 | |||
| a3511e8694 | |||
| 76fda3f0e6 |
@@ -2,7 +2,7 @@ name: Windows Build
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * 1' # Every Monday at 2:00 UTC
|
||||
- cron: '0 2 1 * *' #
|
||||
workflow_dispatch: # Manual trigger available at any time
|
||||
|
||||
concurrency:
|
||||
@@ -113,10 +113,10 @@ jobs:
|
||||
-DQt5_DIR=/ucrt64/lib/cmake/Qt5 \
|
||||
-DQT_VERSION_MAJOR=5 \
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_Qt6=ON \
|
||||
-DBUILD_TESTING=OFF \
|
||||
-DPACKAGE_TESTS=OFF \
|
||||
-DCMAKE_POLICY_DEFAULT_CMP0077=NEW \
|
||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
-DCMAKE_CXX_FLAGS="-DQET_EXPORT_PROJECT_DB" \
|
||||
-DQET_EXPORT_PROJECT_DB=ON \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=/ucrt64/bin/ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=/ucrt64/bin/ccache \
|
||||
-DSQLite3_INCLUDE_DIR=/ucrt64/include \
|
||||
@@ -343,21 +343,21 @@ jobs:
|
||||
with:
|
||||
name: qelectrotech-${{ steps.qet_version.outputs.base_version }}+git${{ steps.qet_version.outputs.head }}-x86-win64-readytouse
|
||||
path: dist/${{ steps.zip_portable.outputs.zip_name }}
|
||||
retention-days: 14
|
||||
retention-days: 40
|
||||
|
||||
- name: Upload NSIS installer
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: qelectrotech-windows-installer
|
||||
path: dist/Installer_*.exe
|
||||
retention-days: 14
|
||||
retention-days: 40
|
||||
|
||||
- name: Upload portable (nom fixe pour le workflow MSI)
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: qelectrotech-windows-portable
|
||||
path: nsis_root/files/
|
||||
retention-days: 14
|
||||
retention-days: 40
|
||||
|
||||
# =============================================================================
|
||||
# Job 2: Qt6 build (EXPERIMENTAL track)
|
||||
@@ -397,6 +397,9 @@ jobs:
|
||||
mingw-w64-ucrt-x86_64-qt6-pdf
|
||||
mingw-w64-ucrt-x86_64-sqlite3
|
||||
mingw-w64-ucrt-x86_64-pkg-config
|
||||
mingw-w64-ucrt-x86_64-kwidgetsaddons
|
||||
mingw-w64-ucrt-x86_64-kcoreaddons
|
||||
mingw-w64-ucrt-x86_64-extra-cmake-modules
|
||||
mingw-w64-ucrt-x86_64-nsis
|
||||
mingw-w64-ucrt-x86_64-angleproject
|
||||
|
||||
@@ -456,11 +459,12 @@ jobs:
|
||||
-DCMAKE_PREFIX_PATH=/ucrt64 \
|
||||
-DQt6_DIR=/ucrt64/lib/cmake/Qt6 \
|
||||
-DQT_VERSION_MAJOR=6 \
|
||||
-DBUILD_WITH_KF5=OFF \
|
||||
-DBUILD_WITH_KF=ON \
|
||||
-DBUILD_KF=OFF \
|
||||
-DPACKAGE_TESTS=OFF \
|
||||
-DCMAKE_POLICY_DEFAULT_CMP0077=NEW \
|
||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
-DCMAKE_CXX_FLAGS="-DQET_EXPORT_PROJECT_DB" \
|
||||
-DQET_EXPORT_PROJECT_DB=ON \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=/ucrt64/bin/ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=/ucrt64/bin/ccache \
|
||||
-DSQLite3_INCLUDE_DIR=/ucrt64/include \
|
||||
@@ -687,21 +691,21 @@ jobs:
|
||||
with:
|
||||
name: qelectrotech-${{ steps.qet_version.outputs.base_version }}+git${{ steps.qet_version.outputs.head }}-x86-win64-readytouse
|
||||
path: dist/${{ steps.zip_portable.outputs.zip_name }}
|
||||
retention-days: 14
|
||||
retention-days: 40
|
||||
|
||||
- name: Upload NSIS installer
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: qelectrotech-windows-installer-qt6
|
||||
path: dist/Installer_*.exe
|
||||
retention-days: 14
|
||||
retention-days: 40
|
||||
|
||||
- name: Upload portable (nom fixe pour le workflow MSI)
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: qelectrotech-windows-portable-qt6
|
||||
path: nsis_root/files/
|
||||
retention-days: 14
|
||||
retention-days: 40
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Job 3 : Publie les assets nightly (exe + zip, Qt5 et Qt6) sur la release
|
||||
@@ -759,8 +763,9 @@ jobs:
|
||||
> ⚠️ This is a development version; it introduces new features you want, but may cause bugs that have not yet been identified yet.
|
||||
> For stable releases, see the [Releases page](https://github.com/${{ github.repository }}/releases).
|
||||
|
||||
> 🧪 **Qt6 builds are experimental.** Files tagged `-qt6-` are built against Qt6 and are not yet
|
||||
> as tested as the Qt5 track. Expect rough edges; report issues clearly labelled "Qt6".
|
||||
> 🧪 **Try Qt6 — soon the only track.** Files tagged `-qt6-` are built against Qt6. The Qt5
|
||||
> builds above will be removed from Windows CI soon; please switch and test Qt6 now — report
|
||||
> any issue clearly labelled "Qt6".
|
||||
prerelease: true
|
||||
make_latest: false
|
||||
files: |
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
- flavor: qt6
|
||||
portable_artifact: qelectrotech-windows-portable-qt6
|
||||
version_source: hardcoded # see note in "Extract version" step
|
||||
label_suffix: "-qt6-EXPERIMENTAL"
|
||||
label_suffix: "-qt6"
|
||||
experimental: true
|
||||
|
||||
permissions:
|
||||
@@ -311,7 +311,7 @@ jobs:
|
||||
with:
|
||||
name: qelectrotech-windows-msi-${{ matrix.flavor }}
|
||||
path: dist\*.msi
|
||||
retention-days: 14
|
||||
retention-days: 40
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Delete old nightly .msi asset for this flavor
|
||||
@@ -319,10 +319,10 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
$pattern = if ("${{ matrix.flavor }}" -eq "qt6") { "-qt6-EXPERIMENTAL.*\\.msi$" } else { "\\.msi$" }
|
||||
$pattern = if ("${{ matrix.flavor }}" -eq "qt6") { "-qt6.*\\.msi$" } else { "\\.msi$" }
|
||||
$names = gh release view nightly --repo $env:REPO --json assets --jq ".assets[] | select(.name | test(`"$pattern`")) | .name"
|
||||
foreach ($name in ($names -split "`n" | Where-Object { $_ })) {
|
||||
if ("${{ matrix.flavor }}" -eq "qt5" -and $name -match "-qt6-EXPERIMENTAL") { continue }
|
||||
if ("${{ matrix.flavor }}" -eq "qt5" -and $name -match "-qt6") { continue }
|
||||
Write-Host "Deleting old asset: $name"
|
||||
gh release delete-asset nightly $name --repo $env:REPO --yes
|
||||
}
|
||||
|
||||
+131
-30
@@ -48,19 +48,11 @@ endif()
|
||||
# INTERFACE_QT_MAJOR_VERSION mismatch at generate time.
|
||||
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (5 or 6)" FORCE)
|
||||
|
||||
# Add sub directories
|
||||
option(PACKAGE_TESTS "Build the tests" ON)
|
||||
if(PACKAGE_TESTS)
|
||||
message("Add sub directory tests")
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
include(cmake/paths_compilation_installation.cmake)
|
||||
include(cmake/start_options.cmake)
|
||||
include(cmake/developer_options.cmake)
|
||||
include(cmake/git_update_submodules.cmake)
|
||||
include(cmake/git_last_commit_sha.cmake)
|
||||
include(cmake/fetch_kdeaddons.cmake)
|
||||
include(cmake/fetch_singleapplication.cmake)
|
||||
include(cmake/fetch_pugixml.cmake)
|
||||
include(cmake/qet_compilation_vars.cmake)
|
||||
@@ -78,12 +70,43 @@ find_package(
|
||||
${QET_COMPONENTS}
|
||||
REQUIRED)
|
||||
|
||||
# Qt6 only creates the Qt::GuiPrivate target (used for QPdfEngine::drawHyperlink)
|
||||
# when the GuiPrivate component is explicitly requested. Qt5 has no such
|
||||
# component package and creates the target implicitly with Gui, so only
|
||||
# request it on Qt6 - requesting it on Qt5 fails the whole configure.
|
||||
# <private/qpdf_p.h> (QPdfEngine::drawHyperlink) needs Qt's private GUI module.
|
||||
# Qt >= 6.7 ships it as a proper find_package component, but some distro
|
||||
# packages (e.g. Ubuntu's qt6-base-private-dev) omit Qt6GuiPrivateConfig.cmake
|
||||
# and only provide the implicit Qt6::GuiPrivate target created alongside
|
||||
# Qt6::Gui. Try the component quietly, then verify the target below so a
|
||||
# missing private-headers package fails here instead of at compile time.
|
||||
# Qt5 has no such component as its GuiPrivate target always exists once Gui is found.
|
||||
if(QT_VERSION_MAJOR GREATER_EQUAL 6)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS GuiPrivate)
|
||||
find_package(Qt6 QUIET COMPONENTS GuiPrivate)
|
||||
endif()
|
||||
|
||||
if(QT_VERSION_MAJOR GREATER_EQUAL 6 AND NOT TARGET Qt6::GuiPrivate)
|
||||
message(FATAL_ERROR
|
||||
"Qt6::GuiPrivate was not found. It is required for PDF hyperlink "
|
||||
"support (<private/qpdf_p.h>). Install the Qt6 private headers "
|
||||
"(e.g. 'qt6-base-private-dev' on Debian/Ubuntu) or use a Qt build "
|
||||
"that provides the GuiPrivate component.")
|
||||
endif()
|
||||
|
||||
# PDF page import (toolbar "Ajouter un PDF") needs the QtPdf module and,
|
||||
# specifically, QPdfDocument::pagePointSize() which only exists since Qt 6.4.
|
||||
# Unlike GuiPrivate above, a missing QtPdf module is NOT fatal here: some
|
||||
# Qt6 distributions (e.g. the Flatpak org.kde.Platform runtime) don't ship
|
||||
# it at all, since it lives in the qtwebengine source tree rather than Qt6
|
||||
# core. When it's missing, or too old, the feature is silently disabled -
|
||||
# see the QT_VERSION_CHECK / QET_HAS_QTPDF guards in diagrameventaddpdf.*
|
||||
# and pdfpagesdialog.*.
|
||||
set(QET_HAS_QTPDF FALSE)
|
||||
if(QT_VERSION_MAJOR GREATER_EQUAL 6)
|
||||
find_package(Qt6 QUIET COMPONENTS Pdf)
|
||||
if(TARGET Qt6::Pdf AND NOT Qt6_VERSION VERSION_LESS 6.4.0)
|
||||
set(QET_HAS_QTPDF TRUE)
|
||||
list(APPEND QET_PRIVATE_LIBRARIES Qt::Pdf)
|
||||
add_compile_definitions(QET_HAS_QTPDF)
|
||||
else()
|
||||
message(STATUS "QtPdf module not available (or Qt < 6.4): PDF page import feature disabled")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(SQLite3 REQUIRED)
|
||||
@@ -100,25 +123,19 @@ endif()
|
||||
|
||||
set(CMAKE_AUTOUIC_SEARCH_PATHS ${QET_DIR}/sources/ui)
|
||||
|
||||
# The default build only compiles the tracked .ts files to .qm (lrelease).
|
||||
# Refreshing the .ts from the sources (lupdate) is a developer action behind
|
||||
# the explicit "update_translations" target below: running lupdate on every
|
||||
# build rewrote tracked files as a side effect, and under high parallelism
|
||||
# lupdate rewriting a .ts while lrelease read the same file made the build
|
||||
# fail with "Premature end of document".
|
||||
set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION "${QET_DIR}/lang")
|
||||
if(QT_VERSION_MAJOR EQUAL 6)
|
||||
qt6_add_translation(QM_FILES ${TS_FILES})
|
||||
set(KF_MAJOR_VERSION 6)
|
||||
else()
|
||||
qt5_add_translation(QM_FILES ${TS_FILES})
|
||||
set(KF_MAJOR_VERSION 5)
|
||||
endif()
|
||||
include(cmake/fetch_kdeaddons.cmake)
|
||||
|
||||
add_custom_target(update_translations
|
||||
COMMAND $<TARGET_FILE:Qt${QT_VERSION_MAJOR}::lupdate> ${CMAKE_SOURCE_DIR}/sources -ts ${TS_FILES}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
COMMENT "Updating .ts files from sources/ (lupdate) - developer target, run explicitly"
|
||||
VERBATIM
|
||||
)
|
||||
# Add sub directories
|
||||
option(PACKAGE_TESTS "Build the tests" ON)
|
||||
if(PACKAGE_TESTS)
|
||||
message("Add sub directory tests")
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
# als laatse
|
||||
include(cmake/define_definitions.cmake)
|
||||
@@ -145,6 +162,90 @@ else()
|
||||
)
|
||||
endif()
|
||||
|
||||
# The default build only compiles the tracked .ts files to .qm (lrelease).
|
||||
# Refreshing the .ts from the sources (lupdate) is a developer action behind
|
||||
# the explicit "update_translations" target below: running lupdate on every
|
||||
# build rewrote tracked files as a side effect, and under high parallelism
|
||||
# lupdate rewriting a .ts while lrelease read the same file made the build
|
||||
# fail with "Premature end of document".
|
||||
set_source_files_properties(
|
||||
${TS_FILES}
|
||||
PROPERTIES OUTPUT_LOCATION "${QET_DIR}/lang"
|
||||
)
|
||||
if(QT_VERSION_MAJOR EQUAL 6)
|
||||
if(Qt6_VERSION VERSION_LESS "6.2")
|
||||
# Qt 6.0–6.1
|
||||
qt6_add_translation(QM_FILES ${TS_FILES})
|
||||
|
||||
# qt6_add_translation() only creates custom commands. Something must
|
||||
# depend on their outputs for them to run during the default build.
|
||||
add_custom_target(${PROJECT_NAME}_lrelease ALL
|
||||
DEPENDS ${QM_FILES}
|
||||
)
|
||||
|
||||
add_custom_target(update_translations
|
||||
COMMAND
|
||||
$<TARGET_FILE:Qt6::lupdate>
|
||||
"${CMAKE_SOURCE_DIR}/sources"
|
||||
-ts ${TS_FILES}
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
COMMENT
|
||||
"Updating .ts files from sources/ (lupdate) - developer target"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
elseif(Qt6_VERSION VERSION_LESS "6.7")
|
||||
# Qt 6.2–6.6: old target-based signature
|
||||
qt_add_lrelease(
|
||||
${PROJECT_NAME}
|
||||
TS_FILES ${TS_FILES}
|
||||
QM_FILES_OUTPUT_VARIABLE QM_FILES
|
||||
)
|
||||
|
||||
# Automatically creates the update_translations umbrella target.
|
||||
qt_add_lupdate(
|
||||
${PROJECT_NAME}
|
||||
TS_FILES ${TS_FILES}
|
||||
)
|
||||
|
||||
else()
|
||||
# Qt 6.7+: new signature
|
||||
qt_add_lrelease(
|
||||
TS_FILES ${TS_FILES}
|
||||
LRELEASE_TARGET ${PROJECT_NAME}_lrelease
|
||||
QM_FILES_OUTPUT_VARIABLE QM_FILES
|
||||
)
|
||||
|
||||
qt_add_lupdate(
|
||||
SOURCE_TARGETS ${PROJECT_NAME}
|
||||
TS_FILES ${TS_FILES}
|
||||
LUPDATE_TARGET update_translations
|
||||
NO_GLOBAL_TARGET
|
||||
)
|
||||
endif()
|
||||
|
||||
else()
|
||||
# Qt 5
|
||||
qt5_add_translation(QM_FILES ${TS_FILES})
|
||||
|
||||
# Likewise, qt5_add_translation() needs a target depending on its outputs,
|
||||
# unless QM_FILES are already consumed elsewhere.
|
||||
add_custom_target(${PROJECT_NAME}_lrelease ALL
|
||||
DEPENDS ${QM_FILES}
|
||||
)
|
||||
|
||||
add_custom_target(update_translations
|
||||
COMMAND
|
||||
$<TARGET_FILE:Qt5::lupdate>
|
||||
"${CMAKE_SOURCE_DIR}/sources"
|
||||
-ts ${TS_FILES}
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
COMMENT
|
||||
"Updating .ts files from sources/ (lupdate) - developer target"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
# Optional precompiled headers -- see QET_ENABLE_PCH in
|
||||
# cmake/developer_options.cmake for what this trades away.
|
||||
#
|
||||
@@ -180,7 +281,7 @@ target_link_libraries(
|
||||
pugixml::pugixml
|
||||
SingleApplication::SingleApplication
|
||||
SQLite3::SQLite3
|
||||
${KF5_PRIVATE_LIBRARIES}
|
||||
${KF_PRIVATE_LIBRARIES}
|
||||
${QET_PRIVATE_LIBRARIES}
|
||||
)
|
||||
|
||||
@@ -221,7 +322,7 @@ target_include_directories(
|
||||
${QET_DIR}/sources/svg
|
||||
)
|
||||
|
||||
if(NOT BUILD_WITH_KF5)
|
||||
if(NOT BUILD_WITH_KF)
|
||||
target_include_directories(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "org.qelectrotech.QElectroTech",
|
||||
"runtime": "org.kde.Platform",
|
||||
"runtime-version": "5.15-25.08",
|
||||
"runtime-version": "6.11",
|
||||
"sdk": "org.kde.Sdk",
|
||||
"command": "qelectrotech",
|
||||
"rename-desktop-file": "org.qelectrotech.qelectrotech.desktop",
|
||||
@@ -30,7 +30,20 @@
|
||||
"pypi-dependencies.json",
|
||||
{
|
||||
"name": "qelectrotech",
|
||||
"buildsystem": "qmake",
|
||||
"//qt6-private-headers-note": "qt6-base-private-dev has no Flatpak build-depends equivalent — private Qt6 headers ship inside org.kde.Sdk itself. If the cmake build fails looking for QtCore/private/*.h, the SDK/runtime branch is mismatched with the source tree, not a missing package.",
|
||||
"//sqlite-driver-note": "libqt6sql6-sqlite / libsqlite3-dev have no Flatpak build-depends equivalent either. QET_EXPORT_PROJECT_DB=ON assumes the KDE runtime ships the Qt6 SQLite plugin (libqsqlite.so) already built into QtSql — verified at Debian packaging time this needed an explicit runtime dep there (see debian/control). Test the in-app database export after building; if it silently fails, the runtime's QtSql plugin set needs checking, not a package to add here.",
|
||||
"buildsystem": "cmake",
|
||||
"config-opts": [
|
||||
"-DCMAKE_INSTALL_PREFIX=/app",
|
||||
"-DQT_VERSION_MAJOR=6",
|
||||
"-DBUILD_WITH_KF=ON",
|
||||
"-DBUILD_KF=OFF",
|
||||
"-DPACKAGE_TESTS=OFF",
|
||||
"-DBUILD_PUGIXML=ON",
|
||||
"-DFETCHCONTENT_FULLY_DISCONNECTED=ON",
|
||||
"-DFETCHCONTENT_SOURCE_DIR_PUGIXML=/run/build/qelectrotech/pugixml",
|
||||
"-DQET_EXPORT_PROJECT_DB=ON"
|
||||
],
|
||||
"post-install": [
|
||||
"mv ${FLATPAK_DEST}/share/mime/packages/qelectrotech.xml ${FLATPAK_DEST}/share/mime/packages/org.qelectrotech.QElectroTech.xml"
|
||||
],
|
||||
@@ -38,12 +51,6 @@
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "../.."
|
||||
},
|
||||
{
|
||||
"type": "patch",
|
||||
"paths": [
|
||||
"patches/fix-the-installation-paths.patch"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ Called from windows-msi.yml deploy-pages job.
|
||||
Environment variables required:
|
||||
DATE, SHORT, REPO, SHA, RUN_URL, RUN_NUMBER,
|
||||
INSTALLER_URL, PORTABLE_URL, MSI_URL (optional)
|
||||
Optional (Qt6 experimental track — omitted entirely if empty):
|
||||
Optional (Qt6 track — omitted entirely if empty):
|
||||
INSTALLER_QT6_URL, PORTABLE_QT6_URL, MSI_QT6_URL
|
||||
"""
|
||||
import os
|
||||
@@ -32,7 +32,7 @@ if msi_url:
|
||||
<span class="btn-text">Windows Installer .msi<small>.msi — for enterprise / GPO deployment</small></span>
|
||||
</a>"""
|
||||
|
||||
# Qt6 experimental section — only rendered if at least one Qt6 asset exists.
|
||||
# Qt6 section — only rendered if at least one Qt6 asset exists.
|
||||
qt6_block = ""
|
||||
if installer_qt6_url or portable_qt6_url or msi_qt6_url:
|
||||
qt6_msi_btn = ""
|
||||
@@ -40,30 +40,30 @@ if installer_qt6_url or portable_qt6_url or msi_qt6_url:
|
||||
qt6_msi_btn = f"""
|
||||
<a class="btn btn-msi" href="{msi_qt6_url}">
|
||||
<span class="btn-icon">⬇</span>
|
||||
<span class="btn-text">Windows Installer .msi (Qt6)<small>.msi — experimental, for enterprise / GPO deployment</small></span>
|
||||
<span class="btn-text">Windows Installer .msi (Qt6)<small>.msi — for enterprise / GPO deployment</small></span>
|
||||
</a>"""
|
||||
qt6_installer_btn = ""
|
||||
if installer_qt6_url:
|
||||
qt6_installer_btn = f"""
|
||||
<a class="btn btn-primary" href="{installer_qt6_url}">
|
||||
<span class="btn-icon">⬇</span>
|
||||
<span class="btn-text">Windows Installer (Qt6)<small>.exe — experimental, includes all dependencies</small></span>
|
||||
<span class="btn-text">Windows Installer (Qt6)<small>.exe — includes all dependencies</small></span>
|
||||
</a>"""
|
||||
qt6_portable_btn = ""
|
||||
if portable_qt6_url:
|
||||
qt6_portable_btn = f"""
|
||||
<a class="btn btn-secondary" href="{portable_qt6_url}">
|
||||
<span class="btn-icon">📦</span>
|
||||
<span class="btn-text">Windows Portable (Qt6)<small>.zip — experimental, no installation required</small></span>
|
||||
<span class="btn-text">Windows Portable (Qt6)<small>.zip — no installation required</small></span>
|
||||
</a>"""
|
||||
|
||||
qt6_block = f"""
|
||||
<div class="card">
|
||||
<h2>🧪 Windows — x86_64 — Qt6 track</h2>
|
||||
<div class="warning">
|
||||
⚠️ <strong>Experimental.</strong> These Qt6-based installer, portable and MSI
|
||||
builds are new and not as thoroughly tested as the Qt5 track above. Expect rough
|
||||
edges; please report issues and mention "Qt6" explicitly.
|
||||
🧪 <strong>Try Qt6 — soon the only track.</strong> The Qt5 builds above
|
||||
will be removed from Windows CI soon; Qt6 is becoming the sole supported track.
|
||||
Please switch and test it now — report any issue and mention "Qt6" explicitly.
|
||||
</div>
|
||||
<div class="downloads">
|
||||
{qt6_installer_btn}
|
||||
|
||||
@@ -1,48 +1,39 @@
|
||||
name: qelectrotech
|
||||
title: QElectroTech
|
||||
base: core22
|
||||
base: core24
|
||||
adopt-info: qelectrotech
|
||||
license: GPL-2.0
|
||||
summary: Electrical diagram editor
|
||||
description: |
|
||||
QElectroTech, or QET in short, is a libre and open source desktop application
|
||||
QElectroTech, or QET in short, is a libre and open source desktop application
|
||||
to create diagrams and schematics.
|
||||
|
||||
grade: stable
|
||||
confinement: strict
|
||||
compression: lzo
|
||||
|
||||
architectures:
|
||||
- build-on: amd64
|
||||
build-for: amd64
|
||||
- build-on: arm64
|
||||
build-for: arm64
|
||||
|
||||
layout:
|
||||
/usr/local/share/qelectrotech:
|
||||
symlink: $SNAP/usr/local/share/qelectrotech
|
||||
|
||||
platforms:
|
||||
amd64:
|
||||
arm64:
|
||||
|
||||
apps:
|
||||
qelectrotech:
|
||||
command: usr/local/bin/qelectrotech
|
||||
command: usr/bin/qelectrotech
|
||||
common-id: qelectrotech.desktop
|
||||
extensions:
|
||||
- kde-neon
|
||||
extensions:
|
||||
- kde-neon-6
|
||||
plugs: &plugs [opengl, unity7, home, removable-media, gsettings, network, cups-control, wayland, x11]
|
||||
environment: &env
|
||||
TCL_LIBRARY: $SNAP/usr/share/tcltk/tcl8.6
|
||||
HOME: $SNAP_USER_COMMON
|
||||
PYTHONPATH: $SNAP:$SNAP/lib/python3.10/site-packages:$SNAP/usr/lib/python3.10:$SNAP/usr/lib/python3.10/lib-dynload
|
||||
PYTHONPATH: $SNAP:$SNAP/lib/python3.12/site-packages:$SNAP/usr/lib/python3.12:$SNAP/usr/lib/python3.12/lib-dynload
|
||||
|
||||
qet-tb-generator:
|
||||
command: bin/qet_tb_generator
|
||||
extensions:
|
||||
- kde-neon
|
||||
extensions:
|
||||
- kde-neon-6
|
||||
plugs: *plugs
|
||||
environment: *env
|
||||
|
||||
|
||||
|
||||
parts:
|
||||
launchers:
|
||||
plugin: dump
|
||||
@@ -55,7 +46,7 @@ parts:
|
||||
source: https://github.com/raulroda/qet_tb_generator-plugin.git
|
||||
source-tag: v1.31
|
||||
python-packages: [PySimpleGUI]
|
||||
stage-packages:
|
||||
stage-packages:
|
||||
- python3-lxml
|
||||
- python3-tk
|
||||
- libtk8.6
|
||||
@@ -63,16 +54,17 @@ parts:
|
||||
kde-sdk-setup:
|
||||
plugin: nil
|
||||
build-snaps:
|
||||
- kf5-5-110-qt-5-15-11-core22-sdk
|
||||
- kf6-core24-sdk
|
||||
- kde-qt6-core24-sdk
|
||||
build-packages:
|
||||
- g++
|
||||
- mesa-common-dev
|
||||
- libglvnd-dev
|
||||
- rsync
|
||||
override-build: |
|
||||
rsync -a --ignore-existing /snap/kf5-5-110-qt-5-15-11-core22-sdk/current/ /
|
||||
rsync -a --ignore-existing /snap/kf6-core24-sdk/current/ /
|
||||
rsync -a --ignore-existing /snap/kde-qt6-core24-sdk/current/ /
|
||||
|
||||
|
||||
qelectrotech:
|
||||
after: [kde-sdk-setup]
|
||||
plugin: nil
|
||||
@@ -81,37 +73,48 @@ parts:
|
||||
- git
|
||||
- sqlite3
|
||||
- xdg-user-dirs
|
||||
# libxcb-cursor0 was added as a hard dependency of the Qt5 xcb platform
|
||||
# plugin in Qt 5.15.x. The kf5-5-110-qt-5-15-11-core22 content snap does
|
||||
# not bundle it, so dlopen() of libqxcb.so fails on Ubuntu 24.04 hosts
|
||||
# with "found but could not load" (issue #373). Staging the 22.04 build
|
||||
# of this library satisfies the dep without changing base or Qt version.
|
||||
# libxcb-cursor0 workaround was needed against the Qt5/KF5 core22 content
|
||||
# snap (issue #373). Not yet re-verified against kf6-core24 — remove this
|
||||
# if the Qt6 xcb platform plugin loads cleanly without it.
|
||||
- libxcb-cursor0
|
||||
build-packages:
|
||||
- git
|
||||
- cmake
|
||||
- ninja-build
|
||||
- libsqlite3-dev
|
||||
- qt6-tools-dev
|
||||
- qt6-base-private-dev
|
||||
- pkgconf
|
||||
override-build: |
|
||||
displayed_version=$(cat sources/qetversion.cpp | grep "return QVersionNumber{"| head -n 1| awk -F "{" '{ print $2 }' | awk -F "}" '{ print $1 }' | sed -e 's/,/./g' -e 's/ //g')
|
||||
snap_version="${displayed_version}-g$(git rev-parse --short=8 HEAD)"
|
||||
modified_displayed_version="${snap_version}.snap"
|
||||
sed -i -E "s|const QString displayedVersion =.*|const QString displayedVersion =\"$modified_displayed_version\";|" sources/qet.h
|
||||
craftctl set version="$snap_version"
|
||||
qmake "$CRAFT_PART_SRC/qelectrotech.pro"
|
||||
make -j${CRAFT_PARALLEL_BUILD_COUNT}
|
||||
make install INSTALL_ROOT="$CRAFT_PART_INSTALL"
|
||||
cmake -G Ninja -B build -S "$CRAFT_PART_SRC" \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DQT_VERSION_MAJOR=6 \
|
||||
-DBUILD_WITH_KF=ON \
|
||||
-DBUILD_KF=OFF \
|
||||
-DPACKAGE_TESTS=OFF \
|
||||
-DBUILD_PUGIXML=ON \
|
||||
-DQET_EXPORT_PROJECT_DB=ON
|
||||
ninja -C build -j${CRAFT_PARALLEL_BUILD_COUNT}
|
||||
DESTDIR="$CRAFT_PART_INSTALL" ninja -C build install
|
||||
override-stage: |
|
||||
craftctl default
|
||||
# patch desktop file with correct icon path
|
||||
SED_CMD="sed -i -E s|^Icon=(.*)|Icon=\${SNAP}/usr/local/share/icons/hicolor/128x128/apps/\1.png|g"
|
||||
$SED_CMD usr/local/share/applications/org.qelectrotech.qelectrotech.desktop
|
||||
SED_CMD="sed -i -E s|^Icon=(.*)|Icon=\${SNAP}/usr/share/icons/hicolor/128x128/apps/\1.png|g"
|
||||
$SED_CMD usr/share/applications/org.qelectrotech.qelectrotech.desktop
|
||||
|
||||
cleanup:
|
||||
after: [qelectrotech, qet-tb-generator]
|
||||
plugin: nil
|
||||
build-snaps: [kf5-5-110-qt-5-15-11-core22]
|
||||
build-snaps: [kf6-core24]
|
||||
override-prime: |
|
||||
set -eux
|
||||
for snap in "kf5-5-110-qt-5-15-11-core22"; do # List all content-snaps you're using here
|
||||
for snap in "kf6-core24"; do # List all content-snaps you're using here
|
||||
cd "/snap/$snap/current" && find . -type f,l -exec rm -f "$CRAFT_PRIME/{}" "$CRAFT_PRIME/usr/{}" \;
|
||||
done
|
||||
for cruft in bug lintian man; do
|
||||
|
||||
@@ -40,8 +40,8 @@ git clone --recursive https://github.com/qelectrotech/qelectrotech-source-mirror
|
||||
Here are the technical choices made for the software development:
|
||||
|
||||
* Integrated development environment: [Qt Framework](https://www.qt.io/ide/)
|
||||
* Libraries: Qt 5.x
|
||||
* [KF5 Framework](https://github.com/KDE)
|
||||
* Libraries: Qt 5.x / Qt 6.x
|
||||
* [KF5/6 Framework](https://github.com/KDE)
|
||||
[Cmake](https://cmake.org/install/)
|
||||
[kcoreaddons](https://github.com/KDE/kcoreaddons/tree/kf5)
|
||||
[kwidgetsaddons](https://github.com/KDE/kwidgetsaddons/tree/kf5).
|
||||
|
||||
@@ -62,10 +62,10 @@ message("PROJECT_SOURCE_DIR :" ${PROJECT_SOURCE_DIR})
|
||||
message("QET_DIR :" ${QET_DIR})
|
||||
message("GIT_COMMIT_SHA :" ${GIT_COMMIT_SHA})
|
||||
|
||||
if(BUILD_WITH_KF5)
|
||||
message("KF5_GIT_TAG :" ${KF5_GIT_TAG})
|
||||
if(BUILD_WITH_KF)
|
||||
message("KF_GIT_TAG :" ${KF_GIT_TAG})
|
||||
else()
|
||||
add_definitions(-DBUILD_WITHOUT_KF5)
|
||||
add_definitions(-DBUILD_WITHOUT_KF)
|
||||
endif()
|
||||
message("QET_COMPONENTS :" ${QET_COMPONENTS})
|
||||
message("QT_VERSION_MAJOR :" ${QT_VERSION_MAJOR})
|
||||
|
||||
@@ -31,8 +31,11 @@ add_definitions(-DQT_MESSAGELOGCONTEXT)
|
||||
# In order to do so, uncomment the following line.
|
||||
#add_definitions(-DTODO_LIST)
|
||||
|
||||
# Build with KF5
|
||||
option(BUILD_WITH_KF5 "Build with KF5" ON)
|
||||
# Build with KDE Frameworks. The major version (KF5/KF6) is derived
|
||||
# automatically from QT_VERSION_MAJOR -- KDE Frameworks deliberately
|
||||
# mirrors Qt's own major version numbering, so there is no independent
|
||||
# choice to make here. See cmake/fetch_kdeaddons.cmake.
|
||||
option(BUILD_WITH_KF "Build with KDE Frameworks" ON)
|
||||
|
||||
# Precompiled headers for the Qt umbrella headers.
|
||||
#
|
||||
|
||||
+58
-36
@@ -16,52 +16,74 @@
|
||||
|
||||
message(" - fetch_kdeaddons")
|
||||
|
||||
if(BUILD_WITH_KF5)
|
||||
# TODO remove path as soon as Qt5 gets retired
|
||||
if(BUILD_WITH_KF)
|
||||
Include(FetchContent)
|
||||
|
||||
option(BUILD_KF5 "Build KF5 libraries, use system ones otherwise" YES)
|
||||
option(BUILD_KF "Build KF libraries, use system ones otherwise" YES)
|
||||
|
||||
if(BUILD_KF5)
|
||||
if(BUILD_KF)
|
||||
|
||||
if(NOT DEFINED KF5_GIT_TAG)
|
||||
#https://qelectrotech.org/forum/viewtopic.php?pid=13924#p13924
|
||||
set(KF5_GIT_TAG v5.77.0)
|
||||
if(KF_MAJOR_VERSION EQUAL 5)
|
||||
if(NOT DEFINED KF_GIT_TAG)
|
||||
#https://qelectrotech.org/forum/viewtopic.php?pid=13924#p13924
|
||||
set(KF_GIT_TAG v5.77.0)
|
||||
endif()
|
||||
else()
|
||||
if(NOT DEFINED KF_GIT_TAG)
|
||||
# this is a more or less random version, taken as an conservative approach
|
||||
set(KF_GIT_TAG v6.10.0)
|
||||
endif()
|
||||
endif()
|
||||
# using a function in order to limit the scope of the variables
|
||||
# with CMake >=3.25 we could use a block()
|
||||
function(qet_make_kf_available)
|
||||
# Fix stop the run autotests of kcoreaddons
|
||||
# see
|
||||
# https://invent.kde.org/frameworks/kcoreaddons/-/blob/master/CMakeLists.txt#L98
|
||||
# issue:
|
||||
# CMake Error at /usr/share/ECM/modules/ECMAddTests.cmake:89 (add_executable):
|
||||
# Cannot find source file:
|
||||
# see
|
||||
# https://qelectrotech.org/forum/viewtopic.php?pid=13929#p13929
|
||||
set(KDE_SKIP_TEST_SETTINGS ON)
|
||||
set(BUILD_TESTING OFF)
|
||||
|
||||
# QElectroTech is a plain QtWidgets application with no QML anywhere in
|
||||
# it; these disable optional features of the fetched KF modules that
|
||||
# would otherwise pull in extra Qt6 components (e.g. Qt6Qml) we don't
|
||||
# have and don't need.
|
||||
set(BUILD_DESIGNERPLUGIN OFF)
|
||||
set(KCOREADDONS_USE_QML OFF)
|
||||
set(BUILD_QCH OFF)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
|
||||
# Fix stop the run autotests of kcoreaddons
|
||||
# see
|
||||
# https://invent.kde.org/frameworks/kcoreaddons/-/blob/master/CMakeLists.txt#L98
|
||||
# issue:
|
||||
# CMake Error at /usr/share/ECM/modules/ECMAddTests.cmake:89 (add_executable):
|
||||
# Cannot find source file:
|
||||
# see
|
||||
# https://qelectrotech.org/forum/viewtopic.php?pid=13929#p13929
|
||||
set(KDE_SKIP_TEST_SETTINGS "TRUE")
|
||||
set(BUILD_TESTING "0")
|
||||
FetchContent_Declare(
|
||||
ecm
|
||||
GIT_REPOSITORY https://invent.kde.org/frameworks/extra-cmake-modules.git
|
||||
GIT_TAG ${KF5_GIT_TAG})
|
||||
FetchContent_MakeAvailable(ecm)
|
||||
FetchContent_Declare(
|
||||
ecm
|
||||
GIT_REPOSITORY https://invent.kde.org/frameworks/extra-cmake-modules.git
|
||||
GIT_TAG ${KF_GIT_TAG})
|
||||
FetchContent_MakeAvailable(ecm)
|
||||
|
||||
FetchContent_Declare(
|
||||
kcoreaddons
|
||||
GIT_REPOSITORY https://invent.kde.org/frameworks/kcoreaddons.git
|
||||
GIT_TAG ${KF5_GIT_TAG})
|
||||
FetchContent_MakeAvailable(kcoreaddons)
|
||||
FetchContent_Declare(
|
||||
kcoreaddons
|
||||
GIT_REPOSITORY https://invent.kde.org/frameworks/kcoreaddons.git
|
||||
GIT_TAG ${KF_GIT_TAG})
|
||||
FetchContent_MakeAvailable(kcoreaddons)
|
||||
|
||||
FetchContent_Declare(
|
||||
kwidgetsaddons
|
||||
GIT_REPOSITORY https://invent.kde.org/frameworks/kwidgetsaddons.git
|
||||
GIT_TAG ${KF5_GIT_TAG})
|
||||
FetchContent_MakeAvailable(kwidgetsaddons)
|
||||
FetchContent_Declare(
|
||||
kwidgetsaddons
|
||||
GIT_REPOSITORY https://invent.kde.org/frameworks/kwidgetsaddons.git
|
||||
GIT_TAG ${KF_GIT_TAG})
|
||||
FetchContent_MakeAvailable(kwidgetsaddons)
|
||||
endfunction()
|
||||
qet_make_kf_available()
|
||||
else()
|
||||
find_package(KF5CoreAddons REQUIRED)
|
||||
find_package(KF5WidgetsAddons REQUIRED)
|
||||
find_package(KF${KF_MAJOR_VERSION}CoreAddons REQUIRED)
|
||||
find_package(KF${KF_MAJOR_VERSION}WidgetsAddons REQUIRED)
|
||||
endif()
|
||||
|
||||
set(KF5_PRIVATE_LIBRARIES
|
||||
KF5::WidgetsAddons
|
||||
KF5::CoreAddons
|
||||
set(KF_PRIVATE_LIBRARIES
|
||||
KF${KF_MAJOR_VERSION}::WidgetsAddons
|
||||
KF${KF_MAJOR_VERSION}::CoreAddons
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -23,6 +23,14 @@ set(QAPPLICATION_CLASS QApplication)
|
||||
|
||||
Include(FetchContent)
|
||||
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/SingleApplication/CMakeLists.txt")
|
||||
# Submodule deja present dans l'arbre source (clone --recursive, tarball
|
||||
# de distro deja peuple via "git submodule update", etc.) : on l'utilise
|
||||
# tel quel, sans acces reseau. Necessaire pour les builds hors-ligne
|
||||
# (pbuilder/sbuild avec FETCHCONTENT_FULLY_DISCONNECTED=ON, Launchpad PPA...).
|
||||
set(FETCHCONTENT_SOURCE_DIR_SINGLEAPPLICATION "${CMAKE_SOURCE_DIR}/SingleApplication")
|
||||
endif()
|
||||
|
||||
FetchContent_Declare(
|
||||
SingleApplication
|
||||
GIT_REPOSITORY https://github.com/itay-grudev/SingleApplication.git
|
||||
|
||||
@@ -20,15 +20,15 @@ message(" - paths_compilation_installation")
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
# for Linux, BSD, Solaris, Minix
|
||||
set(COMPIL_PREFIX "/usr/local/")
|
||||
set(INSTALL_PREFIX "/usr/local/")
|
||||
set(COMPIL_PREFIX "${CMAKE_INSTALL_PREFIX}/")
|
||||
set(INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/")
|
||||
set(QET_BINARY_PATH "bin/")
|
||||
set(QET_COMMON_COLLECTION_PATH "share/qelectrotech/elements/")
|
||||
set(QET_COMMON_TBT_PATH "share/qelectrotech/titleblocks/")
|
||||
set(QET_LANG_PATH "share/qelectrotech/lang/")
|
||||
set(QET_EXAMPLES_PATH "share/qelectrotech/examples/")
|
||||
set(QET_LICENSE_PATH "doc/qelectrotech/")
|
||||
set(QET_MIME_PACKAGE_PATH "../share/mime/packages/")
|
||||
set(QET_MIME_PACKAGE_PATH "share/mime/packages/")
|
||||
set(QET_DESKTOP_PATH "share/applications/")
|
||||
set(QET_ICONS_PATH "share/icons/hicolor/")
|
||||
set(QET_MAN_PATH "man/")
|
||||
|
||||
@@ -33,6 +33,13 @@ set(QET_COMPONENTS
|
||||
Widgets
|
||||
Concurrent)
|
||||
|
||||
# Note: Pdf is intentionally NOT in this list. Some Qt6 distributions
|
||||
# (notably the Flatpak org.kde.Platform runtime) don't ship the QtPdf
|
||||
# module at all - it lives in the qtwebengine source tree, not Qt6 core.
|
||||
# Requesting it here as a REQUIRED component would fail the whole
|
||||
# configure on those setups. It is probed separately, QUIET and
|
||||
# non-fatal, right after the main find_package() call below.
|
||||
|
||||
set(QET_PRIVATE_LIBRARIES
|
||||
Qt::PrintSupport
|
||||
Qt::Gui
|
||||
@@ -45,6 +52,9 @@ set(QET_PRIVATE_LIBRARIES
|
||||
Qt::Concurrent
|
||||
)
|
||||
|
||||
# Qt::Pdf is appended conditionally in CMakeLists.txt, once we know
|
||||
# whether the module was actually found (see QET_HAS_QTPDF).
|
||||
|
||||
set(QET_RES_FILES
|
||||
${QET_DIR}/sources/autoNum/ui/autonumberingdockwidget.ui
|
||||
${QET_DIR}/sources/autoNum/ui/autonumberingmanagementw.ui
|
||||
@@ -91,7 +101,6 @@ set(QET_RES_FILES
|
||||
${QET_DIR}/sources/ui/configsaveloaderwidget.ui
|
||||
${QET_DIR}/sources/ui/diagramcontextwidget.ui
|
||||
${QET_DIR}/sources/ui/diagrameditorhandlersizewidget.ui
|
||||
${QET_DIR}/sources/ui/diagramselection.ui
|
||||
${QET_DIR}/sources/ui/dialogwaiting.ui
|
||||
${QET_DIR}/sources/ui/dynamicelementtextitemeditor.ui
|
||||
${QET_DIR}/sources/ui/elementinfopartwidget.ui
|
||||
@@ -177,6 +186,8 @@ set(QET_SRC_FILES
|
||||
${QET_DIR}/sources/configdialog.h
|
||||
${QET_DIR}/sources/createdxf.cpp
|
||||
${QET_DIR}/sources/createdxf.h
|
||||
${QET_DIR}/sources/dxfpaintdevice.cpp
|
||||
${QET_DIR}/sources/dxfpaintdevice.h
|
||||
${QET_DIR}/sources/diagramcommands.cpp
|
||||
${QET_DIR}/sources/diagramcommands.h
|
||||
${QET_DIR}/sources/diagramcontent.cpp
|
||||
@@ -215,6 +226,8 @@ set(QET_SRC_FILES
|
||||
${QET_DIR}/sources/exportpropertieswidget.h
|
||||
${QET_DIR}/sources/genericpanel.cpp
|
||||
${QET_DIR}/sources/genericpanel.h
|
||||
${QET_DIR}/sources/lastusedstyle.cpp
|
||||
${QET_DIR}/sources/lastusedstyle.h
|
||||
${QET_DIR}/sources/machine_info.cpp
|
||||
${QET_DIR}/sources/machine_info.h
|
||||
${QET_DIR}/sources/main.cpp
|
||||
@@ -296,6 +309,9 @@ set(QET_SRC_FILES
|
||||
${QET_DIR}/sources/dataBase/ui/summaryquerywidget.cpp
|
||||
${QET_DIR}/sources/dataBase/ui/summaryquerywidget.h
|
||||
|
||||
${QET_DIR}/sources/autobreakconductor.cpp
|
||||
${QET_DIR}/sources/autobreakconductor.h
|
||||
|
||||
${QET_DIR}/sources/diagramevent/diagrameventaddelement.cpp
|
||||
${QET_DIR}/sources/diagramevent/diagrameventaddelement.h
|
||||
${QET_DIR}/sources/diagramevent/diagrameventaddimage.cpp
|
||||
@@ -682,8 +698,6 @@ set(QET_SRC_FILES
|
||||
${QET_DIR}/sources/ui/diagrampropertiesdialog.h
|
||||
${QET_DIR}/sources/ui/diagrampropertieseditordockwidget.cpp
|
||||
${QET_DIR}/sources/ui/diagrampropertieseditordockwidget.h
|
||||
${QET_DIR}/sources/ui/diagramselection.cpp
|
||||
${QET_DIR}/sources/ui/diagramselection.h
|
||||
${QET_DIR}/sources/ui/backupdialog.cpp
|
||||
${QET_DIR}/sources/ui/backupdialog.h
|
||||
${QET_DIR}/sources/ui/dialogwaiting.cpp
|
||||
@@ -796,7 +810,7 @@ set(QET_SRC_FILES
|
||||
${QET_DIR}/sources/xml/terminalstriplayoutpatternxml.h
|
||||
)
|
||||
|
||||
if(NOT BUILD_WITH_KF5)
|
||||
if(NOT BUILD_WITH_KF)
|
||||
list(APPEND QET_SRC_FILES
|
||||
${QET_DIR}/sources/ui/nokde/kautosavefile.cpp
|
||||
${QET_DIR}/sources/ui/nokde/kautosavefile.h
|
||||
@@ -807,6 +821,16 @@ if(NOT BUILD_WITH_KF5)
|
||||
)
|
||||
endif()
|
||||
|
||||
# Qt6-only: PDF page import files
|
||||
if(QT_VERSION_MAJOR GREATER_EQUAL 6)
|
||||
list(APPEND QET_SRC_FILES
|
||||
${QET_DIR}/sources/diagramevent/diagrameventaddpdf.cpp
|
||||
${QET_DIR}/sources/diagramevent/diagrameventaddpdf.h
|
||||
${QET_DIR}/sources/ui/pdfpagesdialog.cpp
|
||||
${QET_DIR}/sources/ui/pdfpagesdialog.h
|
||||
)
|
||||
endif()
|
||||
|
||||
set(TS_FILES
|
||||
${QET_DIR}/lang/qet_ar.ts
|
||||
${QET_DIR}/lang/qet_ca.ts
|
||||
|
||||
@@ -28,5 +28,9 @@ add_definitions(-DQET_ALLOW_OVERRIDE_CD_OPTION)
|
||||
# Comment the line below to deactivate the --data-dir option
|
||||
add_definitions(-DQET_ALLOW_OVERRIDE_DD_OPTION)
|
||||
|
||||
#comment the line below to disable the project database export
|
||||
#add_definitions(-DQET_EXPORT_PROJECT_DB) #error Todo
|
||||
# Enable project database export when requested by the build system.
|
||||
option(QET_EXPORT_PROJECT_DB "Enable project database export" OFF)
|
||||
|
||||
if(QET_EXPORT_PROJECT_DB)
|
||||
add_compile_definitions(QET_EXPORT_PROJECT_DB)
|
||||
endif()
|
||||
|
||||
+1
-1
Submodule elements updated: 9cc2f069f7...74bdb77844
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
+1234
-998
File diff suppressed because it is too large
Load Diff
+1226
-999
File diff suppressed because it is too large
Load Diff
+1228
-998
File diff suppressed because it is too large
Load Diff
+1226
-999
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+1553
-1320
File diff suppressed because it is too large
Load Diff
+1226
-999
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+1231
-1000
File diff suppressed because it is too large
Load Diff
+1226
-999
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+1226
-997
File diff suppressed because it is too large
Load Diff
+1227
-996
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+1763
-1482
File diff suppressed because it is too large
Load Diff
+1226
-998
File diff suppressed because it is too large
Load Diff
+1224
-999
File diff suppressed because it is too large
Load Diff
+1223
-996
File diff suppressed because it is too large
Load Diff
+1226
-999
File diff suppressed because it is too large
Load Diff
+1226
-998
File diff suppressed because it is too large
Load Diff
+1226
-999
File diff suppressed because it is too large
Load Diff
+1225
-996
File diff suppressed because it is too large
Load Diff
+1228
-999
File diff suppressed because it is too large
Load Diff
+1225
-999
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+1613
-1354
File diff suppressed because it is too large
Load Diff
+1227
-996
File diff suppressed because it is too large
Load Diff
+1227
-996
File diff suppressed because it is too large
Load Diff
+1228
-1001
File diff suppressed because it is too large
Load Diff
+1227
-996
File diff suppressed because it is too large
Load Diff
+1229
-996
File diff suppressed because it is too large
Load Diff
+1227
-996
File diff suppressed because it is too large
Load Diff
+1226
-999
File diff suppressed because it is too large
Load Diff
+1224
-999
File diff suppressed because it is too large
Load Diff
+1228
-999
File diff suppressed because it is too large
Load Diff
+1490
-1244
File diff suppressed because it is too large
Load Diff
@@ -140,6 +140,7 @@
|
||||
<file>ico/22x22/go-up.png</file>
|
||||
<file>ico/22x22/hotspot.png</file>
|
||||
<file>ico/22x22/insert-image.png</file>
|
||||
<file>ico/22x22/pdf-import.png</file>
|
||||
<file>ico/22x22/label.png</file>
|
||||
<file>ico/22x22/landscape.png</file>
|
||||
<file>ico/22x22/line.png</file>
|
||||
|
||||
@@ -38,6 +38,20 @@ ElementsCollectionModel::ElementsCollectionModel(QObject *parent) :
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementsCollectionModel::~ElementsCollectionModel
|
||||
Destructor. loadCollections() may still have background threads
|
||||
(via QtConcurrent::map()) running setUpData() on this model's items
|
||||
when the model is destroyed (e.g. the user cancels the dialog before
|
||||
loading finishes). Wait for them here so QStandardItemModel's
|
||||
destructor doesn't free items out from under them, which used to
|
||||
crash the whole application (bugtracker #291).
|
||||
*/
|
||||
ElementsCollectionModel::~ElementsCollectionModel()
|
||||
{
|
||||
m_future.waitForFinished();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementsCollectionModel::data
|
||||
Reimplemented from QStandardItemModel
|
||||
@@ -49,6 +63,7 @@ QVariant ElementsCollectionModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (role == Qt::DecorationRole) {
|
||||
QStandardItem *item = itemFromIndex(index);
|
||||
if (!item) return QStandardItemModel::data(index, role);
|
||||
|
||||
if (item->type() == FileElementCollectionItem::Type)
|
||||
static_cast<FileElementCollectionItem*>(item)->setUpIcon();
|
||||
@@ -316,7 +331,6 @@ void ElementsCollectionModel::loadMacrosCollection()
|
||||
void ElementsCollectionModel::addMacrosCollection(bool set_data)
|
||||
{
|
||||
QString macrosPath = QETApp::userMacrosDir();
|
||||
qDebug() << "=== MAKRO PFAD CHECK ===" << macrosPath;
|
||||
if (macrosPath.endsWith("/")) {
|
||||
macrosPath.remove(macrosPath.length() - 1, 1);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ class ElementsCollectionModel : public QStandardItemModel
|
||||
|
||||
public:
|
||||
ElementsCollectionModel(QObject *parent = Q_NULLPTR);
|
||||
~ElementsCollectionModel() override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||
|
||||
@@ -949,14 +949,7 @@ void ElementsCollectionWidget::search()
|
||||
}
|
||||
|
||||
hideCollection(true);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) // ### Qt 6: remove
|
||||
const QStringList text_list = text.split("+", QString::SkipEmptyParts);
|
||||
#else
|
||||
#if TODO_LIST
|
||||
#pragma message("@TODO remove code for QT 5.14 or later")
|
||||
#endif
|
||||
const QStringList text_list = text.split("+", Qt::SkipEmptyParts);
|
||||
#endif
|
||||
QModelIndexList match_index;
|
||||
for (QString txt : text_list) {
|
||||
match_index << m_model->match(m_showed_index.isValid()
|
||||
|
||||
@@ -143,11 +143,7 @@ void ElementsTreeView::startElementDrag(const ElementsLocation &location)
|
||||
QString file_name = (last_slash != -1) ? path.mid(last_slash + 1) : path;
|
||||
|
||||
if (!dir_path.isEmpty()) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||
QStringList parts = dir_path.split('/', QString::SkipEmptyParts);
|
||||
#else
|
||||
QStringList parts = dir_path.split('/', Qt::SkipEmptyParts);
|
||||
#endif
|
||||
QString current_path = "";
|
||||
for (const QString &part : parts) {
|
||||
QString parent_path = current_path;
|
||||
|
||||
@@ -430,7 +430,7 @@ void FileElementCollectionItem::setUpData()
|
||||
*/
|
||||
void FileElementCollectionItem::setUpIcon()
|
||||
{
|
||||
// Must return unconditionally once an icon is set: setIcon() calls
|
||||
// Must return unconditionally once setUpIcon has run: setIcon() calls
|
||||
// setData(), which emits dataChanged() regardless of whether the new
|
||||
// icon differs from the old one (QIcon has no meaningful equality).
|
||||
// QTreeView responds to dataChanged() by recomputing the row's size
|
||||
@@ -438,13 +438,18 @@ void FileElementCollectionItem::setUpIcon()
|
||||
// guard, any repeated setIcon() here recurses until the stack
|
||||
// overflows. Confirmed by crash report on PR #633.
|
||||
//
|
||||
// We use a dedicated bool instead of icon().isNull() because
|
||||
// some items intentionally keep a null icon (e.g. .qetmak),
|
||||
// which would bypass a null-based guard and still recurse.
|
||||
//
|
||||
// This item's m_qet_directory_unreadable is already final by the time
|
||||
// this can run at all: ElementsCollectionModel only attaches itself
|
||||
// to the tree view (making data() reachable) from loadingFinished(),
|
||||
// which fires after the QtConcurrent::map over every item -- this one
|
||||
// included -- has completed. So there is no race to work around here.
|
||||
if (!icon().isNull())
|
||||
if (m_icon_initialized)
|
||||
return;
|
||||
m_icon_initialized = true;
|
||||
|
||||
if (isCollectionRoot()) {
|
||||
QString macrosPath = QETApp::userMacrosDir();
|
||||
|
||||
@@ -69,6 +69,7 @@ class FileElementCollectionItem : public ElementCollectionItem
|
||||
/// rather than acted on in localName(), because setUpData() resets
|
||||
/// the tooltip afterwards and would otherwise discard it.
|
||||
bool m_qet_directory_unreadable = false;
|
||||
bool m_icon_initialized = false;
|
||||
};
|
||||
|
||||
#endif // FILEELEMENTCOLLECTIONITEM2_H
|
||||
|
||||
@@ -46,6 +46,18 @@ class PropertiesEditorDialog : public QDialog
|
||||
PropertiesEditorDialog(T editor, QWidget *parent = nullptr) :
|
||||
QDialog (parent)
|
||||
{
|
||||
// Without this, a QWidget-modal QDialog with a parent falls
|
||||
// back to macOS's automatic sheet presentation, which some
|
||||
// Qt/Cocoa versions render stuck centered on screen and
|
||||
// non-draggable rather than as a proper attached sheet
|
||||
// (bugtracker #275). Other dialogs in this codebase already
|
||||
// opt in explicitly (see ElementDialog, DiagramPropertiesDialog)
|
||||
// -- this one was missing it.
|
||||
setWindowModality(Qt::WindowModal);
|
||||
#ifdef Q_OS_MACOS
|
||||
setWindowFlags(Qt::Sheet);
|
||||
#endif
|
||||
|
||||
//Set dialog title
|
||||
setWindowTitle(editor->title());
|
||||
// Reparent the editor,
|
||||
|
||||
@@ -1020,6 +1020,7 @@ void SearchAndReplaceWidget::on_m_tree_widget_currentItemChanged(
|
||||
{
|
||||
m_highlighted_element = elmt;
|
||||
elmt.data()->setHighlighted(true);
|
||||
elmt.data()->ensureVisible();
|
||||
}
|
||||
}
|
||||
else if (m_text_hash.contains(current))
|
||||
@@ -1029,6 +1030,7 @@ void SearchAndReplaceWidget::on_m_tree_widget_currentItemChanged(
|
||||
{
|
||||
text.data()->setSelected(true);
|
||||
m_last_selected = text;
|
||||
text.data()->ensureVisible();
|
||||
}
|
||||
}
|
||||
else if (m_conductor_hash.contains(current))
|
||||
@@ -1038,6 +1040,7 @@ void SearchAndReplaceWidget::on_m_tree_widget_currentItemChanged(
|
||||
{
|
||||
cond.data()->setSelected(true);
|
||||
m_last_selected = cond;
|
||||
cond.data()->ensureVisible();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define TERMINALSTRIPDRAWER_H
|
||||
|
||||
#include <QPointer>
|
||||
#include <QUuid>
|
||||
|
||||
#include "properties/terminalstriplayoutpattern.h"
|
||||
|
||||
|
||||
@@ -130,12 +130,7 @@ bool PhysicalTerminal::setLevelOf(const QSharedPointer<RealTerminal> &terminal,
|
||||
const int i = m_real_terminal.indexOf(terminal);
|
||||
if (i >= 0)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
|
||||
m_real_terminal.swapItemsAt(i, std::min<int>(level, static_cast<int>(m_real_terminal.size())-1));
|
||||
#else
|
||||
auto j = std::min(level, m_real_terminal.size()-1);
|
||||
std::swap(m_real_terminal.begin()[i], m_real_terminal.begin()[j]);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -64,11 +64,8 @@ bool TerminalStripData::fromXml(const QDomElement &xml_element)
|
||||
"due to wrong tag name. Expected " << this->xmlTagName() << " used " << xml_element.tagName();
|
||||
return false;
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
|
||||
m_uuid = QUuid::fromString(xml_element.attribute(QStringLiteral("uuid")));
|
||||
#else
|
||||
m_uuid = QUuid(xml_element.attribute(QStringLiteral("uuid")));
|
||||
#endif
|
||||
|
||||
for (auto &xml_info :
|
||||
QETXML::findInDomElement(xml_element.firstChildElement(QStringLiteral("informations")),
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "../terminalstrip.h"
|
||||
#include "../../qetinformation.h"
|
||||
|
||||
#include <QUuid>
|
||||
|
||||
TerminalStripTreeDockWidget::TerminalStripTreeDockWidget(QETProject *project, QWidget *parent) :
|
||||
QDockWidget(parent),
|
||||
ui(new Ui::TerminalStripTreeDockWidget)
|
||||
@@ -35,11 +37,7 @@ TerminalStripTreeDockWidget::TerminalStripTreeDockWidget(QETProject *project, QW
|
||||
ui->setupUi(this);
|
||||
setProject(project);
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
|
||||
ui->m_tree_view->expandRecursively(ui->m_tree_view->rootIndex());
|
||||
#else
|
||||
ui->m_tree_view->expandAll();
|
||||
#endif
|
||||
}
|
||||
|
||||
TerminalStripTreeDockWidget::~TerminalStripTreeDockWidget()
|
||||
@@ -93,11 +91,7 @@ void TerminalStripTreeDockWidget::reload()
|
||||
|
||||
buildTree();
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
|
||||
ui->m_tree_view->expandRecursively(ui->m_tree_view->rootIndex());
|
||||
#else
|
||||
ui->m_tree_view->expandAll();
|
||||
#endif
|
||||
|
||||
//Reselect the tree widget item of the current edited strip
|
||||
auto item = m_item_strip_H.key(current_);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QPointer>
|
||||
#include <QHash>
|
||||
|
||||
class QETProject;
|
||||
class QTreeWidgetItem;
|
||||
|
||||
@@ -220,6 +220,24 @@ void NumerotationContext::replaceValue(int index, QString content) {
|
||||
content_[index] = type + "|" + value + "|" + increase + "|" + initvalue + "|" + modulus + "|" + format;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief NumerotationContext::replaceIncrease
|
||||
Change how much this part advances per step, leaving its current value,
|
||||
initial value, modulus and format untouched. Sibling to replaceValue(),
|
||||
which deliberately never touches this field.
|
||||
@param index of NC item
|
||||
@param increase new increase for that item
|
||||
*/
|
||||
void NumerotationContext::replaceIncrease(int index, int increase) {
|
||||
QStringList strl = content_[index].split("|");
|
||||
QString type = strl.at(0);
|
||||
QString value = strl.at(1);
|
||||
QString initvalue = strl.at(3);
|
||||
QString modulus = strl.size() > 4 ? strl.at(4) : QStringLiteral("0");
|
||||
QString format = strl.size() > 5 ? strl.at(5) : QString();
|
||||
content_[index] = type + "|" + value + "|" + QString::number(increase) + "|" + initvalue + "|" + modulus + "|" + format;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief NumerotationContext::formatOf
|
||||
@param item : a context item as returned by itemAt()
|
||||
@@ -230,3 +248,30 @@ QString NumerotationContext::formatOf(const QStringList &item)
|
||||
{
|
||||
return item.size() > 5 ? item.at(5) : QString();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief NumerotationContext::formatValue
|
||||
@param item : a context item as returned by itemAt()
|
||||
@return the part's value, zero-padded exactly as
|
||||
autonum::setSequentialToList() pads it when composing a real label: an
|
||||
explicit format mask wins, then "ten"/"hundred" parts get their
|
||||
implicit 2/3-digit width, "alpha" is used as-is, everything else is a
|
||||
plain number. Kept in step with that function by hand since the two
|
||||
cannot share code without exposing an assignvariables.cpp-local helper.
|
||||
*/
|
||||
QString NumerotationContext::formatValue(const QStringList &item)
|
||||
{
|
||||
const QString &type = item.at(0);
|
||||
const QString &value = item.at(1);
|
||||
if (type == QLatin1String("alpha"))
|
||||
return value;
|
||||
|
||||
const QString mask = formatOf(item);
|
||||
if (!mask.isEmpty())
|
||||
return QString("%1").arg(value.toInt(), mask.length(), 10, QChar('0'));
|
||||
if (type == QLatin1String("ten") || type == QLatin1String("tenfolio"))
|
||||
return QString("%1").arg(value.toInt(), 2, 10, QChar('0'));
|
||||
if (type == QLatin1String("hundred") || type == QLatin1String("hundredfolio"))
|
||||
return QString("%1").arg(value.toInt(), 3, 10, QChar('0'));
|
||||
return QString::number(value.toInt());
|
||||
}
|
||||
|
||||
@@ -54,6 +54,11 @@ class NumerotationContext
|
||||
QDomElement toXml(QDomDocument &, const QString&);
|
||||
void fromXml(QDomElement &);
|
||||
void replaceValue(int, QString);
|
||||
void replaceIncrease(int, int);
|
||||
/// Zero-pad a part's value the same way the real numbering engine
|
||||
/// does (autonum::setSequentialToList in assignvariables.cpp), so a
|
||||
/// UI preview of a part's value matches what actually gets rendered.
|
||||
static QString formatValue(const QStringList &item);
|
||||
|
||||
private:
|
||||
QStringList content_;
|
||||
|
||||
@@ -24,10 +24,14 @@
|
||||
#include "../../titleblockproperties.h"
|
||||
#include "../../ui/projectpropertiesdialog.h"
|
||||
#include "../numerotationcontext.h"
|
||||
#include "../numerotationcontextcommands.h"
|
||||
#include "ui_autonumberingdockwidget.h"
|
||||
#include "../../undocommand/changetitleblockcommand.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QLineEdit>
|
||||
#include <QSignalBlocker>
|
||||
#include <QSpinBox>
|
||||
|
||||
/**
|
||||
@brief AutoNumberingDockWidget::AutoNumberingDockWidget
|
||||
@@ -64,6 +68,29 @@ void AutoNumberingDockWidget::clear()
|
||||
ui->m_conductor_value_le->clear();
|
||||
ui->m_element_value_le->clear();
|
||||
ui->m_folio_value_le->clear();
|
||||
ui->m_conductor_next_le->clear();
|
||||
ui->m_element_next_le->clear();
|
||||
ui->m_folio_next_le->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief AutoNumberingDockWidget::rowFor
|
||||
@return the combo/value/increase/next widgets that make up category's row.
|
||||
*/
|
||||
AutoNumberingDockWidget::Row AutoNumberingDockWidget::rowFor(AutoNumCategory category) const
|
||||
{
|
||||
switch (category) {
|
||||
case AutoNumCategory::Conductor:
|
||||
return {ui->m_conductor_cb, ui->m_conductor_value_le,
|
||||
ui->m_conductor_increase_sb, ui->m_conductor_next_le};
|
||||
case AutoNumCategory::Element:
|
||||
return {ui->m_element_cb, ui->m_element_value_le,
|
||||
ui->m_element_increase_sb, ui->m_element_next_le};
|
||||
case AutoNumCategory::Folio:
|
||||
return {ui->m_folio_cb, ui->m_folio_value_le,
|
||||
ui->m_folio_increase_sb, ui->m_folio_next_le};
|
||||
}
|
||||
return {nullptr, nullptr, nullptr, nullptr};
|
||||
}
|
||||
|
||||
void AutoNumberingDockWidget::projectClosed()
|
||||
@@ -86,33 +113,19 @@ void AutoNumberingDockWidget::setProject(QETProject *project,
|
||||
//Disconnect previous project
|
||||
if (m_project && m_project_view)
|
||||
{
|
||||
//Conductor Signals
|
||||
disconnect(m_project, SIGNAL(conductorAutoNumChanged()),
|
||||
this,SLOT(conductorAutoNumChanged()));
|
||||
disconnect (m_project,SIGNAL(conductorAutoNumRemoved()),
|
||||
this,SLOT(conductorAutoNumChanged()));
|
||||
disconnect (m_project,SIGNAL(conductorAutoNumAdded()),
|
||||
this,SLOT(conductorAutoNumChanged()));
|
||||
disconnect(m_project_view,SIGNAL(diagramActivated(DiagramView*)),
|
||||
this,SLOT(setConductorActive(DiagramView*)));
|
||||
|
||||
//Element Signals
|
||||
disconnect (m_project,SIGNAL(elementAutoNumRemoved(QString)),
|
||||
this,SLOT(elementAutoNumChanged()));
|
||||
disconnect (m_project,SIGNAL(elementAutoNumAdded(QString)),
|
||||
this,SLOT(elementAutoNumChanged()));
|
||||
disconnect(m_project, &QETProject::conductorAutoNumChanged, this, &AutoNumberingDockWidget::conductorAutoNumChanged);
|
||||
disconnect(m_project, &QETProject::conductorAutoNumRemoved, this, &AutoNumberingDockWidget::conductorAutoNumChanged);
|
||||
disconnect(m_project, &QETProject::conductorAutoNumAdded, this, &AutoNumberingDockWidget::conductorAutoNumChanged);
|
||||
disconnect(m_project_view, &ProjectView::diagramActivated, this, &AutoNumberingDockWidget::setConductorActive);
|
||||
|
||||
//Element Signals
|
||||
disconnect(m_project, &QETProject::elementAutoNumRemoved, this, &AutoNumberingDockWidget::elementAutoNumChanged);
|
||||
disconnect(m_project, &QETProject::elementAutoNumAdded, this, &AutoNumberingDockWidget::elementAutoNumChanged);
|
||||
|
||||
//Folio Signals
|
||||
disconnect (m_project,SIGNAL(folioAutoNumRemoved()),
|
||||
this,SLOT(folioAutoNumChanged()));
|
||||
disconnect (m_project,SIGNAL(folioAutoNumAdded()),
|
||||
this,SLOT(folioAutoNumChanged()));
|
||||
disconnect (this,
|
||||
SIGNAL(folioAutoNumChanged(QString)),
|
||||
&m_project_view->currentDiagram()->diagram()->border_and_titleblock,
|
||||
SLOT (slot_setAutoPageNum(QString)));
|
||||
disconnect(m_project, SIGNAL(defaultTitleBlockPropertiesChanged()),
|
||||
this,SLOT(setActive()));
|
||||
disconnect(m_project, &QETProject::folioAutoNumRemoved, this, qOverload<>(&AutoNumberingDockWidget::folioAutoNumChanged));
|
||||
disconnect(m_project, &QETProject::folioAutoNumAdded, this, qOverload<>(&AutoNumberingDockWidget::folioAutoNumChanged));
|
||||
disconnect(m_project, &QETProject::defaultTitleBlockPropertiesChanged, this, &AutoNumberingDockWidget::setActive);
|
||||
|
||||
//Conductor, Element and Folio Signals
|
||||
disconnect(m_project, &QETProject::autoNumContextUpdated,
|
||||
@@ -125,33 +138,19 @@ void AutoNumberingDockWidget::setProject(QETProject *project,
|
||||
m_project_view = projectview;
|
||||
this->setEnabled(true);
|
||||
|
||||
//Conductor Signals
|
||||
connect(m_project, SIGNAL(conductorAutoNumChanged()),
|
||||
this,SLOT(conductorAutoNumChanged()));
|
||||
connect(m_project,SIGNAL(conductorAutoNumRemoved()),
|
||||
this,SLOT(conductorAutoNumChanged()));
|
||||
connect(m_project,SIGNAL(conductorAutoNumAdded()),
|
||||
this,SLOT(conductorAutoNumChanged()));
|
||||
connect(m_project_view,SIGNAL(diagramActivated(DiagramView*)),
|
||||
this,SLOT(setConductorActive(DiagramView*)));
|
||||
connect(m_project, &QETProject::conductorAutoNumChanged, this, &AutoNumberingDockWidget::conductorAutoNumChanged);
|
||||
connect(m_project, &QETProject::conductorAutoNumRemoved, this, &AutoNumberingDockWidget::conductorAutoNumChanged);
|
||||
connect(m_project, &QETProject::conductorAutoNumAdded, this, &AutoNumberingDockWidget::conductorAutoNumChanged);
|
||||
connect(m_project_view, &ProjectView::diagramActivated, this, &AutoNumberingDockWidget::setConductorActive);
|
||||
|
||||
//Element Signals
|
||||
connect (m_project,SIGNAL(elementAutoNumRemoved(QString)),
|
||||
this,SLOT(elementAutoNumChanged()));
|
||||
connect (m_project,SIGNAL(elementAutoNumAdded(QString)),
|
||||
this,SLOT(elementAutoNumChanged()));
|
||||
//Element Signals
|
||||
connect(m_project, &QETProject::elementAutoNumRemoved, this, &AutoNumberingDockWidget::elementAutoNumChanged);
|
||||
connect(m_project, &QETProject::elementAutoNumAdded, this, &AutoNumberingDockWidget::elementAutoNumChanged);
|
||||
|
||||
//Folio Signals
|
||||
connect (m_project,SIGNAL(folioAutoNumRemoved()),
|
||||
this,SLOT(folioAutoNumChanged()));
|
||||
connect (m_project,SIGNAL(folioAutoNumAdded()),
|
||||
this,SLOT(folioAutoNumChanged()));
|
||||
connect (this,
|
||||
SIGNAL(folioAutoNumChanged(QString)),
|
||||
&m_project_view->currentDiagram()->diagram()->border_and_titleblock,
|
||||
SLOT (slot_setAutoPageNum(QString)));
|
||||
connect(m_project, SIGNAL(defaultTitleBlockPropertiesChanged()),
|
||||
this,SLOT(setActive()));
|
||||
connect(m_project, &QETProject::folioAutoNumRemoved, this, qOverload<>(&AutoNumberingDockWidget::folioAutoNumChanged));
|
||||
connect(m_project, &QETProject::folioAutoNumAdded, this, qOverload<>(&AutoNumberingDockWidget::folioAutoNumChanged));
|
||||
connect(m_project, &QETProject::defaultTitleBlockPropertiesChanged, this, &AutoNumberingDockWidget::setActive);
|
||||
|
||||
//Conductor, Element and Folio Signals
|
||||
connect(m_project, &QETProject::autoNumContextUpdated,
|
||||
@@ -201,9 +200,9 @@ void AutoNumberingDockWidget::setContext()
|
||||
|
||||
//The combo boxes have just been repopulated, so the value fields next
|
||||
//to them are showing whatever the previous project left there.
|
||||
refreshValueField(ui->m_conductor_cb, ui->m_conductor_value_le, AutoNumCategory::Conductor);
|
||||
refreshValueField(ui->m_element_cb, ui->m_element_value_le, AutoNumCategory::Element);
|
||||
refreshValueField(ui->m_folio_cb, ui->m_folio_value_le, AutoNumCategory::Folio);
|
||||
refreshRow(AutoNumCategory::Conductor);
|
||||
refreshRow(AutoNumCategory::Element);
|
||||
refreshRow(AutoNumCategory::Folio);
|
||||
|
||||
this->setActive();
|
||||
}
|
||||
@@ -279,7 +278,7 @@ void AutoNumberingDockWidget::on_m_conductor_cb_activated(int)
|
||||
m_project->setCurrentConductorAutoNum(current_autonum);
|
||||
m_project_view->currentDiagram()->diagram()->setConductorsAutonumName(current_autonum);
|
||||
m_project_view->currentDiagram()->diagram()->loadCndFolioSeq();
|
||||
refreshValueField(ui->m_conductor_cb, ui->m_conductor_value_le, AutoNumCategory::Conductor);
|
||||
refreshRow(AutoNumCategory::Conductor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,7 +307,7 @@ void AutoNumberingDockWidget::on_m_element_cb_activated(int)
|
||||
{
|
||||
m_project->setCurrrentElementAutonum(ui->m_element_cb->currentText());
|
||||
m_project_view->currentDiagram()->diagram()->loadElmtFolioSeq();
|
||||
refreshValueField(ui->m_element_cb, ui->m_element_value_le, AutoNumCategory::Element);
|
||||
refreshRow(AutoNumCategory::Element);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -345,8 +344,17 @@ void AutoNumberingDockWidget::on_m_folio_cb_activated(int) {
|
||||
ip.folio = "%id/%total";
|
||||
m_project->setDefaultTitleBlockProperties(ip);
|
||||
}
|
||||
emit(folioAutoNumChanged(current_autonum));
|
||||
refreshValueField(ui->m_folio_cb, ui->m_folio_value_le, AutoNumCategory::Folio);
|
||||
|
||||
if (m_project_view && m_project_view->currentDiagram()) {
|
||||
Diagram *diagram = m_project_view->currentDiagram()->diagram();
|
||||
TitleBlockProperties old_properties = diagram->border_and_titleblock.exportTitleBlock();
|
||||
TitleBlockProperties new_properties = old_properties;
|
||||
new_properties.auto_page_num = ip.auto_page_num;
|
||||
new_properties.folio = ip.folio;
|
||||
if (new_properties != old_properties)
|
||||
diagram->undoStack().push(new ChangeTitleBlockCommand(diagram, old_properties, new_properties));
|
||||
}
|
||||
refreshRow(AutoNumCategory::Folio);
|
||||
}
|
||||
|
||||
void AutoNumberingDockWidget::on_m_configure_pb_clicked()
|
||||
@@ -389,6 +397,21 @@ void AutoNumberingDockWidget::on_m_folio_value_le_editingFinished()
|
||||
applyValueField(ui->m_folio_cb, ui->m_folio_value_le, AutoNumCategory::Folio);
|
||||
}
|
||||
|
||||
void AutoNumberingDockWidget::on_m_conductor_increase_sb_valueChanged(int)
|
||||
{
|
||||
applyIncreaseField(ui->m_conductor_cb, ui->m_conductor_increase_sb, AutoNumCategory::Conductor);
|
||||
}
|
||||
|
||||
void AutoNumberingDockWidget::on_m_element_increase_sb_valueChanged(int)
|
||||
{
|
||||
applyIncreaseField(ui->m_element_cb, ui->m_element_increase_sb, AutoNumCategory::Element);
|
||||
}
|
||||
|
||||
void AutoNumberingDockWidget::on_m_folio_increase_sb_valueChanged(int)
|
||||
{
|
||||
applyIncreaseField(ui->m_folio_cb, ui->m_folio_increase_sb, AutoNumCategory::Folio);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief AutoNumberingDockWidget::contextFor
|
||||
@return the numerotation context named by combo_box, for category
|
||||
@@ -455,17 +478,21 @@ int AutoNumberingDockWidget::counterIndex(const NumerotationContext &context)
|
||||
*/
|
||||
void AutoNumberingDockWidget::refreshValueFields()
|
||||
{
|
||||
//Leave alone a field the user is typing in: numbering an element
|
||||
//refreshes all three, and overwriting a half-typed value under the
|
||||
//cursor is worse than showing it a moment out of date. Only this
|
||||
//automatic path skips; an explicit refresh after a reset or an edit
|
||||
//still writes, so the field always ends up canonical.
|
||||
if (!ui->m_conductor_value_le->hasFocus())
|
||||
refreshValueField(ui->m_conductor_cb, ui->m_conductor_value_le, AutoNumCategory::Conductor);
|
||||
if (!ui->m_element_value_le->hasFocus())
|
||||
refreshValueField(ui->m_element_cb, ui->m_element_value_le, AutoNumCategory::Element);
|
||||
if (!ui->m_folio_value_le->hasFocus())
|
||||
refreshValueField(ui->m_folio_cb, ui->m_folio_value_le, AutoNumCategory::Folio);
|
||||
//Leave alone a row the user is typing in: numbering an element
|
||||
//refreshes all three rows, and overwriting a half-typed value or
|
||||
//increment under the cursor is worse than showing it a moment out
|
||||
//of date. Only this automatic path skips; an explicit refresh after
|
||||
//a reset or an edit still writes, so the row always ends up
|
||||
//canonical. The next-value preview has no such guard: it is
|
||||
//read-only, so there is nothing a refresh could clobber.
|
||||
for (AutoNumCategory category : {AutoNumCategory::Conductor,
|
||||
AutoNumCategory::Element,
|
||||
AutoNumCategory::Folio})
|
||||
{
|
||||
const Row row = rowFor(category);
|
||||
if (!row.value->hasFocus() && !row.increase->hasFocus())
|
||||
refreshRow(category);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -507,13 +534,114 @@ void AutoNumberingDockWidget::applyValueField(QComboBox *combo_box, QLineEdit *l
|
||||
const QString typed = line_edit->text();
|
||||
if (typed.isEmpty() || typed == context.itemAt(index).at(1))
|
||||
{
|
||||
refreshValueField(combo_box, line_edit, category);
|
||||
refreshRow(category);
|
||||
return;
|
||||
}
|
||||
|
||||
context.replaceValue(index, typed);
|
||||
storeContext(combo_box, category, context);
|
||||
refreshValueField(combo_box, line_edit, category);
|
||||
refreshRow(category);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief AutoNumberingDockWidget::refreshIncreaseField
|
||||
Show the counter's current step size (bug #331: previously only
|
||||
reachable from the full configuration dialog, via "Configurer").
|
||||
*/
|
||||
void AutoNumberingDockWidget::refreshIncreaseField(QComboBox *combo_box, QSpinBox *increase_sb, AutoNumCategory category)
|
||||
{
|
||||
//QSpinBox::setValue() emits valueChanged() even when called
|
||||
//programmatically. Without blocking it, this refresh would
|
||||
//immediately re-trigger on_..._increase_sb_valueChanged() ->
|
||||
//applyIncreaseField() -> storeContext() -> the project's
|
||||
//autoNumContextUpdated signal -> refreshValueFields() -> back here.
|
||||
const QSignalBlocker blocker(increase_sb);
|
||||
if (!m_project || combo_box->currentText().isEmpty())
|
||||
{
|
||||
increase_sb->setEnabled(false);
|
||||
increase_sb->setValue(increase_sb->minimum());
|
||||
return;
|
||||
}
|
||||
|
||||
const NumerotationContext context = contextFor(combo_box, category);
|
||||
const int index = counterIndex(context);
|
||||
increase_sb->setEnabled(index >= 0);
|
||||
increase_sb->setValue(index >= 0 ? context.itemAt(index).at(2).toInt()
|
||||
: increase_sb->minimum());
|
||||
}
|
||||
|
||||
/**
|
||||
@brief AutoNumberingDockWidget::applyIncreaseField
|
||||
Write the spin box's step size to the counter it displays (bug #331).
|
||||
*/
|
||||
void AutoNumberingDockWidget::applyIncreaseField(QComboBox *combo_box, QSpinBox *increase_sb, AutoNumCategory category)
|
||||
{
|
||||
if (!m_project || combo_box->currentText().isEmpty())
|
||||
return;
|
||||
|
||||
NumerotationContext context = contextFor(combo_box, category);
|
||||
const int index = counterIndex(context);
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
if (increase_sb->value() == context.itemAt(index).at(2).toInt())
|
||||
return;
|
||||
|
||||
context.replaceIncrease(index, increase_sb->value());
|
||||
storeContext(combo_box, category, context);
|
||||
refreshRow(category);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief AutoNumberingDockWidget::refreshNextField
|
||||
Show what this counter will read after one more step (bug #331: "visualiser
|
||||
la prochaine numérotation qui sera appliquée"). Advances a copy of the
|
||||
whole context through NumerotationContextCommands -- the same engine the
|
||||
Suivant button in the full configuration dialog uses to step a context --
|
||||
so wrap-and-carry into this part from a following part, or out of it into
|
||||
a preceding one, comes out identical to what will actually happen when the
|
||||
number is next consumed.
|
||||
*/
|
||||
void AutoNumberingDockWidget::refreshNextField(QComboBox *combo_box, QLineEdit *next_edit, AutoNumCategory category)
|
||||
{
|
||||
if (!m_project || combo_box->currentText().isEmpty())
|
||||
{
|
||||
next_edit->clear();
|
||||
next_edit->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const NumerotationContext context = contextFor(combo_box, category);
|
||||
const int index = counterIndex(context);
|
||||
if (index < 0)
|
||||
{
|
||||
next_edit->clear();
|
||||
next_edit->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Diagram *diagram = (m_project_view && m_project_view->currentDiagram())
|
||||
? m_project_view->currentDiagram()->diagram()
|
||||
: nullptr;
|
||||
NumerotationContextCommands ncc(context, diagram);
|
||||
const NumerotationContext next_context = ncc.next();
|
||||
|
||||
next_edit->setEnabled(true);
|
||||
next_edit->setText(NumerotationContext::formatValue(next_context.itemAt(index)));
|
||||
}
|
||||
|
||||
/**
|
||||
@brief AutoNumberingDockWidget::refreshRow
|
||||
Refresh a category's value, increment and next-value preview together --
|
||||
every call site that used to refresh just the value field needs the
|
||||
other two kept in step with it as well.
|
||||
*/
|
||||
void AutoNumberingDockWidget::refreshRow(AutoNumCategory category)
|
||||
{
|
||||
const Row row = rowFor(category);
|
||||
refreshValueField(row.combo, row.value, category);
|
||||
refreshIncreaseField(row.combo, row.increase, category);
|
||||
refreshNextField(row.combo, row.next, category);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -557,10 +685,5 @@ void AutoNumberingDockWidget::resetAutoNum(QComboBox *combo_box, AutoNumCategory
|
||||
}
|
||||
|
||||
storeContext(combo_box, category, context);
|
||||
|
||||
switch (category) {
|
||||
case AutoNumCategory::Conductor: refreshValueField(combo_box, ui->m_conductor_value_le, category); break;
|
||||
case AutoNumCategory::Element: refreshValueField(combo_box, ui->m_element_value_le, category); break;
|
||||
case AutoNumCategory::Folio: refreshValueField(combo_box, ui->m_folio_value_le, category); break;
|
||||
}
|
||||
refreshRow(category);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
class QComboBox;
|
||||
class QLineEdit;
|
||||
class QSpinBox;
|
||||
|
||||
namespace Ui {
|
||||
class AutoNumberingDockWidget;
|
||||
@@ -66,12 +67,27 @@ class AutoNumberingDockWidget : public QDockWidget
|
||||
void on_m_element_value_le_editingFinished();
|
||||
void on_m_folio_value_le_editingFinished();
|
||||
|
||||
void on_m_conductor_increase_sb_valueChanged(int);
|
||||
void on_m_element_increase_sb_valueChanged(int);
|
||||
void on_m_folio_increase_sb_valueChanged(int);
|
||||
|
||||
signals:
|
||||
void folioAutoNumChanged(QString);
|
||||
|
||||
private:
|
||||
enum class AutoNumCategory { Conductor, Element, Folio };
|
||||
|
||||
/// The four widgets that make up one category's row, bundled so
|
||||
/// refreshRow() can be called with just a category instead of
|
||||
/// four pointers that must always be passed in matching sets.
|
||||
struct Row
|
||||
{
|
||||
QComboBox *combo;
|
||||
QLineEdit *value;
|
||||
QSpinBox *increase;
|
||||
QLineEdit *next;
|
||||
};
|
||||
Row rowFor(AutoNumCategory category) const;
|
||||
|
||||
/**
|
||||
@brief resetAutoNum
|
||||
Reset the numerotation context currently selected in combo_box
|
||||
@@ -93,6 +109,23 @@ class AutoNumberingDockWidget : public QDockWidget
|
||||
void refreshValueField(QComboBox *combo_box, QLineEdit *line_edit, AutoNumCategory category);
|
||||
void applyValueField(QComboBox *combo_box, QLineEdit *line_edit, AutoNumCategory category);
|
||||
|
||||
/// Refresh/apply the increment spin box the same way.
|
||||
void refreshIncreaseField(QComboBox *combo_box, QSpinBox *increase_sb, AutoNumCategory category);
|
||||
void applyIncreaseField(QComboBox *combo_box, QSpinBox *increase_sb, AutoNumCategory category);
|
||||
|
||||
/// Show what the counter will read after one more step, using
|
||||
/// the same NumerotationContextCommands engine the Suivant
|
||||
/// button in the full configuration dialog already advances
|
||||
/// the whole context with -- so the preview can never disagree
|
||||
/// with what actually happens when the number is next consumed.
|
||||
void refreshNextField(QComboBox *combo_box, QLineEdit *next_edit, AutoNumCategory category);
|
||||
|
||||
/// Refresh a whole row -- value, increment and next-value
|
||||
/// preview -- in one call. Every refresh call site needs the
|
||||
/// increment and preview kept in step with the value now, so
|
||||
/// this replaces refreshValueField() at each of them.
|
||||
void refreshRow(AutoNumCategory category);
|
||||
|
||||
Ui::AutoNumberingDockWidget *ui;
|
||||
QETProject* m_project = nullptr;
|
||||
ProjectView* m_project_view = nullptr;
|
||||
|
||||
@@ -15,6 +15,36 @@
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="value_header_label">
|
||||
<property name="text">
|
||||
<string>Valeur</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QLabel" name="increase_header_label">
|
||||
<property name="text">
|
||||
<string>Incrément</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="5">
|
||||
<widget class="QLabel" name="next_header_label">
|
||||
<property name="text">
|
||||
<string>Suivant</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="m_element_cb"/>
|
||||
</item>
|
||||
@@ -61,6 +91,47 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="4">
|
||||
<widget class="QSpinBox" name="m_conductor_increase_sb">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>55</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Incrément : valeur ajoutée au compteur à chaque nouvelle numérotation</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="accelerated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="5">
|
||||
<widget class="QLineEdit" name="m_conductor_next_le">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Prochaine valeur qui sera appliquée avec cet incrément</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
@@ -108,6 +179,47 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="4">
|
||||
<widget class="QSpinBox" name="m_element_increase_sb">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>55</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Incrément : valeur ajoutée au compteur à chaque nouvelle numérotation</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="accelerated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="5">
|
||||
<widget class="QLineEdit" name="m_element_next_le">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Prochaine valeur qui sera appliquée avec cet incrément</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="m_folio_cb"/>
|
||||
</item>
|
||||
@@ -144,6 +256,47 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="4">
|
||||
<widget class="QSpinBox" name="m_folio_increase_sb">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>55</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Incrément : valeur ajoutée au compteur à chaque nouvelle numérotation</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="accelerated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="5">
|
||||
<widget class="QLineEdit" name="m_folio_next_le">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Prochaine valeur qui sera appliquée avec cet incrément</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
|
||||
@@ -112,7 +112,7 @@ void SelectAutonumW::setContext(const NumerotationContext &context)
|
||||
else {
|
||||
for (int i=0; i<m_context.size(); ++i) { //build with the content of @context
|
||||
NumPartEditorW *part= new NumPartEditorW(m_context, i, m_edited_type, this);
|
||||
connect (part, SIGNAL(changed()), this, SLOT(applyEnable()));
|
||||
connect(part, &NumPartEditorW::changed, this, [this]() { applyEnable(); });
|
||||
num_part_list_ << part;
|
||||
ui -> editor_layout -> addWidget(part);
|
||||
}
|
||||
@@ -145,7 +145,7 @@ void SelectAutonumW::on_add_button_clicked()
|
||||
{
|
||||
applyEnable(false);
|
||||
NumPartEditorW *part = new NumPartEditorW(m_edited_type, this);
|
||||
connect (part, SIGNAL(changed()), this, SLOT(applyEnable()));
|
||||
connect(part, &NumPartEditorW::changed, this, [this]() { applyEnable(); });
|
||||
num_part_list_ << part;
|
||||
ui -> editor_layout -> addWidget(part);
|
||||
ui -> remove_button -> setEnabled(true);
|
||||
@@ -160,7 +160,7 @@ void SelectAutonumW::on_remove_button_clicked()
|
||||
//remove if @num_part_list contains more than one item
|
||||
if (num_part_list_.size() > 1) {
|
||||
NumPartEditorW *part = num_part_list_.takeLast();
|
||||
disconnect(part, SIGNAL(changed()), this, SLOT(applyEnable()));
|
||||
// deliberately not disconnecting as not possible to resolve with lambda and will happen automatically when the object "part" is destroyed.
|
||||
delete part;
|
||||
if (num_part_list_.size() == 1) {
|
||||
ui -> remove_button -> setDisabled(true);
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "autobreakconductor.h"
|
||||
|
||||
#include "diagram.h"
|
||||
#include "qetproject.h"
|
||||
#include "conductorautonumerotation.h"
|
||||
#include "qetgraphicsitem/element.h"
|
||||
#include "qetgraphicsitem/terminal.h"
|
||||
#include "qetgraphicsitem/conductor.h"
|
||||
#include "undocommand/addgraphicsobjectcommand.h"
|
||||
#include "undocommand/deleteqgraphicsitemcommand.h"
|
||||
|
||||
#include <QPainterPath>
|
||||
#include <QMap>
|
||||
#include <limits>
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
@brief distanceToSegment
|
||||
@param point : point to measure from
|
||||
@param segment : the finite segment (not the infinite line through it)
|
||||
@return the distance from @a point to the closest point actually on
|
||||
@a segment. Unlike QET::orthogonalProjection(), which reports a hit
|
||||
for any point on the segment's infinite extension, this clamps the
|
||||
projection to the segment itself: a point collinear with a wire but
|
||||
past its actual drawn end is correctly reported as far away, not "on"
|
||||
the wire.
|
||||
*/
|
||||
qreal distanceToSegment(const QPointF &point, const QLineF &segment)
|
||||
{
|
||||
const QPointF a = segment.p1();
|
||||
const QPointF b = segment.p2();
|
||||
const QPointF ab = b - a;
|
||||
const qreal len2 = QPointF::dotProduct(ab, ab);
|
||||
|
||||
if (len2 <= 0.0)
|
||||
return QLineF(point, a).length();
|
||||
|
||||
qreal t = QPointF::dotProduct(point - a, ab) / len2;
|
||||
t = qBound(0.0, t, 1.0);
|
||||
return QLineF(point, a + t * ab).length();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
/**
|
||||
@brief autoBreakConductors
|
||||
For each terminal of @a element, check if its dock point lies on an
|
||||
existing conductor. If so, break the conductor and reconnect through
|
||||
the element's terminal. Broken conductors from the same circuit
|
||||
(sharing the same far-end endpoint) are broken together; independent
|
||||
crossing conductors are left untouched.
|
||||
*/
|
||||
QSet<Terminal *> autoBreakConductors(
|
||||
Diagram *diagram,
|
||||
Element *element,
|
||||
QUndoCommand *parent,
|
||||
QList<Conductor *> &conductors_handled,
|
||||
QSet<Terminal *> &used_terminals)
|
||||
{
|
||||
QSet<Terminal *> broken_endpoints;
|
||||
|
||||
if (!diagram->project()->autoBreakConductor())
|
||||
return broken_endpoints;
|
||||
|
||||
foreach (Terminal *t, element->terminals())
|
||||
{
|
||||
if (used_terminals.contains(t))
|
||||
continue;
|
||||
|
||||
QPointF t_dock = t->dockConductor();
|
||||
|
||||
struct ConductorMatch {
|
||||
Conductor *conductor;
|
||||
Terminal *connect_to;
|
||||
Terminal *other;
|
||||
};
|
||||
QList<ConductorMatch> all_matches;
|
||||
|
||||
foreach (Conductor *c, diagram->conductors())
|
||||
{
|
||||
if (conductors_handled.contains(c) ||
|
||||
c->terminal1->parentElement() == element ||
|
||||
c->terminal2->parentElement() == element)
|
||||
continue;
|
||||
|
||||
QPointF local_dock = c->mapFromScene(t_dock);
|
||||
bool point_on_conductor = false;
|
||||
QPainterPath path = c->path();
|
||||
for (int i = 0; i < path.elementCount() - 1; ++i)
|
||||
{
|
||||
const QPainterPath::Element &e1 = path.elementAt(i);
|
||||
const QPainterPath::Element &e2 = path.elementAt(i + 1);
|
||||
QLineF segment(QPointF(e1.x, e1.y), QPointF(e2.x, e2.y));
|
||||
if (distanceToSegment(local_dock, segment) < 5.0)
|
||||
{
|
||||
point_on_conductor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!point_on_conductor)
|
||||
continue;
|
||||
|
||||
Terminal *c1 = c->terminal1;
|
||||
Terminal *c2 = c->terminal2;
|
||||
QPointF c1_dock = c1->dockConductor();
|
||||
QPointF c2_dock = c2->dockConductor();
|
||||
|
||||
Terminal *connect_to = nullptr;
|
||||
Terminal *other = nullptr;
|
||||
|
||||
switch (t->orientation()) {
|
||||
case Qet::North:
|
||||
if (c1_dock.y() < t_dock.y()) { connect_to = c1; other = c2; }
|
||||
else if (c2_dock.y() < t_dock.y()) { connect_to = c2; other = c1; }
|
||||
break;
|
||||
case Qet::South:
|
||||
if (c1_dock.y() > t_dock.y()) { connect_to = c1; other = c2; }
|
||||
else if (c2_dock.y() > t_dock.y()) { connect_to = c2; other = c1; }
|
||||
break;
|
||||
case Qet::East:
|
||||
if (c1_dock.x() > t_dock.x()) { connect_to = c1; other = c2; }
|
||||
else if (c2_dock.x() > t_dock.x()) { connect_to = c2; other = c1; }
|
||||
break;
|
||||
case Qet::West:
|
||||
if (c1_dock.x() < t_dock.x()) { connect_to = c1; other = c2; }
|
||||
else if (c2_dock.x() < t_dock.x()) { connect_to = c2; other = c1; }
|
||||
break;
|
||||
}
|
||||
|
||||
if (!connect_to) {
|
||||
qreal d1 = QLineF(t_dock, c1_dock).length();
|
||||
qreal d2 = QLineF(t_dock, c2_dock).length();
|
||||
if (d1 <= d2) { connect_to = c1; other = c2; }
|
||||
else { connect_to = c2; other = c1; }
|
||||
}
|
||||
|
||||
all_matches.append({c, connect_to, other});
|
||||
}
|
||||
|
||||
if (all_matches.isEmpty())
|
||||
continue;
|
||||
|
||||
QMap<Terminal *, QList<ConductorMatch>> groups;
|
||||
for (const auto &m : all_matches) {
|
||||
groups[m.other].append(m);
|
||||
}
|
||||
Terminal *best_other = nullptr;
|
||||
int best_count = 0;
|
||||
for (auto it = groups.constBegin(); it != groups.constEnd(); ++it) {
|
||||
if (it.value().size() > best_count) {
|
||||
best_count = it.value().size();
|
||||
best_other = it.key();
|
||||
}
|
||||
}
|
||||
|
||||
const QList<ConductorMatch> &matches = groups[best_other];
|
||||
|
||||
used_terminals.insert(t);
|
||||
|
||||
DiagramContent content;
|
||||
for (const auto &m : matches) {
|
||||
content.m_other_conductors.append(m.conductor);
|
||||
conductors_handled.append(m.conductor);
|
||||
}
|
||||
new DeleteQGraphicsItemCommand(diagram, content, parent);
|
||||
|
||||
for (const auto &m : matches) {
|
||||
Conductor *new_c = new Conductor(m.connect_to, t);
|
||||
new AddGraphicsObjectCommand(new_c, diagram, QPointF(), parent);
|
||||
ConductorAutoNumerotation can(new_c, diagram, parent);
|
||||
can.numerate();
|
||||
if (diagram->freezeNewConductors() || diagram->project()->isFreezeNewConductors())
|
||||
new_c->setFreezeLabel(true);
|
||||
|
||||
broken_endpoints.insert(m.connect_to);
|
||||
}
|
||||
|
||||
QPointF other_dock = best_other->dockConductor();
|
||||
Terminal *other_terminal = nullptr;
|
||||
qreal best_dist = std::numeric_limits<qreal>::max();
|
||||
foreach (Terminal *ot, element->terminals())
|
||||
{
|
||||
if (used_terminals.contains(ot))
|
||||
continue;
|
||||
|
||||
QPointF ot_dock = ot->dockConductor();
|
||||
bool orientation_ok = false;
|
||||
switch (ot->orientation()) {
|
||||
case Qet::North: orientation_ok = other_dock.y() < ot_dock.y(); break;
|
||||
case Qet::South: orientation_ok = other_dock.y() > ot_dock.y(); break;
|
||||
case Qet::East: orientation_ok = other_dock.x() > ot_dock.x(); break;
|
||||
case Qet::West: orientation_ok = other_dock.x() < ot_dock.x(); break;
|
||||
}
|
||||
if (!orientation_ok)
|
||||
continue;
|
||||
|
||||
qreal dist = QLineF(ot_dock, other_dock).length();
|
||||
if (dist < best_dist) {
|
||||
best_dist = dist;
|
||||
other_terminal = ot;
|
||||
}
|
||||
}
|
||||
|
||||
if (other_terminal) {
|
||||
Conductor *new_c2 = new Conductor(best_other, other_terminal);
|
||||
new AddGraphicsObjectCommand(new_c2, diagram, QPointF(), parent);
|
||||
ConductorAutoNumerotation can2(new_c2, diagram, parent);
|
||||
can2.numerate();
|
||||
if (diagram->freezeNewConductors() || diagram->project()->isFreezeNewConductors())
|
||||
new_c2->setFreezeLabel(true);
|
||||
|
||||
broken_endpoints.insert(best_other);
|
||||
used_terminals.insert(other_terminal);
|
||||
}
|
||||
}
|
||||
|
||||
return broken_endpoints;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef AUTOBREAKCONDUCTOR_H
|
||||
#define AUTOBREAKCONDUCTOR_H
|
||||
|
||||
#include <QSet>
|
||||
#include <QList>
|
||||
|
||||
class Diagram;
|
||||
class Element;
|
||||
class Terminal;
|
||||
class Conductor;
|
||||
class QUndoCommand;
|
||||
|
||||
/**
|
||||
@brief autoBreakConductors
|
||||
For each terminal of @a element, check if its dock point lies on an
|
||||
existing conductor. If so, break the conductor and reconnect through the
|
||||
element's terminal. Broken conductors from the same circuit (sharing the
|
||||
same far-end endpoint) are broken together; independent crossing
|
||||
conductors are left untouched.
|
||||
@param diagram the diagram containing the conductors
|
||||
@param element the element whose terminals trigger breaks
|
||||
@param parent undo command under which break/reconnect
|
||||
sub-commands are created
|
||||
@param conductors_handled shared list of conductors already claimed by
|
||||
a previous call in the same batch (prevents
|
||||
double-processing when multiple elements are
|
||||
broken in one pass)
|
||||
@param used_terminals shared set of terminals already used as
|
||||
connect_to or other_terminal in the same batch
|
||||
@return set of terminals that were connected to a broken conductor's
|
||||
far-end (useful for preventing duplicate auto-connect)
|
||||
*/
|
||||
QSet<Terminal *> autoBreakConductors(
|
||||
Diagram *diagram,
|
||||
Element *element,
|
||||
QUndoCommand *parent,
|
||||
QList<Conductor *> &conductors_handled,
|
||||
QSet<Terminal *> &used_terminals);
|
||||
|
||||
#endif // AUTOBREAKCONDUCTOR_H
|
||||
@@ -73,6 +73,8 @@ class BorderTitleBlock : public QObject
|
||||
return(rows_count_ * rows_height_); }
|
||||
/// @return la rows header width, in pixels
|
||||
qreal rowsHeaderWidth() const { return(rows_header_width_); }
|
||||
/// @return the edge where title block is docked
|
||||
Qt::Edge titleBlockEdge() const { return(m_edge); }
|
||||
|
||||
// border - title block = diagram
|
||||
/**
|
||||
|
||||
@@ -72,14 +72,8 @@ bool ConductorNumExport::toCsv()
|
||||
if (file.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
{
|
||||
QTextStream stream(&file);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) // ### Qt 6: remove
|
||||
stream << wiresNum() << endl;
|
||||
#else
|
||||
#if TODO_LIST
|
||||
#pragma message("@TODO remove code for QT 5.15 or later")
|
||||
#endif
|
||||
|
||||
stream << wiresNum() << &Qt::endl(stream);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
|
||||
@@ -811,14 +811,7 @@ void ConductorProperties::readStyle(const QString &style_string) {
|
||||
if (style_string.isEmpty()) return;
|
||||
|
||||
// recupere la liste des couples style / valeur
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) // ### Qt 6: remove
|
||||
QStringList styles = style_string.split(";", QString::SkipEmptyParts);
|
||||
#else
|
||||
#if TODO_LIST
|
||||
#pragma message("@TODO remove code QString::SkipEmptyParts for QT 5.14 or later")
|
||||
#endif
|
||||
QStringList styles = style_string.split(";", Qt::SkipEmptyParts);
|
||||
#endif
|
||||
|
||||
QRegularExpression Rx("^(?<name>[a-z-]+): (?<value>[a-z-]+)$");
|
||||
if (!Rx.isValid())
|
||||
|
||||
@@ -74,10 +74,9 @@ ConfigDialog::ConfigDialog(QWidget *parent) : QDialog(parent) {
|
||||
setLayout(dialog_layout);
|
||||
|
||||
// connexion signaux / slots
|
||||
connect(buttons, SIGNAL(accepted()), this, SLOT(applyConf()));
|
||||
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
connect(pages_list, SIGNAL(currentRowChanged(int)),
|
||||
pages_widget, SLOT(setCurrentIndex(int)));
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &ConfigDialog::applyConf);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &ConfigDialog::reject);
|
||||
connect(pages_list, &QListWidget::currentRowChanged, pages_widget, &QStackedWidget::setCurrentIndex);
|
||||
|
||||
// set maximum a bit smaller than available size = (screen-size - Task-Bar):
|
||||
setMaximumSize((int)(0.94 * MachineInfo::instance()->i_max_available_width()),
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
#include "../diagramposition.h"
|
||||
#include "../elementprovider.h"
|
||||
#include "../qetapp.h"
|
||||
#include "../qetgraphicsitem/conductor.h"
|
||||
#include "../qetgraphicsitem/element.h"
|
||||
#include "../qetgraphicsitem/terminal.h"
|
||||
#include "../qetinformation.h"
|
||||
#include "../qetproject.h"
|
||||
|
||||
@@ -87,6 +89,7 @@ void projectDataBase::updateDB()
|
||||
populateDiagramInfoTable();
|
||||
populateElementTable();
|
||||
populateElementInfoTable();
|
||||
populateConductorTable();
|
||||
emit dataBaseUpdated();
|
||||
}
|
||||
|
||||
@@ -245,6 +248,132 @@ void projectDataBase::diagramOrderChanged()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::addConductor
|
||||
@param conductor
|
||||
*/
|
||||
void projectDataBase::addConductor(Conductor *conductor)
|
||||
{
|
||||
if (!conductor || !conductor->diagram()) {
|
||||
qDebug() << "projectDataBase::addConductor: null conductor or diagram";
|
||||
return;
|
||||
}
|
||||
|
||||
//Both endpoints must belong to an element: the terminal table is keyed
|
||||
//on (terminal, element) and a terminal with no parent has no identity
|
||||
//to key on. Terminals whose *definition* predates terminal uuids are
|
||||
//fine -- Terminal::stableUuid() derives one from the terminal's local
|
||||
//position, which is what the project format itself matches on.
|
||||
if (!conductor->terminal1->parentElement()
|
||||
|| !conductor->terminal2->parentElement()) {
|
||||
return;
|
||||
}
|
||||
|
||||
insertTerminal(conductor->terminal1);
|
||||
insertTerminal(conductor->terminal2);
|
||||
|
||||
watchConductor(conductor);
|
||||
bindConductorValues(m_insert_conductor_query, conductor, conductor->diagram());
|
||||
if (!m_insert_conductor_query.exec()) {
|
||||
qDebug() << "projectDataBase::addConductor insert error : " << m_insert_conductor_query.lastError();
|
||||
} else {
|
||||
emit dataBaseUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::removeConductor
|
||||
@param conductor
|
||||
*/
|
||||
void projectDataBase::removeConductor(Conductor *conductor)
|
||||
{
|
||||
m_remove_conductor_query.bindValue(":uuid", conductor->uuid().toString());
|
||||
if (!m_remove_conductor_query.exec()) {
|
||||
qDebug() << "projectDataBase::removeConductor delete error : " << m_remove_conductor_query.lastError();
|
||||
} else {
|
||||
emit dataBaseUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::updateConductor
|
||||
Refresh the mutable columns of an already-inserted conductor.
|
||||
|
||||
Only the text (the wire number) can change without the conductor being
|
||||
removed and re-added: its endpoints are fixed for its lifetime. Without
|
||||
this, renaming a wire left the database holding the old number and the
|
||||
wiring list showed a stale value until the next full repopulate.
|
||||
@param conductor
|
||||
*/
|
||||
void projectDataBase::updateConductor(Conductor *conductor)
|
||||
{
|
||||
if (!conductor) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_update_conductor_query.bindValue(QStringLiteral(":uuid"), conductor->uuid().toString());
|
||||
m_update_conductor_query.bindValue(QStringLiteral(":text"), conductor->properties().text);
|
||||
if (!m_update_conductor_query.exec()) {
|
||||
qDebug() << "projectDataBase::updateConductor update error : " << m_update_conductor_query.lastError();
|
||||
}
|
||||
|
||||
//Deliberately no dataBaseUpdated() here, unlike add/remove. The only
|
||||
//column this touches is the wire text, which no view watched by
|
||||
//ProjectDBModel displays -- the nomenclature shows elements, and its
|
||||
//wire_count changes when a conductor appears or disappears, not when
|
||||
//it is renamed. Emitting would make every ProjectDBModel re-run its
|
||||
//query, and auto-numbering renames every conductor in the project in
|
||||
//one pass.
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::watchConductor
|
||||
Keep this conductor's row in step with its properties.
|
||||
|
||||
Conductor::setProperties() has a dozen call sites (auto-numbering, the
|
||||
properties dialog, element moves, deletion re-links...), so listening to
|
||||
the signal it already emits is the only way to catch them all -- and the
|
||||
only way to catch the ones added later. Qt::UniqueConnection makes a
|
||||
repeated insert or a full repopulate harmless.
|
||||
@param conductor
|
||||
*/
|
||||
void projectDataBase::watchConductor(Conductor *conductor)
|
||||
{
|
||||
connect(conductor, &Conductor::propertiesChange,
|
||||
this, &projectDataBase::conductorPropertiesChanged,
|
||||
Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::conductorPropertiesChanged
|
||||
*/
|
||||
void projectDataBase::conductorPropertiesChanged()
|
||||
{
|
||||
if (auto *conductor = qobject_cast<Conductor *>(sender())) {
|
||||
updateConductor(conductor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::bindConductorValues
|
||||
One binder for both insert paths, so a conductor added to a live diagram
|
||||
and one read from a file can never drift apart -- the same reason
|
||||
bindElementValues() exists for elements.
|
||||
@param query
|
||||
@param conductor
|
||||
@param diagram : the diagram the conductor belongs to
|
||||
*/
|
||||
void projectDataBase::bindConductorValues(QSqlQuery &query, Conductor *conductor, Diagram *diagram)
|
||||
{
|
||||
query.bindValue(QStringLiteral(":uuid"), conductor->uuid().toString());
|
||||
query.bindValue(QStringLiteral(":diagram_uuid"), diagram->uuid().toString());
|
||||
query.bindValue(QStringLiteral(":terminal1_uuid"), conductor->terminal1->stableUuid().toString());
|
||||
query.bindValue(QStringLiteral(":terminal1_element_uuid"), conductor->terminal1->parentElement()->uuid().toString());
|
||||
query.bindValue(QStringLiteral(":terminal2_uuid"), conductor->terminal2->stableUuid().toString());
|
||||
query.bindValue(QStringLiteral(":terminal2_element_uuid"), conductor->terminal2->parentElement()->uuid().toString());
|
||||
query.bindValue(QStringLiteral(":text"), conductor->properties().text);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::createDataBase
|
||||
Create the data base
|
||||
@@ -323,6 +452,57 @@ bool projectDataBase::createDataBase()
|
||||
qDebug() << " element_info_table query : " << query_.lastError();
|
||||
}
|
||||
|
||||
//Create the terminal table.
|
||||
//Terminal::uuid() is the terminal-position id baked into the catalog
|
||||
//.elmt definition (e.g. "the top terminal") -- identical across every
|
||||
//placed instance of that catalog element, not a per-instance id. A
|
||||
//terminal instance is only uniquely identified by (uuid, element_uuid)
|
||||
//together, so that pair is the primary key here, not uuid alone.
|
||||
QString terminal_table("CREATE TABLE terminal"
|
||||
"( "
|
||||
"uuid VARCHAR(50) NOT NULL, "
|
||||
"element_uuid VARCHAR(50) NOT NULL,"
|
||||
"name VARCHAR(50),"
|
||||
"PRIMARY KEY (uuid, element_uuid),"
|
||||
"FOREIGN KEY (element_uuid) REFERENCES element (uuid)"
|
||||
")");
|
||||
if (!query_.exec(terminal_table)) {
|
||||
qDebug() << "terminal_table query : "<< query_.lastError();
|
||||
}
|
||||
|
||||
//Create the conductor table
|
||||
QString conductor_table("CREATE TABLE conductor"
|
||||
"( "
|
||||
"uuid VARCHAR(50) PRIMARY KEY NOT NULL, "
|
||||
"diagram_uuid VARCHAR(50) NOT NULL,"
|
||||
"terminal1_uuid VARCHAR(50) NOT NULL,"
|
||||
"terminal1_element_uuid VARCHAR(50) NOT NULL,"
|
||||
"terminal2_uuid VARCHAR(50) NOT NULL,"
|
||||
"terminal2_element_uuid VARCHAR(50) NOT NULL,"
|
||||
"text VARCHAR(100),"
|
||||
"FOREIGN KEY (diagram_uuid) REFERENCES diagram (uuid),"
|
||||
"FOREIGN KEY (terminal1_uuid, terminal1_element_uuid) REFERENCES terminal (uuid, element_uuid),"
|
||||
"FOREIGN KEY (terminal2_uuid, terminal2_element_uuid) REFERENCES terminal (uuid, element_uuid)"
|
||||
")");
|
||||
if (!query_.exec(conductor_table)) {
|
||||
qDebug() << "conductor_table query : "<< query_.lastError();
|
||||
}
|
||||
|
||||
//The element-facing columns are looked up per element row, not per
|
||||
//conductor row: element_nomenclature_view carries a correlated
|
||||
//subquery counting the wires touching each element. Without these
|
||||
//indexes each element row full-scans the conductor table, which grows
|
||||
//as elements x conductors.
|
||||
for (const QString &index_ : {
|
||||
QStringLiteral("CREATE INDEX idx_conductor_terminal1_element ON conductor (terminal1_element_uuid)"),
|
||||
QStringLiteral("CREATE INDEX idx_conductor_terminal2_element ON conductor (terminal2_element_uuid)"),
|
||||
QStringLiteral("CREATE INDEX idx_conductor_diagram ON conductor (diagram_uuid)") })
|
||||
{
|
||||
if (!query_.exec(index_)) {
|
||||
qDebug() << "conductor index query : " << query_.lastError();
|
||||
}
|
||||
}
|
||||
|
||||
createElementNomenclatureView();
|
||||
createSummaryView();
|
||||
prepareQuery();
|
||||
@@ -534,6 +714,58 @@ void projectDataBase::populateDiagramInfoTable()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::populateConductorTable
|
||||
Populate the terminal and conductor tables. Terminals only matter here
|
||||
in the context of a conductor referencing them, so their population is
|
||||
folded into this method rather than tracked independently.
|
||||
*/
|
||||
void projectDataBase::populateConductorTable()
|
||||
{
|
||||
QSqlQuery query(m_data_base);
|
||||
query.exec(QStringLiteral("DELETE FROM conductor"));
|
||||
query.exec(QStringLiteral("DELETE FROM terminal"));
|
||||
|
||||
for (auto *diagram : m_project->diagrams())
|
||||
{
|
||||
const auto conductor_list = diagram->conductors();
|
||||
for (auto *conductor : conductor_list)
|
||||
{
|
||||
//See addConductor(): only a terminal with no parent element is
|
||||
//skipped. A missing terminal uuid is handled by stableUuid().
|
||||
if (!conductor->terminal1->parentElement()
|
||||
|| !conductor->terminal2->parentElement()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
insertTerminal(conductor->terminal1);
|
||||
insertTerminal(conductor->terminal2);
|
||||
|
||||
watchConductor(conductor);
|
||||
bindConductorValues(m_insert_conductor_query, conductor, diagram);
|
||||
if (!m_insert_conductor_query.exec()) {
|
||||
qDebug() << "projectDataBase::populateConductorTable insert error : " << m_insert_conductor_query.lastError();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::insertTerminal
|
||||
Insert (or, if already present -- e.g. a junction shared by several
|
||||
conductors -- silently keep) @terminal in the terminal table.
|
||||
@param terminal
|
||||
*/
|
||||
void projectDataBase::insertTerminal(Terminal *terminal)
|
||||
{
|
||||
m_insert_terminal_query.bindValue(":uuid", terminal->stableUuid().toString());
|
||||
m_insert_terminal_query.bindValue(":element_uuid", terminal->parentElement()->uuid().toString());
|
||||
m_insert_terminal_query.bindValue(":name", terminal->name());
|
||||
if (!m_insert_terminal_query.exec()) {
|
||||
qDebug() << "projectDataBase::insertTerminal insert error : " << m_insert_terminal_query.lastError();
|
||||
}
|
||||
}
|
||||
|
||||
void projectDataBase::prepareQuery()
|
||||
{
|
||||
//INSERT DIAGRAM
|
||||
@@ -606,6 +838,23 @@ void projectDataBase::prepareQuery()
|
||||
update_str.append(" WHERE element_uuid = :uuid");
|
||||
m_update_element_query = QSqlQuery(m_data_base);
|
||||
m_update_element_query.prepare(update_str);
|
||||
|
||||
//INSERT TERMINAL
|
||||
m_insert_terminal_query = QSqlQuery(m_data_base);
|
||||
m_insert_terminal_query.prepare("INSERT OR IGNORE INTO terminal (uuid, element_uuid, name) VALUES (:uuid, :element_uuid, :name)");
|
||||
|
||||
//INSERT CONDUCTOR
|
||||
m_insert_conductor_query = QSqlQuery(m_data_base);
|
||||
m_insert_conductor_query.prepare("INSERT INTO conductor (uuid, diagram_uuid, terminal1_uuid, terminal1_element_uuid, terminal2_uuid, terminal2_element_uuid, text) "
|
||||
"VALUES (:uuid, :diagram_uuid, :terminal1_uuid, :terminal1_element_uuid, :terminal2_uuid, :terminal2_element_uuid, :text)");
|
||||
|
||||
//UPDATE CONDUCTOR
|
||||
m_update_conductor_query = QSqlQuery(m_data_base);
|
||||
m_update_conductor_query.prepare(QStringLiteral("UPDATE conductor SET text = :text WHERE uuid = :uuid"));
|
||||
|
||||
//REMOVE CONDUCTOR
|
||||
m_remove_conductor_query = QSqlQuery(m_data_base);
|
||||
m_remove_conductor_query.prepare("DELETE FROM conductor WHERE uuid=:uuid");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
class Element;
|
||||
class QETProject;
|
||||
class Diagram;
|
||||
class Conductor;
|
||||
class Terminal;
|
||||
class sqlite3;
|
||||
|
||||
/**
|
||||
@@ -58,6 +60,16 @@ class projectDataBase : public QObject
|
||||
void diagramInfoChanged (Diagram *diagram);
|
||||
void diagramOrderChanged();
|
||||
|
||||
void addConductor (Conductor *conductor);
|
||||
void removeConductor (Conductor *conductor);
|
||||
void updateConductor (Conductor *conductor);
|
||||
|
||||
private slots:
|
||||
//Refresh the sender()'s row after Conductor::setProperties().
|
||||
void conductorPropertiesChanged();
|
||||
|
||||
public:
|
||||
|
||||
signals:
|
||||
void dataBaseUpdated();
|
||||
|
||||
@@ -69,6 +81,10 @@ class projectDataBase : public QObject
|
||||
void populateElementTable();
|
||||
void populateElementInfoTable();
|
||||
void populateDiagramInfoTable();
|
||||
void populateConductorTable();
|
||||
void bindConductorValues(QSqlQuery &query, Conductor *conductor, Diagram *diagram);
|
||||
void watchConductor(Conductor *conductor);
|
||||
void insertTerminal(Terminal *terminal);
|
||||
void prepareQuery();
|
||||
static QHash<QString, QString> elementInfoToString(
|
||||
Element *elmt);
|
||||
@@ -86,7 +102,11 @@ class projectDataBase : public QObject
|
||||
m_insert_diagram_info_query,
|
||||
m_update_diagram_info_query,
|
||||
m_diagram_order_changed,
|
||||
m_diagram_info_order_changed;
|
||||
m_diagram_info_order_changed,
|
||||
m_insert_terminal_query,
|
||||
m_insert_conductor_query,
|
||||
m_update_conductor_query,
|
||||
m_remove_conductor_query;
|
||||
|
||||
#ifdef QET_EXPORT_PROJECT_DB
|
||||
public:
|
||||
|
||||
@@ -48,14 +48,8 @@ ElementQueryWidget::ElementQueryWidget(QWidget *parent) :
|
||||
m_button_group.addButton(ui->m_protection_cb, 5);
|
||||
m_button_group.addButton(ui->m_thumbnail_cb, 6);
|
||||
m_button_group.addButton(ui->m_plc_cb, 7);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) // ### Qt 6: remove
|
||||
connect(&m_button_group, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), [this](int id)
|
||||
#else
|
||||
#if TODO_LIST
|
||||
#pragma message("@TODO remove code for QT 5.15 or later")
|
||||
#endif
|
||||
connect(&m_button_group, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::idClicked), [this](int id)
|
||||
#endif
|
||||
|
||||
{
|
||||
auto check_box = static_cast<QCheckBox *>(m_button_group.button(0));
|
||||
if (id == 0)
|
||||
@@ -384,10 +378,10 @@ QString ElementQueryWidget::queryStr() const
|
||||
where.clear();
|
||||
}
|
||||
|
||||
QString exclude_condition = "(exclude_from_bom IS NULL OR exclude_from_bom != '1')";
|
||||
|
||||
filter_ += " AND " + exclude_condition;
|
||||
// -------------------------------------------------------------
|
||||
// exclude_from_bom is already filtered by element_nomenclature_view
|
||||
// (see createElementNomenclatureView() in projectdatabase.cpp); this
|
||||
// widget's query reads FROM that view, so a flagged element never
|
||||
// reaches this point in the first place.
|
||||
|
||||
if (where.isEmpty() && !filter_.isEmpty()) {
|
||||
filter_.remove(0, 4); //Remove the first " AND" of filter.
|
||||
|
||||
+21
-10
@@ -67,6 +67,7 @@ Diagram::Diagram(QETProject *project) :
|
||||
m_project (project),
|
||||
use_border_ (true),
|
||||
draw_terminals_ (true),
|
||||
draw_terminal_names_ (true),
|
||||
draw_colored_conductors_ (true),
|
||||
m_event_interface (nullptr),
|
||||
m_freeze_new_elements (false),
|
||||
@@ -1555,14 +1556,6 @@ bool Diagram::fromXml(QDomElement &document,
|
||||
if (content_ptr) {
|
||||
content_ptr -> m_elements = added_elements;
|
||||
content_ptr -> m_conductors_to_move = added_conductors;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) // ### Qt 6: remove
|
||||
content_ptr -> m_text_fields = added_texts.toSet();
|
||||
content_ptr -> m_images = added_images.toSet();
|
||||
content_ptr -> m_shapes = added_shapes.toSet();
|
||||
#else
|
||||
#if TODO_LIST
|
||||
#pragma message("@TODO remove code for QT 5.14 or later")
|
||||
#endif
|
||||
content_ptr -> m_text_fields = QSet<IndependentTextItem *>(
|
||||
added_texts.begin(),
|
||||
added_texts.end());
|
||||
@@ -1573,7 +1566,6 @@ bool Diagram::fromXml(QDomElement &document,
|
||||
added_shapes.begin(),
|
||||
added_shapes.end());
|
||||
content_ptr->m_terminal_strip.swap(added_strips);
|
||||
#endif
|
||||
content_ptr->m_tables.swap(added_tables);
|
||||
}
|
||||
|
||||
@@ -1674,6 +1666,7 @@ void Diagram::addItem(QGraphicsItem *item)
|
||||
conductor->terminal1->addConductor(conductor);
|
||||
conductor->terminal2->addConductor(conductor);
|
||||
conductor->calculateTextItemPosition();
|
||||
m_project->dataBase()->addConductor(conductor);
|
||||
break;
|
||||
}
|
||||
default: {break;}
|
||||
@@ -1704,6 +1697,7 @@ void Diagram::removeItem(QGraphicsItem *item)
|
||||
Conductor *conductor = static_cast<Conductor *>(item);
|
||||
conductor->terminal1->removeConductor(conductor);
|
||||
conductor->terminal2->removeConductor(conductor);
|
||||
m_project->dataBase()->removeConductor(conductor);
|
||||
break;
|
||||
}
|
||||
default: {break;}
|
||||
@@ -2319,6 +2313,7 @@ ExportProperties Diagram::applyProperties(
|
||||
old_properties.draw_border = border_and_titleblock.borderIsDisplayed();
|
||||
old_properties.draw_titleblock = border_and_titleblock.titleBlockIsDisplayed();
|
||||
old_properties.draw_terminals = drawTerminals();
|
||||
old_properties.draw_terminal_names = drawTerminalNames();
|
||||
old_properties.draw_colored_conductors = drawColoredConductors();
|
||||
old_properties.exported_area = useBorder() ? QET::BorderArea
|
||||
: QET::ElementsArea;
|
||||
@@ -2327,6 +2322,7 @@ ExportProperties Diagram::applyProperties(
|
||||
// applique les nouvelles options de rendu
|
||||
setUseBorder (new_properties.exported_area == QET::BorderArea);
|
||||
setDrawTerminals (new_properties.draw_terminals);
|
||||
setDrawTerminalNames (new_properties.draw_terminal_names);
|
||||
setDrawColoredConductors (new_properties.draw_colored_conductors);
|
||||
setDisplayGrid (new_properties.draw_grid);
|
||||
setDisplayGuides (new_properties.draw_guides);
|
||||
@@ -2397,9 +2393,24 @@ QPointF Diagram::snapToGrid(const QPointF &p)
|
||||
\~French true pour afficher les bornes, false sinon
|
||||
*/
|
||||
void Diagram::setDrawTerminals(bool dt) {
|
||||
draw_terminals_ = dt;
|
||||
foreach(QGraphicsItem *qgi, items()) {
|
||||
if (Terminal *t = qgraphicsitem_cast<Terminal *>(qgi)) {
|
||||
t -> setVisible(dt);
|
||||
t -> update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::setDrawTerminalNames
|
||||
Defines whether or not to display the terminal names/labels
|
||||
@param dt : true to display the terminal names, false otherwise
|
||||
*/
|
||||
void Diagram::setDrawTerminalNames(bool dt) {
|
||||
draw_terminal_names_ = dt;
|
||||
foreach(QGraphicsItem *qgi, items()) {
|
||||
if (Terminal *t = qgraphicsitem_cast<Terminal *>(qgi)) {
|
||||
t -> update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +127,7 @@ class Diagram : public QGraphicsScene
|
||||
bool draw_guides_;
|
||||
QList<Diagram::Guide> m_guides_list;
|
||||
bool draw_terminals_;
|
||||
bool draw_terminal_names_;
|
||||
bool draw_colored_conductors_;
|
||||
|
||||
QString m_conductors_autonum_name;
|
||||
@@ -226,6 +227,8 @@ class Diagram : public QGraphicsScene
|
||||
|
||||
bool drawTerminals() const;
|
||||
void setDrawTerminals(bool);
|
||||
bool drawTerminalNames() const;
|
||||
void setDrawTerminalNames(bool);
|
||||
bool drawColoredConductors() const;
|
||||
void setDrawColoredConductors(bool);
|
||||
|
||||
@@ -426,6 +429,15 @@ inline bool Diagram::drawTerminals() const
|
||||
return(draw_terminals_);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::drawTerminalNames
|
||||
@return true if terminal names are rendered, false otherwise
|
||||
*/
|
||||
inline bool Diagram::drawTerminalNames() const
|
||||
{
|
||||
return(draw_terminal_names_);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::drawColoredConductors
|
||||
@return true if conductors colors are rendered, false otherwise.
|
||||
|
||||
@@ -75,6 +75,13 @@ void PasteDiagramCommand::redo()
|
||||
{
|
||||
first_redo = false;
|
||||
|
||||
//make new uuid for every pasted conductor, because old uuid are
|
||||
//the uuid of the copied conductor
|
||||
const QList <Conductor *> all_pasted_conductors = content.conductors();
|
||||
for (Conductor *c : all_pasted_conductors) {
|
||||
c -> newUuid();
|
||||
}
|
||||
|
||||
//this is the first paste, we do some actions for the new element
|
||||
const QList <Element *> elmts_list = content.m_elements;
|
||||
for (Element *e : elmts_list)
|
||||
@@ -92,12 +99,18 @@ void PasteDiagramCommand::redo()
|
||||
dc.addValue("location", "");
|
||||
e->setElementInformations(dc);
|
||||
|
||||
//Reset the text of conductors
|
||||
//Reset the text of conductors, the same way the label/comment/
|
||||
//location above are reset to "" rather than to some other
|
||||
//value - "erase on copy" means erase, not "replace with the
|
||||
//project's default new-conductor text" (which happens to
|
||||
//default to a literal "_" character, unrelated to whether the
|
||||
//user wanted this copy's old label kept or cleared; see
|
||||
//issue #413).
|
||||
const QList <Conductor *> conductors_list = content.m_conductors_to_move;
|
||||
for (Conductor *c : conductors_list)
|
||||
{
|
||||
ConductorProperties cp = c -> properties();
|
||||
cp.text = c->diagram() ? c -> diagram() -> defaultConductorProperties.text : "_";
|
||||
cp.text = "";
|
||||
c -> setProperties(cp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,17 @@
|
||||
#include "../conductorautonumerotation.h"
|
||||
#include "../diagram.h"
|
||||
#include "../undocommand/addgraphicsobjectcommand.h"
|
||||
#include "../undocommand/deleteqgraphicsitemcommand.h"
|
||||
#include "../factory/elementfactory.h"
|
||||
#include "../qetapp.h"
|
||||
#include "../qetdiagrameditor.h"
|
||||
#include "../qetgraphicsitem/element.h"
|
||||
#include "../qetgraphicsitem/conductor.h"
|
||||
#include "../qetgraphicsitem/terminal.h"
|
||||
#include "../qet.h"
|
||||
#include "../autobreakconductor.h"
|
||||
#include <QPainterPath>
|
||||
#include <limits>
|
||||
|
||||
/**
|
||||
@brief DiagramEventAddElement::DiagramEventAddElement
|
||||
@@ -248,16 +254,32 @@ void DiagramEventAddElement::addElement()
|
||||
QUndoCommand *undo_object = new QUndoCommand(tr("Ajouter %1").arg(element->name()));
|
||||
new AddGraphicsObjectCommand(element, m_diagram, m_element -> pos(), undo_object);
|
||||
|
||||
//When we search for free aligned terminal we temporally remove m_element to
|
||||
//avoid any interaction with the function Element::AlignedFreeTerminals
|
||||
//This is useful when an element has two (or more) terminals on opposite sides,
|
||||
//because m_element is exactly at the same pos of the new element
|
||||
//added to the scene so new conductor are created between terminal of the new element
|
||||
//and the opposite terminal of m_element.
|
||||
//When we search for free aligned terminal we temporally remove m_element to
|
||||
//avoid any interaction with the function Element::AlignedFreeTerminals
|
||||
//This is useful when an element has two (or more) terminals on opposite sides,
|
||||
//because m_element is exactly at the same pos of the new element
|
||||
//added to the scene so new conductor are created between terminal of the new element
|
||||
//and the opposite terminal of m_element.
|
||||
m_diagram->removeItem(m_element);
|
||||
while (!element -> AlignedFreeTerminals().isEmpty() && m_diagram -> project() -> autoConductor())
|
||||
|
||||
//Auto break conductor: if a terminal of the new element lies on an existing
|
||||
//conductor, break the conductor and reconnect through the new element's terminal.
|
||||
//Track the endpoints of broken conductors so auto-connect doesn't create duplicates.
|
||||
QList<Conductor *> conductors_handled;
|
||||
QSet<Terminal *> used_terminals;
|
||||
QSet<Terminal *> broken_endpoints = autoBreakConductors(m_diagram, element, undo_object,
|
||||
conductors_handled, used_terminals);
|
||||
|
||||
//Auto-connect: collect all aligned pairs first, then filter and process.
|
||||
QList<QPair<Terminal *, Terminal *>> aligned_pairs;
|
||||
if (m_diagram->project()->autoConductor())
|
||||
aligned_pairs = element->AlignedFreeTerminals();
|
||||
|
||||
for (const QPair<Terminal *, Terminal *> &pair : aligned_pairs)
|
||||
{
|
||||
QPair <Terminal *, Terminal *> pair = element -> AlignedFreeTerminals().takeFirst();
|
||||
//Skip if the other terminal was an endpoint of a broken conductor
|
||||
if (broken_endpoints.contains(pair.second))
|
||||
continue;
|
||||
|
||||
Conductor *conductor = new Conductor(pair.first, pair.second);
|
||||
new AddGraphicsObjectCommand(conductor, m_diagram, QPointF(), undo_object);
|
||||
|
||||
@@ -43,11 +43,7 @@ m_preview_item(nullptr)
|
||||
QString file_name = (last_slash != -1) ? path.mid(last_slash + 1) : path;
|
||||
|
||||
if (!dir_path.isEmpty()) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||
QStringList parts = dir_path.split('/', QString::SkipEmptyParts);
|
||||
#else
|
||||
QStringList parts = dir_path.split('/', Qt::SkipEmptyParts);
|
||||
#endif
|
||||
QString current_path = "";
|
||||
for (const QString &part : parts) {
|
||||
QString parent_path = current_path;
|
||||
@@ -202,11 +198,7 @@ bool DiagramEventAddMacro::loadMacro()
|
||||
QString file_name = (last_slash != -1) ? path.mid(last_slash + 1) : path;
|
||||
|
||||
if (!dir_path.isEmpty()) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||
QStringList parts = dir_path.split('/', QString::SkipEmptyParts);
|
||||
#else
|
||||
QStringList parts = dir_path.split('/', Qt::SkipEmptyParts);
|
||||
#endif
|
||||
QString current_path = "";
|
||||
for (const QString &part : parts) {
|
||||
QString parent_path = current_path;
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "diagrameventaddpdf.h"
|
||||
|
||||
// Whole file is a no-op unless QtPdf is available (see diagrameventaddpdf.h).
|
||||
#ifdef QET_HAS_QTPDF
|
||||
|
||||
#include "../qetapp.h"
|
||||
#include "../diagram.h"
|
||||
#include "../undocommand/addgraphicsobjectcommand.h"
|
||||
#include "../qetgraphicsitem/diagramimageitem.h"
|
||||
#include "../ui/pdfpagesdialog.h"
|
||||
|
||||
#include <QPdfDocument>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QPainter>
|
||||
|
||||
/**
|
||||
@brief DiagramEventAddPdf::DiagramEventAddPdf
|
||||
Constructor
|
||||
@param diagram the diagram where this event operates
|
||||
*/
|
||||
DiagramEventAddPdf::DiagramEventAddPdf(Diagram *diagram) :
|
||||
DiagramEventInterface(diagram),
|
||||
m_image(nullptr),
|
||||
m_is_added(false)
|
||||
{
|
||||
openDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DiagramEventAddPdf::~DiagramEventAddPdf
|
||||
Destructor
|
||||
*/
|
||||
DiagramEventAddPdf::~DiagramEventAddPdf()
|
||||
{
|
||||
if (m_running || m_abort)
|
||||
{
|
||||
if (m_is_added) m_diagram->removeItem(m_image);
|
||||
delete m_image;
|
||||
}
|
||||
|
||||
foreach (QGraphicsView *view, m_diagram->views())
|
||||
view->setContextMenuPolicy((Qt::DefaultContextMenu));
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DiagramEventAddPdf::mousePressEvent
|
||||
Action when mouse is pressed
|
||||
@param event event of mouse pressed
|
||||
*/
|
||||
void DiagramEventAddPdf::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (m_image && event->button() == Qt::LeftButton)
|
||||
{
|
||||
QPointF pos = event->scenePos();
|
||||
pos.rx() -= m_image->boundingRect().width()/2;
|
||||
pos.ry() -= m_image->boundingRect().height()/2;
|
||||
m_diagram->undoStack().push(new AddGraphicsObjectCommand(m_image, m_diagram, pos));
|
||||
|
||||
for (QGraphicsView *view : m_diagram->views()) {
|
||||
view->setContextMenuPolicy((Qt::DefaultContextMenu));
|
||||
}
|
||||
|
||||
m_running = false;
|
||||
emit finish();
|
||||
event->setAccepted(true);
|
||||
}
|
||||
else if (m_image && event->button() == Qt::RightButton)
|
||||
{
|
||||
m_image->setRotation(m_image->rotation() + 90);
|
||||
event->setAccepted(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DiagramEventAddPdf::mouseMoveEvent
|
||||
Action when mouse moves
|
||||
@param event event of mouse move
|
||||
*/
|
||||
void DiagramEventAddPdf::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (!m_image || event->buttons() != Qt::NoButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
QPointF pos = event->scenePos();
|
||||
|
||||
if (!m_is_added)
|
||||
{
|
||||
for (QGraphicsView *view : m_diagram->views()) {
|
||||
view->setContextMenuPolicy((Qt::NoContextMenu));
|
||||
}
|
||||
|
||||
m_diagram->addItem(m_image);
|
||||
m_is_added = true;
|
||||
}
|
||||
|
||||
m_image->setPos(pos - m_image->boundingRect().center());
|
||||
event->setAccepted(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DiagramEventAddPdf::mouseDoubleClickEvent
|
||||
Overwrite double click to prevent opening properties dialog.
|
||||
@param event event of mouse double click
|
||||
*/
|
||||
void DiagramEventAddPdf::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) {
|
||||
event->setAccepted(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DiagramEventAddPdf::wheelEvent
|
||||
Action when mouse wheel is rotated (CTRL+wheel to scale)
|
||||
@param event event of mouse wheel
|
||||
*/
|
||||
void DiagramEventAddPdf::wheelEvent(QGraphicsSceneWheelEvent *event)
|
||||
{
|
||||
if (!m_is_added || !m_image || event->modifiers() != Qt::CTRL) {
|
||||
return;
|
||||
}
|
||||
|
||||
qreal scaling = m_image->scale();
|
||||
event->delta() > 1 ? scaling += 0.01 : scaling -= 0.01;
|
||||
if (scaling > 0.01 && scaling <= 2) {
|
||||
m_image->setScale(scaling);
|
||||
}
|
||||
|
||||
event->setAccepted(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DiagramEventAddPdf::isNull
|
||||
@return true if the PDF image couldn't be loaded, false otherwise
|
||||
*/
|
||||
bool DiagramEventAddPdf::isNull() const
|
||||
{
|
||||
if (!m_image) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DiagramEventAddPdf::openDialog
|
||||
Opens a file dialog to select a PDF file, then opens a page selection
|
||||
dialog with DPI options. The selected page is rendered to a QImage at
|
||||
the chosen DPI and converted to a DiagramImageItem.
|
||||
*/
|
||||
void DiagramEventAddPdf::openDialog()
|
||||
{
|
||||
if (m_diagram->isReadOnly()) return;
|
||||
|
||||
// Open file dialog to select a PDF file
|
||||
QString pathPDFs = QETApp::documentDir();
|
||||
QString fileName = QFileDialog::getOpenFileName(
|
||||
m_diagram->views().isEmpty() ? nullptr : m_diagram->views().first(),
|
||||
QObject::tr("Sélectionner un fichier PDF..."),
|
||||
pathPDFs,
|
||||
QObject::tr("Fichiers PDF (*.pdf)")
|
||||
);
|
||||
|
||||
if (fileName.isEmpty()) return;
|
||||
|
||||
// Load the PDF document
|
||||
QPdfDocument document;
|
||||
document.load(fileName);
|
||||
|
||||
if (document.status() != QPdfDocument::Status::Ready)
|
||||
{
|
||||
QMessageBox::critical(
|
||||
m_diagram->views().isEmpty() ? nullptr : m_diagram->views().first(),
|
||||
QObject::tr("Erreur"),
|
||||
QObject::tr("Impossible de charger le fichier PDF.")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
int pageCount = document.pageCount();
|
||||
if (pageCount <= 0)
|
||||
{
|
||||
QMessageBox::critical(
|
||||
m_diagram->views().isEmpty() ? nullptr : m_diagram->views().first(),
|
||||
QObject::tr("Erreur"),
|
||||
QObject::tr("Le fichier PDF ne contient aucune page.")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Always show the dialog so the user can choose page and DPI
|
||||
PdfPagesDialog dialog(document,
|
||||
m_diagram->views().isEmpty() ? nullptr : m_diagram->views().first());
|
||||
if (dialog.exec() != QDialog::Accepted) return;
|
||||
int pageIndex = dialog.selectedPage() - 1; // Convert to 0-based index
|
||||
int dpi = dialog.selectedDpi();
|
||||
|
||||
// Calculate pixel size from PDF points at the user-selected DPI
|
||||
// PDF point = 1/72 inch
|
||||
QSizeF pageSize = document.pagePointSize(pageIndex);
|
||||
int pixelWidth = qRound((pageSize.width() / 72.0) * dpi);
|
||||
int pixelHeight = qRound((pageSize.height() / 72.0) * dpi);
|
||||
|
||||
if (pixelWidth <= 0 || pixelHeight <= 0)
|
||||
{
|
||||
QMessageBox::critical(
|
||||
m_diagram->views().isEmpty() ? nullptr : m_diagram->views().first(),
|
||||
QObject::tr("Erreur"),
|
||||
QObject::tr("Impossible de déterminer la taille de la page PDF.")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Render PDF page to QImage
|
||||
QImage image = document.render(pageIndex, QSize(pixelWidth, pixelHeight));
|
||||
if (image.isNull())
|
||||
{
|
||||
QMessageBox::critical(
|
||||
m_diagram->views().isEmpty() ? nullptr : m_diagram->views().first(),
|
||||
QObject::tr("Erreur"),
|
||||
QObject::tr("Impossible de rendre la page PDF.")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill white background to handle transparent PDFs
|
||||
QImage background(image.size(), QImage::Format_ARGB32_Premultiplied);
|
||||
background.fill(Qt::white);
|
||||
QPainter painter(&background);
|
||||
painter.drawImage(0, 0, image);
|
||||
painter.end();
|
||||
|
||||
m_image = new DiagramImageItem(QPixmap::fromImage(background));
|
||||
m_running = true;
|
||||
}
|
||||
|
||||
#endif // QET_HAS_QTPDF
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef DIAGRAMEVENTADDPDF_H
|
||||
#define DIAGRAMEVENTADDPDF_H
|
||||
|
||||
// PDF page import needs the QtPdf module (QET_HAS_QTPDF, set by CMake
|
||||
// only when QtPdf is present AND Qt >= 6.4, for QPdfDocument::pagePointSize()).
|
||||
#ifdef QET_HAS_QTPDF
|
||||
|
||||
#include "diagrameventinterface.h"
|
||||
|
||||
class Diagram;
|
||||
class DiagramImageItem;
|
||||
|
||||
/**
|
||||
@brief The DiagramEventAddPdf class
|
||||
This diagram event handles the adding of a PDF page as an image in a diagram.
|
||||
The selected PDF page is rendered to a QImage at 150 DPI and added as a
|
||||
DiagramImageItem. The placement, rotation and scaling behavior is identical
|
||||
to the existing image import.
|
||||
*/
|
||||
class DiagramEventAddPdf : public DiagramEventInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DiagramEventAddPdf(Diagram *diagram);
|
||||
~DiagramEventAddPdf() override;
|
||||
|
||||
void mousePressEvent (QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseMoveEvent (QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseDoubleClickEvent (QGraphicsSceneMouseEvent *event) override;
|
||||
void wheelEvent (QGraphicsSceneWheelEvent *event) override;
|
||||
|
||||
bool isNull() const;
|
||||
|
||||
private:
|
||||
void openDialog();
|
||||
|
||||
DiagramImageItem *m_image;
|
||||
bool m_is_added;
|
||||
};
|
||||
|
||||
#endif // QET_HAS_QTPDF
|
||||
|
||||
#endif // DIAGRAMEVENTADDPDF_H
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "diagrameventaddshape.h"
|
||||
|
||||
#include "../diagram.h"
|
||||
#include "../lastusedstyle.h"
|
||||
#include "../undocommand/addgraphicsobjectcommand.h"
|
||||
|
||||
/**
|
||||
@@ -77,6 +78,14 @@ void DiagramEventAddShape::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
if (!m_shape_item)
|
||||
{
|
||||
m_shape_item = new QetShapeItem(pos, pos, m_shape_type);
|
||||
//Start from whatever pen/brush was last applied this
|
||||
//session, rather than always the hardcoded default.
|
||||
if (LastUsedStyle::hasShapePen()) {
|
||||
m_shape_item->setPen(LastUsedStyle::shapePen());
|
||||
}
|
||||
if (LastUsedStyle::hasShapeBrush()) {
|
||||
m_shape_item->setBrush(LastUsedStyle::shapeBrush());
|
||||
}
|
||||
m_diagram->addItem (m_shape_item);
|
||||
event->setAccepted(true);
|
||||
return;
|
||||
|
||||
+6
-27
@@ -79,7 +79,7 @@ DiagramView::DiagramView(Diagram *diagram, QWidget *parent) :
|
||||
m_diagram->loadCndFolioSeq();
|
||||
|
||||
m_paste_here = new QAction(QET::Icons::EditPaste, tr("Coller ici", "context menu action"), this);
|
||||
connect(m_paste_here, SIGNAL(triggered()), this, SLOT(pasteHere()));
|
||||
connect(m_paste_here, &QAction::triggered, this, &DiagramView::pasteHere);
|
||||
|
||||
m_multi_paste = new QAction(QET::Icons::EditPaste, tr("Collage multiple"), this);
|
||||
connect(m_multi_paste, &QAction::triggered, [this]() {
|
||||
@@ -89,7 +89,7 @@ DiagramView::DiagramView(Diagram *diagram, QWidget *parent) :
|
||||
|
||||
// Setup the action to create a template
|
||||
m_create_template = new QAction(tr("Créer un template", "context menu action"), this);
|
||||
connect(m_create_template, SIGNAL(triggered()), this, SLOT(createTemplateFromSelection()));
|
||||
connect(m_create_template, &QAction::triggered, this, &DiagramView::createTemplateFromSelection);
|
||||
|
||||
//setup three separators, to be use in context menu
|
||||
for(int i=0 ; i<3 ; ++i)
|
||||
@@ -98,10 +98,10 @@ DiagramView::DiagramView(Diagram *diagram, QWidget *parent) :
|
||||
m_separators.last()->setSeparator(true);
|
||||
}
|
||||
|
||||
connect(m_diagram, SIGNAL(showDiagram(Diagram*)), this, SIGNAL(showDiagram(Diagram*)));
|
||||
connect(m_diagram, SIGNAL(sceneRectChanged(QRectF)), this, SLOT(adjustSceneRect()));
|
||||
connect(&(m_diagram -> border_and_titleblock), SIGNAL(diagramTitleChanged(const QString &)), this, SLOT(updateWindowTitle()));
|
||||
connect(diagram, SIGNAL(findElementRequired(ElementsLocation)), this, SIGNAL(findElementRequired(ElementsLocation)));
|
||||
connect(m_diagram, &Diagram::showDiagram, this, &DiagramView::showDiagram);
|
||||
connect(m_diagram, &QGraphicsScene::sceneRectChanged, this, &DiagramView::adjustSceneRect);
|
||||
connect(&(m_diagram -> border_and_titleblock), &BorderTitleBlock::informationChanged, this, &DiagramView::updateWindowTitle);
|
||||
connect(diagram, &Diagram::findElementRequired, this, &DiagramView::findElementRequired);
|
||||
|
||||
QShortcut *edit_conductor_color_shortcut = new QShortcut(QKeySequence(Qt::Key_F2), this);
|
||||
connect(edit_conductor_color_shortcut, &QShortcut::activated, [this]()
|
||||
@@ -458,14 +458,7 @@ void DiagramView::mousePressEvent(QMouseEvent *e)
|
||||
if (m_event_interface && m_event_interface->mousePressEvent(e)) return;
|
||||
|
||||
//Start drag view when hold the middle button
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 1) // ### Qt 6: remove
|
||||
if (e->button() == Qt::MidButton)
|
||||
#else
|
||||
#if TODO_LIST
|
||||
#pragma message("@TODO remove code for QT 6 or later")
|
||||
#endif
|
||||
if (e->button() == Qt::MiddleButton)
|
||||
#endif
|
||||
{
|
||||
m_drag_last_pos = e->pos();
|
||||
viewport()->setCursor(Qt::ClosedHandCursor);
|
||||
@@ -515,14 +508,7 @@ void DiagramView::mouseMoveEvent(QMouseEvent *e)
|
||||
if (m_event_interface && m_event_interface->mouseMoveEvent(e)) return;
|
||||
|
||||
// Drag the view
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 1) // ### Qt 6: remove
|
||||
if (e->buttons() == Qt::MidButton)
|
||||
#else
|
||||
#if TODO_LIST
|
||||
#pragma message("@TODO remove code for QT 6 or later")
|
||||
#endif
|
||||
if (e->buttons() == Qt::MiddleButton)
|
||||
#endif
|
||||
{
|
||||
QScrollBar *h = horizontalScrollBar();
|
||||
QScrollBar *v = verticalScrollBar();
|
||||
@@ -583,14 +569,7 @@ void DiagramView::mouseReleaseEvent(QMouseEvent *e)
|
||||
if (m_event_interface && m_event_interface->mouseReleaseEvent(e)) return;
|
||||
|
||||
// Stop drag view
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 1) // ### Qt 6: remove
|
||||
if (e->button() == Qt::MidButton)
|
||||
#else
|
||||
#if TODO_LIST
|
||||
#pragma message("@TODO remove code for QT 6 or later")
|
||||
#endif
|
||||
if (e->button() == Qt::MiddleButton)
|
||||
#endif
|
||||
{
|
||||
viewport()->setCursor(Qt::ArrowCursor);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "dxfpaintdevice.h"
|
||||
#include "createdxf.h"
|
||||
|
||||
#include <QPainterPath>
|
||||
#include <QTextItem>
|
||||
#include <QtMath>
|
||||
#include <QDebug>
|
||||
|
||||
/**
|
||||
@brief DxfPaintEngine::DxfPaintEngine
|
||||
@param filepath path of the already-open DXF file entities are
|
||||
appended to (see Createdxf::dxfBegin()/dxfEnd()).
|
||||
*/
|
||||
DxfPaintEngine::DxfPaintEngine(const QString &filepath) :
|
||||
QPaintEngine(QPaintEngine::AllFeatures),
|
||||
m_filepath(filepath)
|
||||
{
|
||||
}
|
||||
|
||||
bool DxfPaintEngine::begin(QPaintDevice *pdev)
|
||||
{
|
||||
Q_UNUSED(pdev)
|
||||
setActive(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DxfPaintEngine::end()
|
||||
{
|
||||
setActive(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DxfPaintEngine::updateState
|
||||
Track the properties this engine cares about. Everything drawn is
|
||||
mapped through the transform in effect at draw time - this is what
|
||||
lets an item's paint() be replayed unmodified at whatever scene
|
||||
position/rotation it actually has, exactly as QPainter does for
|
||||
on-screen painting.
|
||||
*/
|
||||
void DxfPaintEngine::updateState(const QPaintEngineState &state)
|
||||
{
|
||||
QPaintEngine::DirtyFlags flags = state.state();
|
||||
|
||||
if (flags & QPaintEngine::DirtyTransform)
|
||||
m_world_transform = state.transform();
|
||||
if (flags & QPaintEngine::DirtyPen)
|
||||
m_pen = state.pen();
|
||||
if (flags & QPaintEngine::DirtyBrush)
|
||||
m_brush = state.brush();
|
||||
if (flags & QPaintEngine::DirtyFont)
|
||||
m_font = state.font();
|
||||
}
|
||||
|
||||
QPointF DxfPaintEngine::toDxf(const QPointF &local_point) const
|
||||
{
|
||||
// Createdxf's QRectF/QLineF/QPolygonF/QPointF convenience overloads
|
||||
// each apply xScale/yScale and the sheetHeight Y-flip internally, so
|
||||
// this engine's whole job is composing the world transform - not
|
||||
// touching DXF units at all.
|
||||
return m_world_transform.map(local_point);
|
||||
}
|
||||
|
||||
void DxfPaintEngine::strokePolygon(const QPolygonF &poly, bool close)
|
||||
{
|
||||
if (poly.size() < 2)
|
||||
return;
|
||||
if (m_pen.style() == Qt::NoPen)
|
||||
return;
|
||||
|
||||
QPolygonF mapped;
|
||||
mapped.reserve(poly.size());
|
||||
for (const QPointF &p : poly)
|
||||
mapped << toDxf(p);
|
||||
|
||||
if (close && mapped.first() != mapped.last())
|
||||
mapped << mapped.first();
|
||||
|
||||
Createdxf::drawPolyline(m_filepath, mapped, Createdxf::dxfColor(m_pen));
|
||||
}
|
||||
|
||||
void DxfPaintEngine::drawLines(const QLineF *lines, int lineCount)
|
||||
{
|
||||
if (m_pen.style() == Qt::NoPen)
|
||||
return;
|
||||
for (int i = 0; i < lineCount; ++i)
|
||||
{
|
||||
QLineF mapped(toDxf(lines[i].p1()), toDxf(lines[i].p2()));
|
||||
Createdxf::drawLine(m_filepath, mapped, Createdxf::dxfColor(m_pen));
|
||||
}
|
||||
}
|
||||
|
||||
void DxfPaintEngine::drawRects(const QRectF *rects, int rectCount)
|
||||
{
|
||||
for (int i = 0; i < rectCount; ++i)
|
||||
{
|
||||
QPolygonF corners;
|
||||
corners << rects[i].topLeft() << rects[i].topRight()
|
||||
<< rects[i].bottomRight() << rects[i].bottomLeft();
|
||||
|
||||
// No HATCH support in v1 (see the design note in the header) - a
|
||||
// filled rect is emitted as its outline only, same as an unfilled
|
||||
// one. Still emitted (not skipped) when there's no pen but there
|
||||
// is a brush, so a filled-only shape isn't dropped entirely.
|
||||
if (m_pen.style() != Qt::NoPen || m_brush.style() != Qt::NoBrush)
|
||||
strokePolygon(corners, true);
|
||||
}
|
||||
}
|
||||
|
||||
void DxfPaintEngine::drawEllipse(const QRectF &rect)
|
||||
{
|
||||
if (m_pen.style() == Qt::NoPen && m_brush.style() == Qt::NoBrush)
|
||||
return;
|
||||
|
||||
// A non axis-aligned ellipse (rotation baked into m_world_transform)
|
||||
// can't be expressed as Createdxf::drawEllipse(), which only accepts
|
||||
// an axis-aligned QRectF. Approximate with a flattened polygon in
|
||||
// that case; use the exact CIRCLE/ELLIPSE entity when the transform
|
||||
// is axis-aligned, matching what the existing hand-written exporters
|
||||
// already produce for unrotated shapes.
|
||||
const bool axis_aligned = qFuzzyIsNull(m_world_transform.m12())
|
||||
&& qFuzzyIsNull(m_world_transform.m21());
|
||||
if (axis_aligned)
|
||||
{
|
||||
QRectF mapped = m_world_transform.mapRect(rect);
|
||||
Createdxf::drawEllipse(m_filepath, mapped, Createdxf::dxfColor(m_pen));
|
||||
return;
|
||||
}
|
||||
|
||||
QPainterPath path;
|
||||
path.addEllipse(rect);
|
||||
const auto polygons = path.toSubpathPolygons();
|
||||
for (const QPolygonF &poly : polygons)
|
||||
strokePolygon(poly, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DxfPaintEngine::drawPath
|
||||
Handles drawArc()/drawPie() (QPainterPath::Arc elements - emitted as
|
||||
chord segments, since Createdxf's ARC-equivalent
|
||||
(drawArcEllipse()) only takes an axis-aligned ellipse + angle pair,
|
||||
which doesn't compose with an arbitrary path transform) and
|
||||
fillPath()/generic paths (flattened to polylines).
|
||||
*/
|
||||
void DxfPaintEngine::drawPath(const QPainterPath &path)
|
||||
{
|
||||
if (m_pen.style() == Qt::NoPen && m_brush.style() == Qt::NoBrush)
|
||||
return;
|
||||
|
||||
const auto polygons = path.toSubpathPolygons();
|
||||
for (const QPolygonF &poly : polygons)
|
||||
strokePolygon(poly, path.isEmpty() ? false : true);
|
||||
}
|
||||
|
||||
void DxfPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
|
||||
{
|
||||
if (pointCount < 2)
|
||||
return;
|
||||
QPolygonF poly;
|
||||
poly.reserve(pointCount);
|
||||
for (int i = 0; i < pointCount; ++i)
|
||||
poly << points[i];
|
||||
|
||||
strokePolygon(poly, mode != PolylineMode);
|
||||
}
|
||||
|
||||
void DxfPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
|
||||
{
|
||||
qreal font_size = textItem.font().pointSizeF();
|
||||
if (font_size < 0)
|
||||
font_size = textItem.font().pixelSize();
|
||||
|
||||
// Rotation: extract the angle baked into the world transform by
|
||||
// mapping a unit vector, the same approach used to compute rotation
|
||||
// for the existing element-text DXF export in exportdialog.cpp.
|
||||
QPointF origin = m_world_transform.map(p);
|
||||
QPointF right = m_world_transform.map(p + QPointF(1, 0));
|
||||
qreal angle = qRadiansToDegrees(qAtan2(-(right.y() - origin.y()), right.x() - origin.x()));
|
||||
|
||||
Createdxf::drawText(m_filepath, textItem.text(), toDxf(p), font_size,
|
||||
angle, Createdxf::dxfColor(m_pen), 0.72);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DxfPaintEngine::drawPixmap
|
||||
Not implemented in v1 - out of scope per the design note in the
|
||||
header (CrossRefItem, the only item exported through this engine so
|
||||
far, never draws a pixmap). qWarning() rather than a hard failure, so
|
||||
an item that does call this in the future degrades to "one entity
|
||||
missing" instead of crashing the whole export.
|
||||
*/
|
||||
void DxfPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
|
||||
{
|
||||
Q_UNUSED(r)
|
||||
Q_UNUSED(pm)
|
||||
Q_UNUSED(sr)
|
||||
qWarning() << "DxfPaintEngine::drawPixmap: not supported, entity skipped";
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DxfPaintDevice::DxfPaintDevice
|
||||
@param filepath path of the already-open DXF file (see
|
||||
Createdxf::dxfBegin()/dxfEnd() - this device only appends entities in
|
||||
between, it does not open/close the file itself).
|
||||
*/
|
||||
DxfPaintDevice::DxfPaintDevice(const QString &filepath) :
|
||||
QPaintDevice(),
|
||||
m_filepath(filepath),
|
||||
m_engine(new DxfPaintEngine(filepath))
|
||||
{
|
||||
}
|
||||
|
||||
DxfPaintDevice::~DxfPaintDevice()
|
||||
{
|
||||
delete m_engine;
|
||||
}
|
||||
|
||||
QPaintEngine *DxfPaintDevice::paintEngine() const
|
||||
{
|
||||
return m_engine;
|
||||
}
|
||||
|
||||
int DxfPaintDevice::metric(PaintDeviceMetric metric) const
|
||||
{
|
||||
// A DXF document has no pixel grid or physical size in the sense
|
||||
// QPaintDevice::metric() expects - large-but-finite values here are
|
||||
// enough to keep QPainter's own bookkeeping (e.g. clip region setup)
|
||||
// from misbehaving; nothing in this engine actually consults them.
|
||||
switch (metric)
|
||||
{
|
||||
case PdmWidth:
|
||||
case PdmHeight:
|
||||
return 1000000;
|
||||
case PdmDpiX:
|
||||
case PdmDpiY:
|
||||
case PdmPhysicalDpiX:
|
||||
case PdmPhysicalDpiY:
|
||||
return 96;
|
||||
case PdmWidthMM:
|
||||
case PdmHeightMM:
|
||||
return 1000000;
|
||||
case PdmNumColors:
|
||||
return 16777216;
|
||||
case PdmDepth:
|
||||
return 24;
|
||||
case PdmDevicePixelRatio:
|
||||
case PdmDevicePixelRatioScaled:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef DXFPAINTDEVICE_H
|
||||
#define DXFPAINTDEVICE_H
|
||||
|
||||
#include <QPaintDevice>
|
||||
#include <QPaintEngine>
|
||||
#include <QPen>
|
||||
#include <QBrush>
|
||||
#include <QFont>
|
||||
#include <QString>
|
||||
|
||||
/**
|
||||
@brief The DxfPaintEngine class
|
||||
A QPaintEngine that translates the small set of QPainter calls made by
|
||||
QGraphicsItem::paint() implementations (drawLines, drawRects,
|
||||
drawEllipse, drawPolygon/drawPolyline, drawTextItem) into DXF entities
|
||||
written through Createdxf, instead of pixels.
|
||||
|
||||
This exists so that an item's existing, already-correct paint() code
|
||||
can be reused unmodified to produce a DXF export: construct a QPainter
|
||||
on a DxfPaintDevice targeting the item, call painter.begin()/end()
|
||||
around item->paint(&painter, ...), and every primitive the item draws
|
||||
becomes a DXF entity at the item's scene position instead of a pixel
|
||||
on screen.
|
||||
|
||||
Scope (deliberately not the full QPainter surface - only what
|
||||
QElectroTech's own paint() implementations are observed to call):
|
||||
- drawLines -> LINE
|
||||
- drawRects -> LWPOLYLINE (closed 4-point outline; DXF has no
|
||||
filled-rect primitive in the AC1006 dialect this
|
||||
exporter targets, so brush fill is not emitted -
|
||||
see fillPath below)
|
||||
- drawEllipse -> Createdxf::drawArcEllipse (full sweep) or CIRCLE
|
||||
- drawPath -> arcs (from QPainterPath::Arc elements, used by
|
||||
drawArc/drawPie) become a sequence of LINE chords;
|
||||
anything else in the path is flattened to
|
||||
polyline segments via QPainterPath::toSubpathPolygons
|
||||
- drawPolygon -> LWPOLYLINE
|
||||
- drawTextItem -> TEXT (drawText() calls route through this)
|
||||
- fillPath -> same outline-only handling as drawRects; no HATCH
|
||||
support in v1 (see design note in the PR)
|
||||
|
||||
Anything outside this list (images, gradients, etc.) is intentionally
|
||||
unimplemented and asserts in debug builds rather than silently
|
||||
producing an incomplete drawing - callers should know immediately if
|
||||
an item they're exporting uses something this engine doesn't cover
|
||||
yet, rather than getting a DXF file quietly missing content.
|
||||
*/
|
||||
class DxfPaintEngine : public QPaintEngine
|
||||
{
|
||||
public:
|
||||
explicit DxfPaintEngine(const QString &filepath);
|
||||
|
||||
bool begin(QPaintDevice *pdev) override;
|
||||
bool end() override;
|
||||
void updateState(const QPaintEngineState &state) override;
|
||||
|
||||
void drawLines(const QLineF *lines, int lineCount) override;
|
||||
void drawRects(const QRectF *rects, int rectCount) override;
|
||||
void drawEllipse(const QRectF &rect) override;
|
||||
void drawPath(const QPainterPath &path) override;
|
||||
void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override;
|
||||
void drawTextItem(const QPointF &p, const QTextItem &textItem) override;
|
||||
void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override;
|
||||
|
||||
Type type() const override { return QPaintEngine::User; }
|
||||
|
||||
private:
|
||||
QPointF toDxf(const QPointF &scene_point) const;
|
||||
void strokePolygon(const QPolygonF &poly, bool close);
|
||||
|
||||
QString m_filepath;
|
||||
QTransform m_world_transform;
|
||||
QPen m_pen;
|
||||
QBrush m_brush;
|
||||
QFont m_font;
|
||||
};
|
||||
|
||||
/**
|
||||
@brief The DxfPaintDevice class
|
||||
Pairs with DxfPaintEngine. One instance targets one already-open DXF
|
||||
file (Createdxf::dxfBegin()/dxfEnd() bracket the whole export, same as
|
||||
today - this device only ever appends entities in between).
|
||||
*/
|
||||
class DxfPaintDevice : public QPaintDevice
|
||||
{
|
||||
public:
|
||||
explicit DxfPaintDevice(const QString &filepath);
|
||||
~DxfPaintDevice() override;
|
||||
|
||||
QPaintEngine *paintEngine() const override;
|
||||
|
||||
protected:
|
||||
int metric(PaintDeviceMetric metric) const override;
|
||||
|
||||
private:
|
||||
QString m_filepath;
|
||||
DxfPaintEngine *m_engine;
|
||||
};
|
||||
|
||||
#endif // DXFPAINTDEVICE_H
|
||||
@@ -276,14 +276,7 @@ void ChangeZValueCommand::applyRaise(const QList<QGraphicsItem *> &items_list) {
|
||||
for (int i = my_items_list.count() - 2 ; i >= 0 ; -- i) {
|
||||
if (my_items_list[i] -> isSelected()) {
|
||||
if (!my_items_list[i +1] -> isSelected()) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) // ### Qt 6: remove
|
||||
my_items_list.swap(i, i + 1);
|
||||
#else
|
||||
#if TODO_LIST
|
||||
#pragma message("@TODO remove code for QT 5.13 or later")
|
||||
#endif
|
||||
my_items_list.swapItemsAt(i, i + 1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,14 +294,7 @@ void ChangeZValueCommand::applyLower(const QList<QGraphicsItem *> &items_list) {
|
||||
for (int i = 1 ; i < my_items_list.count() ; ++ i) {
|
||||
if (my_items_list[i] -> isSelected()) {
|
||||
if (!my_items_list[i - 1] -> isSelected()) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) // ### Qt 6: remove
|
||||
my_items_list.swap(i, i - 1);
|
||||
#else
|
||||
#if TODO_LIST
|
||||
#pragma message("@TODO remove code for QT 5.13 or later")
|
||||
#endif
|
||||
my_items_list.swapItemsAt(i, i - 1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,10 +69,8 @@ ElementScene::ElementScene(QETElementEditor *editor, QObject *parent) :
|
||||
initPasteArea();
|
||||
m_undo_stack.setClean();
|
||||
m_decorator_lock = new QMutex();
|
||||
connect(&m_undo_stack, SIGNAL(indexChanged(int)),
|
||||
this, SLOT(managePrimitivesGroups()));
|
||||
connect(this, SIGNAL(selectionChanged()),
|
||||
this, SLOT(managePrimitivesGroups()));
|
||||
connect(&m_undo_stack, &QUndoStack::indexChanged, this, &ElementScene::managePrimitivesGroups);
|
||||
connect(this, &ElementScene::selectionChanged, this, &ElementScene::managePrimitivesGroups);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,8 +100,7 @@ void ElementScene::setElementData(ElementData data)
|
||||
ElementScene::~ElementScene()
|
||||
{
|
||||
//Disconnect to avoid crash, see bug report N° 122.
|
||||
disconnect(&m_undo_stack, SIGNAL(indexChanged(int)),
|
||||
this, SLOT(managePrimitivesGroups()));
|
||||
disconnect(&m_undo_stack, &QUndoStack::indexChanged, this, &ElementScene::managePrimitivesGroups);
|
||||
delete m_decorator_lock;
|
||||
|
||||
if (m_event_interface)
|
||||
@@ -124,7 +121,7 @@ void ElementScene::mouseMoveEvent(QGraphicsSceneMouseEvent *e)
|
||||
{
|
||||
if (m_event_interface) {
|
||||
if (m_event_interface -> mouseMoveEvent(e)) {
|
||||
emit mouseMoved(e -> scenePos());
|
||||
emit mouseMoved(snapToGrid(e->scenePos()));
|
||||
if (m_event_interface->isFinish()) {
|
||||
delete m_event_interface;
|
||||
m_event_interface = nullptr;
|
||||
@@ -907,8 +904,8 @@ void ElementScene::slot_editAuthorInformations()
|
||||
QDialogButtonBox::Ok
|
||||
| QDialogButtonBox::Cancel);
|
||||
dialog_layout -> addWidget(dialog_buttons);
|
||||
connect(dialog_buttons, SIGNAL(accepted()),&dialog_author, SLOT(accept()));
|
||||
connect(dialog_buttons, SIGNAL(rejected()),&dialog_author, SLOT(reject()));
|
||||
connect(dialog_buttons, &QDialogButtonBox::accepted, &dialog_author, &QDialog::accept);
|
||||
connect(dialog_buttons, &QDialogButtonBox::rejected, &dialog_author, &QDialog::reject);
|
||||
|
||||
// start the dialogue
|
||||
// lance le dialogue
|
||||
@@ -1399,9 +1396,7 @@ void ElementScene::managePrimitivesGroups()
|
||||
if (!m_decorator)
|
||||
{
|
||||
m_decorator = new ElementPrimitiveDecorator();
|
||||
connect(m_decorator,
|
||||
SIGNAL(actionFinished(ElementEditionCommand*)),
|
||||
this, SLOT(stackAction(ElementEditionCommand *)));
|
||||
connect(m_decorator, &ElementPrimitiveDecorator::actionFinished, this, &ElementScene::stackAction);
|
||||
addItem(m_decorator);
|
||||
m_decorator -> hide();
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ ElementView::ElementView(ElementScene *scene, QWidget *parent) :
|
||||
setResizeAnchor(QGraphicsView::AnchorUnderMouse);
|
||||
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
|
||||
zoomReset();
|
||||
connect(m_scene, SIGNAL(pasteAreaDefined(const QRectF &)), this, SLOT(pasteAreaDefined(const QRectF &)));
|
||||
connect(m_scene, SIGNAL(needZoomFit()), this, SLOT(zoomFit()));
|
||||
connect(m_scene, &ElementScene::pasteAreaDefined, this, &ElementView::pasteAreaDefined);
|
||||
connect(m_scene, &ElementScene::needZoomFit, this, &ElementView::zoomFit);
|
||||
}
|
||||
|
||||
/// Destructeur
|
||||
@@ -372,14 +372,7 @@ ElementContent ElementView::pasteWithOffset(const QDomDocument &xml_document) {
|
||||
*/
|
||||
void ElementView::mousePressEvent(QMouseEvent* e)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 1) // ### Qt 6: remove
|
||||
if (e->button() == Qt::MidButton)
|
||||
#else
|
||||
#if TODO_LIST
|
||||
#pragma message("@TODO remove code for QT 6 or later")
|
||||
#endif
|
||||
if (e->button() == Qt::MiddleButton)
|
||||
#endif
|
||||
{
|
||||
setCursor( (Qt::ClosedHandCursor));
|
||||
reference_view_ = e->pos();
|
||||
@@ -394,14 +387,7 @@ void ElementView::mousePressEvent(QMouseEvent* e)
|
||||
*/
|
||||
void ElementView::mouseMoveEvent(QMouseEvent* e)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 1) // ### Qt 6: remove
|
||||
if (e->buttons() == Qt::MidButton)
|
||||
#else
|
||||
#if TODO_LIST
|
||||
#pragma message("@TODO remove code for QT 6 or later")
|
||||
#endif
|
||||
if (e->buttons() == Qt::MiddleButton)
|
||||
#endif
|
||||
{
|
||||
QScrollBar *h = horizontalScrollBar();
|
||||
QScrollBar *v = verticalScrollBar();
|
||||
@@ -420,14 +406,7 @@ void ElementView::mouseMoveEvent(QMouseEvent* e)
|
||||
*/
|
||||
void ElementView::mouseReleaseEvent(QMouseEvent* e)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 1) // ### Qt 6: remove
|
||||
if (e->button() == Qt::MidButton)
|
||||
#else
|
||||
#if TODO_LIST
|
||||
#pragma message("@TODO remove code for QT 6 or later")
|
||||
#endif
|
||||
if (e->button() == Qt::MiddleButton)
|
||||
#endif
|
||||
{
|
||||
setCursor(Qt::ArrowCursor);
|
||||
adjustSceneRect();
|
||||
|
||||
@@ -521,14 +521,7 @@ void CustomElementGraphicPart::stylesFromXml(const QDomElement &qde)
|
||||
resetStyles();
|
||||
|
||||
//Get the list of pair style/value
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) // ### Qt 6: remove
|
||||
QStringList styles = qde.attribute("style").split(";", QString::SkipEmptyParts);
|
||||
#else
|
||||
#if TODO_LIST
|
||||
#pragma message("@TODO remove code for QT 5.14 or later")
|
||||
#endif
|
||||
QStringList styles = qde.attribute("style").split(";", Qt::SkipEmptyParts);
|
||||
#endif
|
||||
|
||||
//Check each pair of style
|
||||
QRegularExpression rx("^\\s*([a-z-]+)\\s*:\\s*([a-zA-Z-]+)\\s*$");
|
||||
|
||||
@@ -148,6 +148,7 @@ const QDomElement PartDynamicTextField::toXml(QDomDocument &dom_doc) const
|
||||
root_element.setAttribute("frame", m_frame? "true" : "false");
|
||||
root_element.setAttribute("text_width", QString::number(m_text_width));
|
||||
root_element.setAttribute("keep_visual_rotation", m_keep_visual_rotation ? "true" : "false");
|
||||
root_element.setAttribute("rotation_point_center", m_rotation_point_center ? "true" : "false");
|
||||
|
||||
QMetaEnum me = DynamicElementTextItem::textFromMetaEnum();
|
||||
root_element.setAttribute("text_from", me.valueToKey(m_text_from));
|
||||
@@ -212,6 +213,7 @@ void PartDynamicTextField::fromXml(const QDomElement &dom_elmt) {
|
||||
setZValue(dom_elmt.attribute("z", QString::number(zValue())).toDouble());
|
||||
QGraphicsObject::setRotation(QET::correctAngle(dom_elmt.attribute("rotation", QString::number(0)).toDouble()));
|
||||
setKeepVisualRotation(dom_elmt.attribute("keep_visual_rotation", "true") == "true"? true : false);
|
||||
setRotationPointCenter(dom_elmt.attribute("rotation_point_center", "false") == "true"? true : false);
|
||||
|
||||
if (dom_elmt.hasAttribute("font")) {
|
||||
QFont font_;
|
||||
@@ -492,6 +494,20 @@ bool PartDynamicTextField::keepVisualRotation() const {
|
||||
return m_keep_visual_rotation;
|
||||
}
|
||||
|
||||
void PartDynamicTextField::setRotationPointCenter(const bool ¢er)
|
||||
{
|
||||
if (center == this->m_rotation_point_center) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_rotation_point_center = center;
|
||||
emit rotationPointCenterChanged(center);
|
||||
}
|
||||
|
||||
bool PartDynamicTextField::rotationPointCenter() const {
|
||||
return m_rotation_point_center;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief PartDynamicTextField::mouseMoveEvent
|
||||
@param event
|
||||
|
||||
@@ -44,6 +44,7 @@ class PartDynamicTextField : public QGraphicsTextItem, public CustomElementPart
|
||||
Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged)
|
||||
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
|
||||
Q_PROPERTY(bool keepVisualRotation READ keepVisualRotation WRITE setKeepVisualRotation NOTIFY keepVisualRotationChanged)
|
||||
Q_PROPERTY(bool rotationPointCenter READ rotationPointCenter WRITE setRotationPointCenter NOTIFY rotationPointCenterChanged)
|
||||
|
||||
public:
|
||||
///PROPERTY
|
||||
@@ -62,6 +63,7 @@ class PartDynamicTextField : public QGraphicsTextItem, public CustomElementPart
|
||||
void alignmentChanged(Qt::Alignment alignment);
|
||||
void fontChanged(QFont font);
|
||||
void keepVisualRotationChanged(bool keep);
|
||||
void rotationPointCenterChanged(bool center);
|
||||
|
||||
public:
|
||||
PartDynamicTextField(QETElementEditor *editor, QGraphicsItem *parent = nullptr);
|
||||
@@ -100,6 +102,8 @@ class PartDynamicTextField : public QGraphicsTextItem, public CustomElementPart
|
||||
void setFont(const QFont &font);
|
||||
void setKeepVisualRotation(const bool &keep);
|
||||
bool keepVisualRotation() const;
|
||||
void setRotationPointCenter(const bool ¢er);
|
||||
bool rotationPointCenter() const;
|
||||
|
||||
void setRotation(qreal angle);
|
||||
void mirror();
|
||||
@@ -129,7 +133,8 @@ class PartDynamicTextField : public QGraphicsTextItem, public CustomElementPart
|
||||
bool m_frame = false,
|
||||
m_first_add = true,
|
||||
m_block_alignment = false,
|
||||
m_keep_visual_rotation = false;
|
||||
m_keep_visual_rotation = false,
|
||||
m_rotation_point_center = false;
|
||||
qreal m_text_width = -1;
|
||||
Qt::Alignment m_alignment = Qt::AlignTop|Qt::AlignLeft;
|
||||
QRectF m_alignment_rect;
|
||||
|
||||
@@ -50,14 +50,14 @@ PartText::PartText(QETElementEditor *editor, QGraphicsItem *parent) :
|
||||
|
||||
adjustItemPosition(1);
|
||||
// adjust textfield position after line additions/deletions
|
||||
connect(document(),
|
||||
&QTextDocument::blockCountChanged,
|
||||
this,
|
||||
&PartText::adjustItemPosition);
|
||||
connect(document(),
|
||||
SIGNAL(blockCountChanged(int)),
|
||||
&QTextDocument::contentsChanged,
|
||||
this,
|
||||
SLOT(adjustItemPosition(int)));
|
||||
connect(document(),
|
||||
SIGNAL(contentsChanged()),
|
||||
this,
|
||||
SLOT(adjustItemPosition()));
|
||||
[this]() { adjustItemPosition(); });
|
||||
}
|
||||
|
||||
/// Destructeur
|
||||
@@ -440,6 +440,14 @@ void PartText::setFont(const QFont &font) {
|
||||
// at a different spot after save/reopen (the position recomputes from
|
||||
// the saved font on load). See #158.
|
||||
adjustItemPosition();
|
||||
// Keep real_font_size_ in sync with the actual font. It's the base
|
||||
// size startUserTransformation()/handleUserTransformation() scale
|
||||
// from when the user drags a resize handle, and flip() also reads it
|
||||
// to reposition the item. Left stale here, either one computes from
|
||||
// whatever size the item had when it was first created, ignoring any
|
||||
// size set since (toolbar, property editor, or loaded from XML) -
|
||||
// found investigating #158.
|
||||
real_font_size_ = font.pointSize();
|
||||
emit fontChanged(font);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -582,17 +582,25 @@ bool StyleEditor::isStyleEditable(QList<CustomElementPart *> cep_list)
|
||||
*/
|
||||
void StyleEditor::activeConnections(bool active) {
|
||||
if (active) {
|
||||
connect (outline_color, SIGNAL(activated(int)), this, SLOT(updatePartColor()));
|
||||
connect(line_style, SIGNAL(activated(int)), this, SLOT(updatePartLineStyle()));
|
||||
connect(size_weight, SIGNAL(activated(int)), this, SLOT(updatePartLineWeight()));
|
||||
connect(filling_color, SIGNAL(activated(int)), this, SLOT(updatePartFilling()));
|
||||
connect(antialiasing, SIGNAL(stateChanged(int)), this, SLOT(updatePartAntialiasing()));
|
||||
connect(outline_color, qOverload<int>(&QComboBox::activated), this, &StyleEditor::updatePartColor);
|
||||
connect(line_style, qOverload<int>(&QComboBox::activated), this, &StyleEditor::updatePartLineStyle);
|
||||
connect(size_weight, qOverload<int>(&QComboBox::activated), this, &StyleEditor::updatePartLineWeight);
|
||||
connect(filling_color, qOverload<int>(&QComboBox::activated), this, &StyleEditor::updatePartFilling);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0) // TODO Qt 6.7: remove, checkStateChanged() always available
|
||||
connect(antialiasing, &QCheckBox::stateChanged, this, &StyleEditor::updatePartAntialiasing);
|
||||
#else
|
||||
connect(antialiasing, &QCheckBox::checkStateChanged, this, &StyleEditor::updatePartAntialiasing);
|
||||
#endif
|
||||
} else {
|
||||
disconnect(outline_color, SIGNAL(activated(int)), this, SLOT(updatePartColor()));
|
||||
disconnect(line_style, SIGNAL(activated(int)), this, SLOT(updatePartLineStyle()));
|
||||
disconnect(size_weight, SIGNAL(activated(int)), this, SLOT(updatePartLineWeight()));
|
||||
disconnect(filling_color, SIGNAL(activated(int)), this, SLOT(updatePartFilling()));
|
||||
disconnect(antialiasing, SIGNAL(stateChanged(int)), this, SLOT(updatePartAntialiasing()));
|
||||
disconnect(outline_color, qOverload<int>(&QComboBox::activated), this, &StyleEditor::updatePartColor);
|
||||
disconnect(line_style, qOverload<int>(&QComboBox::activated), this, &StyleEditor::updatePartLineStyle);
|
||||
disconnect(size_weight, qOverload<int>(&QComboBox::activated), this, &StyleEditor::updatePartLineWeight);
|
||||
disconnect(filling_color, qOverload<int>(&QComboBox::activated), this, &StyleEditor::updatePartFilling);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0) // TODO Qt 6.7: remove, checkStateChanged() always available
|
||||
disconnect(antialiasing, &QCheckBox::stateChanged, this, &StyleEditor::updatePartAntialiasing);
|
||||
#else
|
||||
disconnect(antialiasing, &QCheckBox::checkStateChanged, this, &StyleEditor::updatePartAntialiasing);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user