Compare commits

..

42 Commits

Author SHA1 Message Date
Laurent Trinques f16cf7dac8 Merge remote-tracking branch 'origin/master' into qt6_cmake_joshua
# Conflicts:
#	CMakeLists.txt
2026-05-28 09:32:07 +02:00
Laurent Trinques 471d1f2538 Merge pull request #470 from Kellermorph/makro-fix
Fix: Wiring list filter and dynamic text timing
2026-05-27 06:19:06 +02:00
Kellermorph dc52105868 Fix: Wiring list filter and dynamic text timing 2026-05-26 18:31:37 +02:00
Laurent Trinques 1b8dc5f410 texteditor: set font size spinbox minimum to 4pt to prevent SIGSEGV 2026-05-25 13:14:57 +02:00
Laurent Trinques 26d5d019cc texteditor: fix SIGSEGV caused by font size spinbox reaching 0 2026-05-25 13:04:37 +02:00
Laurent Trinques 6dd7c2d926 crossrefitem: fix SIGSEGV crash + improve double-click navigation + fix PDF/SVG/print rendering
Windows Build / build-windows (push) Has been cancelled
Test Windows VS2026 migration / Build on windows-2025-vs2026 (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
Fix a use-after-free crash (SIGSEGV in QRegion::begin, Qt5Gui+0x49af60)
confirmed by analysis of 19 coredumps. The crash was triggered when the
scene viewport clip region was freed during zoom/resize events while
QPicture::play() replayed drawPolyline commands through the scene painter.
Qt's raster engine then dereferenced a stale QRegionData pointer.

Root cause: CrossRefItem used three nested QPicture objects (m_drawing,
m_hdr_no_ctc, m_hdr_nc_ctc). The nested drawPicture() calls amplified
the use-after-free risk on any repaint event.

Fix: remove all QPicture from CrossRefItem entirely.
- updateLabel() now uses a QImage-backed dummy painter to compute
  m_bounding_rect, m_shape_path and m_hovered_contacts_map geometry.
  A bool m_update_map flag prevents the map from being overwritten
  during paint().
- paint() calls drawAsCross()/drawAsContacts() directly on the scene
  painter — no QPicture::play() anywhere in the class.
- buildHeaderContact() now draws NO/NC symbols directly onto the painter
  instead of recording them into QPicture members.

Also fix mouseDoubleClickEvent: the element under the click is now found
directly from m_hovered_contacts_map using the event position, rather
than relying on m_hovered_contact which could be reset by hoverMoveEvent
between the two clicks of a double-click.

Also remove setBold(true) on terminal name labels: the Qt PDF/SVG/print
engine rendered bold at 4pt as extremely thick glyphs, making exports
unreadable. Normal weight at 4pt is correct and legible on all backends.

Fixes: SIGSEGV in CrossRefItem::paint() on zoom/resize
Fixes: double-click navigation unreliable on Xref contact symbols
Fixes: terminal name labels unreadable in PDF/SVG/print export
2026-05-24 16:00:45 +02:00
Laurent Trinques 47796e183a Update translation files 2026-05-23 19:28:22 +02:00
Laurent Trinques ddf5ffcd89 xref: add option to hide terminal names in cross-references
Add a new boolean property 'showTerminalName' (default: true) to
XRefProperties, with full persistence in XML and QSettings.

A new checkbox "Afficher les numéros de bornes dans les Xrefs" is
added to the XRefPropertiesWidget in the main display group (not in
the cross-only group), so it is active in both Cross and Contacts modes.

When unchecked, terminal names are hidden in all three rendering paths:
  - drawContact()            (Contacts mode: NO/NC/SW symbols)
  - fillCrossRef()           (Cross mode: NO and NC columns)
  - setUpCrossBoundingRect() (Cross mode: bounding rect sizing)

Backward compatible: existing project files without the attribute
default to showTerminalName=true (no visual change).

Files changed:
  sources/properties/xrefproperties.h
  sources/properties/xrefproperties.cpp
  sources/ui/xrefpropertieswidget.ui
  sources/ui/xrefpropertieswidget.cpp
  sources/qetgraphicsitem/crossrefitem.cpp
2026-05-23 19:23:43 +02:00
Laurent Trinques 7d718bb9a0 crossrefitem: fix terminal name sorting for power contacts
The previous sort used QString::toInt() to order terminal names,
which returns 0 for any string containing a non-numeric prefix
(e.g. "R1", "R2", "L1", "L2"...). This caused undefined sort order
and incorrect pole pairing in the Xref contact mirror.

Example: a 4-pole NC power contact with terminals R1..R8 was
displaying R1/R3, R5/R7, R2/R4, R6/R8 instead of the correct
R1/R2, R3/R4, R5/R6, R7/R8.

Fix: extract the trailing numeric part of each terminal name and
compare prefixes separately. If both names share the same prefix
and both have a trailing number, sort numerically on that number;
otherwise fall back to full string comparison.

This covers all naming conventions: "1"/"2"/"3", "R1"/"R2"/"R3",
"L1"/"L2"/"L3", etc.

Applied in both drawContact() and setUpCrossBoundingRect().
2026-05-23 15:50:11 +02:00
Laurent Trinques d949e6eb8c crossrefitem: fix SIGSEGV when dropping power NC contact on diagram
Three bugs were causing a crash (SIGSEGV in QRegion::begin) when
a power NC slave element was placed on a folio, even before linking
it to a master element.

1. m_drawing (QPicture) was never reset between updateLabel() calls.
   QPicture accumulates paint commands — calling qp.begin() on an
   existing QPicture appends to it rather than replacing its content.
   After several updates (load, move, hover...) the picture became
   corrupted and crashed on play().
   Fix: reset m_drawing = QPicture() at the start of updateLabel().

2. m_drawed_contacts was only initialized to 0 in drawAsContacts(),
   but not in drawAsCross(). When drawing in Cross mode, fillCrossRef()
   called drawContact() with an uninitialized m_drawed_contacts value,
   producing a garbage offset. The NC contact symbol uses drawPolyline()
   with a sub-pixel Y coordinate (offset+2.5); with a random offset Qt
   generated an invalid QRegion and crashed.
   This explains why NC contacts crashed but NO contacts did not: the
   NO symbol only uses drawLine() which is more tolerant of bad coords.
   Fix: add m_drawed_contacts = 0 at the start of drawAsCross().

3. setUpCrossBoundingRect() used QRectF() (null rect) as the reference
   rect for painter.boundingRect(), which can return invalid dimensions.
   Additionally, height was accumulated incorrectly: united() + setHeight()
   doubled the height at each iteration, causing an exponentially growing
   bounding rect with multiple contacts.
   Fix: use QRectF(0, 0, 500, 20) as reference rect and accumulate
   height and width independently.
2026-05-23 15:32:28 +02:00
Laurent Trinques dbda958261 terminaldata: add No, Nc, Common types for SW contacts
Extend TerminalData::Type enum with three new semantic values:
- No     : Normally Open terminal of a switch (SW) contact
- Nc     : Normally Closed terminal of a switch (SW) contact
- Common : Common terminal of a switch (SW) contact

Update typeToString() and typeFromString() accordingly.
Fully backward compatible: existing Generic/Inner/Outer types
are unchanged. Elements without typed terminals fall back
to the previous behavior (first 2 named terminals).

terminal: expose terminalType() as public accessor

Add Terminal::terminalType() returning the TerminalData::Type
of this terminal. This allows crossrefitem and other consumers
to filter terminals by semantic role (No, Nc, Common) without
accessing TerminalData internals directly.

terminaleditor: add No, Nc, Common entries to type combobox

Expose the three new TerminalData types (No, Nc, Common) in
the element editor UI so users can assign a semantic role to
each terminal of a SW contact element.

Also fix a pre-existing bug in updateForm() where m_type_cb
was incorrectly using m_orientation_cb->findData() instead
of m_type_cb->findData(), preventing the type from being
restored correctly when selecting a terminal.

terminaleditor: add No, Nc, Common entries to type combobox

Expose the three new TerminalData types (No, Nc, Common) in
the element editor UI so users can assign a semantic role to
each terminal of a SW contact element.

Also fix a pre-existing bug in updateForm() where m_type_cb
was incorrectly using m_orientation_cb->findData() instead
of m_type_cb->findData(), preventing the type from being
restored correctly when selecting a terminal.
2026-05-23 02:09:32 +02:00
Laurent Trinques d691489165 Update translations files 2026-05-22 10:16:24 +02:00
Laurent Trinques 202ea38e40 Merge pull request #464 from Kellermorph/makro-fix
New element: Line definition
2026-05-22 10:06:20 +02:00
Kellermorph 86dafcb576 Merge branch 'master' into makro-fix 2026-05-21 20:52:18 +02:00
Kellermorph f416c2a97e New element: Line definition 2026-05-21 20:47:44 +02:00
Laurent Trinques 7426fedba3 crossrefitem: display terminal names on contact symbols in Xref
When a slave element has named terminals in its element definition
(.elmt), the terminal names (e.g. 13/14 for NO, 11/12 for NC,
12/13/14 for SW) are now displayed on each side of the contact
symbol in the cross-reference view.

- NO/NC contacts: name[0] on the left, name[1] on the right
- SW contacts: name[0] (NO) top-left, name[1] (common) top-right,
  name[2] (NC) bottom-left

Terminal names are read from Terminal::name() which is populated
from TerminalData::m_name during element parsing. If terminals are
not named, nothing is displayed (fully backward compatible).

Users are expected to name their own terminals in the element
editor to avoid duplicating elements in the official collection.
2026-05-21 17:10:44 +02:00
Laurent Trinques 027050c7e7 Update windows-msi.yml 2026-05-21 14:32:07 +02:00
Laurent Trinques fc948ad963 QElectroTech.wxs — add ProductCode="$(var.ProductCode)" to <Package>
+ AllowSameVersionUpgrades="yes" to <MajorUpgrade>
windows-msi.yml — in the ‘Extract version’ step, calculate a unique GUID
based on the commit’s SHA, then pass -d ‘ProductCode=$productGuid’ to the WIX build
This ensures that each build will have a different ProductCode → MajorUpgrade will always be triggered
2026-05-21 14:10:17 +02:00
Laurent Trinques 24d075b64c msi: add The AllowSameVersionUpgrades="yes" setting forces uninstallation
even when the MSI version is identical (which is the case here, as 0.100.1.0
remains unchanged between two nightly builds).
2026-05-21 13:59:53 +02:00
Laurent Trinques f914f91e77 tests sign msi installer 2026-05-21 12:33:46 +02:00
Laurent Trinques fe03a0f643 Restore workflows to pre-test state 2026-05-21 12:29:22 +02:00
Laurent Trinques a7ad0278a6 Update windows-build.yml 2026-05-21 11:49:30 +02:00
Laurent Trinques f517489421 Update windows-build.yml 2026-05-21 11:15:25 +02:00
Laurent Trinques c8fa1c9fa4 test 2026-05-21 10:45:14 +02:00
Laurent Trinques d85aff0c0f Update CMakeLists.txt 2026-05-21 03:40:14 +02:00
Laurent Trinques 96b8e4b19c fix(msi): move Custom element conditions to Condition attribute (WIX0400)
WiX v7 requires conditions to be set via the Condition attribute,
not as inner text of the Custom element.
2026-05-21 03:00:49 +02:00
Laurent Trinques 9760288db6 fix(msi): correct CustomAction pattern for elements\ read-only
- Use SetProperty + WixQuietExec two-step pattern to pass runtime
  INSTALLDIR to a deferred CustomAction (fixes WIX1077 and WIX0400)
- Add WixToolset.Util.wixext/7.0.0 extension (required for WixQuietExec)
- Fix condition syntax: collapse multi-line conditions to single line
- Add -ext WixToolset.Util.wixext to wix build command in windows-msi.yml
2026-05-21 02:55:28 +02:00
Laurent Trinques eeaa059a77 This PR improves the MSI installer by removing the Lancer QET.bat wrapper and handling everything natively in QElectroTech.wxs.
**`build-aux/windows/QElectroTech.wxs`**
- Desktop and Start Menu shortcuts now point directly to `bin\qelectrotech.exe` with all required arguments (`--common-elements-dir`, `--common-tbt-dir`, `--lang-dir`, `-style windowsvista`) — no `.bat` wrapper needed
- Added a deferred `CustomAction` that runs after `InstallFiles` and recursively sets all files in `elements\` to read-only using an inline PowerShell command

**`.github/workflows/windows-msi.yml`**
- Replaced the step that created `Lancer QET.bat` with a step that removes it from the artifact before the WiX build, so it is not embedded in the MSI
- The `.bat` file remains untouched in the ZIP portable build (managed by `windows-build.yml`)

- No console window flashing when launching QElectroTech from the MSI shortcuts
- The `elements\` directory is properly set to read-only after installation, as required
- Cleaner MSI package — no `.bat` file shipped to end users installing via MSI
2026-05-21 02:34:38 +02:00
Laurent Trinques fa334d34a4 git submodule update --remote elements 2026-05-20 22:14:43 +02:00
joshua 2d4f968348 Fix crash when collapse root item of macro element tree view
Add an icon to the macro type of FileElementCollectionItem.

The first time the model (ElementsCollectionModel) ask for decoration
role the FileElementCollectionItem check if icon is null, if true the
icon is set, if false the function return.

In the case of the macro type, befor this commit the macro have a null
icon, and so the setIcon is call each time (many) the mouse move hover
and cause a qet crash.
This commit fix that, only by setUp an icon
2026-05-06 00:20:07 +02:00
joshua 6f669e1074 Merge branch 'master' into qt6_cmake_joshua 2026-05-05 20:11:05 +02:00
joshua 0b91318749 Remove several QT_VERSION_CHECK
Remove several QT_VERSION_CHECK related to Qt5 and Qt4.
2026-03-19 19:54:51 +01:00
joshua 1ba97c7e92 Remove hoto_update_cmake_message.cmake 2026-03-03 21:45:48 +01:00
joshua cd09fc0d32 Re-enable git_update_submodules.cmake
Re-enable git_update_submodules.cmake, use for update the elements repo.
Remove the unused include : fetch_elements.cmake
2026-03-03 21:44:35 +01:00
Laurent Trinques 924fe082fb Update fr_window_build_msys2.md 2026-03-03 06:56:24 +01:00
joshua ad37b0f9a5 Add documentation for build under Windows
The documentation is available in french only.
Contributors, feel free to create a new documentation in English (create
a new directory named 'en' for this purpose).
2026-03-02 22:47:32 +01:00
joshua fedc1cb092 Made available compilation on windows with msys2
The aim of this commit is to easily build qelectrotech under windows
with qt6/cmake and the package tool MSYS2.

-Update some cmake file.
-Remove Git submodule for pugixml and single application
-Use cmake find_package for pugi xml
-Use cmake fetchContent for single application.
-Use cmake find_package for KCoreAddons and KWidgetsAddons.
-Minor change for pugi xml
-Minor change on ProjectPrintWindow class to compatible with Qt6 API.
2026-03-02 22:34:16 +01:00
joshua 5f318e09c8 Add forgotten file and folder.
Add folder and file to cmake.
2026-01-30 19:43:07 +01:00
joshua 27afeaefe2 Upgrade pugixml version.
Upgrade pugixml to be compatible with cmake >= 4.0.0.

Compatibility with CMake < 3.5 has been removed from CMake since 4.0.0.
Pugixml V1.11.4 used cmake 3.4. Latest version V1.15 use cmake VERSION
3.5...3.30
2026-01-30 18:46:21 +01:00
joshua ab2f933fdf Re-enable multi-threading to load collection
The name of the elements and folders of the collection are not displayed
until we hover the item with the mouse.
This due that QtConcurent::run was disabled at loading of collection in
the goal of use QtConcurrent::run with Qt6.
Run is made to run a function once.
Map is made to run a fonction for each item of a sequence (what we need
in this case).
Remove code of run and re-enable code for map.
2026-01-28 19:47:04 +01:00
joshua 7f718f672f Fix : can't open recent file 2026-01-28 00:15:09 +01:00
joshua 9ec02bc088 Build with qt6 and cmake
First build with qt6 and cmake.
QET compil, but a lot of things don't work.
Build tested on debian sid and ubuntu 25.04.

Dependency needed under debian and ubuntu :
qtcreator cmake qt6-tools-dev qt6-svg-dev libsqlite3-dev
libkf6coreaddons-dev extra-cmake-modules libkf6widgetsaddons-dev
2026-01-27 23:31:34 +01:00
123 changed files with 13336 additions and 16093 deletions
+59 -56
View File
@@ -18,16 +18,19 @@ jobs:
build-msi:
name: Build MSI with WiX v7
runs-on: windows-latest
# Only runs if Windows Build succeeded (or triggered manually)
if: >
github.event_name == 'workflow_dispatch' ||
github.event.workflow_run.conclusion == 'success'
permissions:
contents: write
pages: write
id-token: write
steps:
# ----------------------------------------------------------------
# 1. Checkout (to retrieve QElectroTech.wxs and sources)
# ----------------------------------------------------------------
@@ -38,16 +41,12 @@ jobs:
# ----------------------------------------------------------------
# 2. Download the portable artifact from the main build
# Requires windows-build.yml to upload an artifact named
# "qelectrotech-windows-portable" (fixed name)
# ----------------------------------------------------------------
- name: Download portable artifact
uses: actions/download-artifact@v4
with:
name: qelectrotech-windows-portable
path: artifact\files
# workflow_run => use the triggering run's ID
# workflow_dispatch => use input run_id if provided, otherwise current run
run-id: ${{ github.event.workflow_run.id || github.event.inputs.run_id || github.run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }}
@@ -59,12 +58,10 @@ jobs:
id: version
shell: pwsh
run: |
# Version from qetversion.cpp (same logic as windows-build.yml)
$src = Get-Content "sources\qetversion.cpp" -Raw -ErrorAction SilentlyContinue
if ($src -match 'return QVersionNumber\{([^}]+)\}') {
$ver = $Matches[1] -replace '\s','' -replace ',','.'
} else {
# Fallback: CMakeLists.txt
$cmake = Get-Content "CMakeLists.txt" -Raw
if ($cmake -match 'project\s*\([^)]*VERSION\s+([\d]+\.[\d]+\.[\d]+)') {
$ver = $Matches[1]
@@ -72,43 +69,41 @@ jobs:
$ver = "0.0.0"
}
}
# Numeric MSI version: 4 digits required (e.g. 0.100.1.0)
$verMsi = "$ver.0"
# Short SHA for the display version
$sha = git rev-parse --short HEAD 2>$null
if (-not $sha) { $sha = "unknown" }
# Cumulative revision number (same calculation as windows-build.yml)
$count = git rev-list HEAD --count 2>$null
$rev = [int]$count + 473
$verDisplay = "${ver}-r${rev}-${sha}_x86_64-win64"
# Generate a unique ProductCode GUID from the commit SHA
# This ensures MajorUpgrade always triggers, even for same-version builds
$fullSha = git rev-parse HEAD 2>$null
if (-not $fullSha) { $fullSha = [System.Guid]::NewGuid().ToString() }
$bytes = [System.Text.Encoding]::UTF8.GetBytes($fullSha)
$md5 = [System.Security.Cryptography.MD5]::Create().ComputeHash($bytes)
$guidBytes = [byte[]]$md5[0..15]
$productGuid = [System.Guid]::new($guidBytes).ToString().ToUpper()
echo "VERSION_MSI=$verMsi" >> $env:GITHUB_OUTPUT
echo "VERSION_DISPLAY=$verDisplay" >> $env:GITHUB_OUTPUT
echo "PRODUCT_GUID=$productGuid" >> $env:GITHUB_OUTPUT
Write-Host "Version MSI : $verMsi"
Write-Host "Version display : $verDisplay"
Write-Host "Product GUID : $productGuid"
# ----------------------------------------------------------------
# 4. Install WiX v7, accept EULA and install WixUI extension
# All done in one step: PATH is updated within the same step
# so wix is immediately available for eula and extension commands
# ----------------------------------------------------------------
- name: Install WiX v7
shell: pwsh
run: |
dotnet tool install --global wix --version 7.0.0
# Update PATH immediately for the rest of this step
$toolsPath = [System.IO.Path]::Combine($env:USERPROFILE, '.dotnet', 'tools')
$env:PATH = "$toolsPath;$env:PATH"
# Also export for subsequent steps
echo $toolsPath >> $env:GITHUB_PATH
# Accept OSMF EULA (official CI/CD method: writes a sentinel file)
wix eula accept wix7
# Install WixUI extension
wix extension add WixToolset.UI.wixext/7.0.0
wix extension add WixToolset.Util.wixext/7.0.0
Write-Host "WiX v7 installed, EULA accepted, UI extension added."
# ----------------------------------------------------------------
@@ -120,8 +115,6 @@ jobs:
$wxs = "build-aux\windows\QElectroTech.wxs"
if (-not (Test-Path $wxs)) {
Write-Error "WXS file not found: $wxs"
Write-Host "Contents of build-aux\windows\ :"
Get-ChildItem "build-aux\windows\" -ErrorAction SilentlyContinue
exit 1
}
Write-Host "WXS found: $wxs"
@@ -136,10 +129,8 @@ jobs:
Get-ChildItem -Path "artifact\files" -Depth 2 |
Select-Object FullName | Format-Table -AutoSize
# Search for qelectrotech.exe in the artifact
$exe = Get-ChildItem -Path "artifact\files" -Filter "qelectrotech.exe" -Recurse | Select-Object -First 1
if (-not $exe) {
# Also try QElectroTech.exe (capital Q)
$exe = Get-ChildItem -Path "artifact\files" -Filter "QElectroTech.exe" -Recurse | Select-Object -First 1
}
if (-not $exe) {
@@ -147,69 +138,58 @@ jobs:
exit 1
}
Write-Host "Executable: $($exe.FullName) ($([math]::Round($exe.Length/1MB,1)) MB)"
# FilesDir = folder containing bin\
$binDir = $exe.Directory.FullName
$filesDir = Split-Path $binDir -Parent
echo "FILES_DIR=$filesDir" >> $env:GITHUB_ENV
Write-Host "FILES_DIR: $filesDir"
# ----------------------------------------------------------------
# 7. Convert LICENSE (GPL-2) to RTF for the WixUI licence screen
# RTF is the only format accepted by Windows Installer.
# The conversion wraps plain text lines in basic RTF markup.
# ----------------------------------------------------------------
- name: Convert LICENSE to RTF
shell: pwsh
run: |
$licSrc = "LICENSE"
$licRtf = "$env:TEMP\License.rtf"
if (-not (Test-Path $licSrc)) {
Write-Error "LICENSE file not found in repository root"
exit 1
}
$lines = Get-Content $licSrc -Encoding UTF8
# RTF header — Courier New, 9pt, black
$rtf = New-Object System.Text.StringBuilder
[void]$rtf.AppendLine('{\rtf1\ansi\ansicpg1252\deff0')
[void]$rtf.AppendLine('{\fonttbl{\f0\fmodern\fprq1\fcharset0 Courier New;}}')
[void]$rtf.AppendLine('{\colortbl;\red0\green0\blue0;}')
[void]$rtf.AppendLine('\f0\fs18\cf1')
foreach ($line in $lines) {
# Escape RTF special characters
$escaped = $line `
-replace '\\', '\\\\' `
-replace '\{', '\{' `
-replace '\}', '\}'
[void]$rtf.AppendLine("$escaped\par")
}
[void]$rtf.AppendLine('}')
[System.IO.File]::WriteAllText($licRtf, $rtf.ToString(), [System.Text.Encoding]::ASCII)
echo "LICENSE_RTF=$licRtf" >> $env:GITHUB_ENV
Write-Host "License.rtf generated: $licRtf ($([math]::Round((Get-Item $licRtf).Length/1KB,1)) KB)"
# ----------------------------------------------------------------
# 8. Replace Lancer QET.bat with the MSI-specific version
# The portable version uses relative paths suited for the zip.
# The MSI version uses %~dp0 to resolve paths relative to
# the installation directory in Program Files.
# 8. Remove Lancer QET.bat from the artifact
# The MSI does not use the .bat: shortcuts point directly to
# qelectrotech.exe, and elements\ is set read-only via a
# CustomAction in QElectroTech.wxs.
# The .bat is kept as-is in the ZIP portable build.
# ----------------------------------------------------------------
- name: Replace Lancer QET.bat for MSI
- name: Remove Lancer QET.bat from artifact
shell: pwsh
run: |
$bat = "$env:FILES_DIR\Lancer QET.bat"
$content = "@echo off`r`nstart `"`" `"%~dp0bin\qelectrotech.exe`" --common-elements-dir=`"%~dp0elements/`" --common-tbt-dir=`"%~dp0titleblocks/`" --lang-dir=`"%~dp0lang/`" -style windowsvista`r`n"
[System.IO.File]::WriteAllText($bat, $content, [System.Text.Encoding]::ASCII)
Write-Host "Lancer QET.bat replaced for MSI installation."
Write-Host "=== Content of new Lancer QET.bat ==="
Get-Content $bat
if (Test-Path $bat) {
Remove-Item $bat -Force
Write-Host "Lancer QET.bat removed from artifact (MSI uses direct exe shortcut)."
} else {
Write-Host "Lancer QET.bat not found in artifact (already absent)."
}
# ----------------------------------------------------------------
# 9. Build the MSI
@@ -233,26 +213,57 @@ jobs:
Write-Host " LicenseRtf : $licRtf"
Write-Host " Output : dist\$outputName"
$productGuid = "${{ steps.version.outputs.PRODUCT_GUID }}"
wix build $wxs `
-arch x64 `
-d "Version=$version" `
-d "ProductVersion=$verDisplay" `
-d "ProductCode=$productGuid" `
-d "FilesDir=$filesDir" `
-d "LicenseRtf=$licRtf" `
-ext WixToolset.UI.wixext `
-ext WixToolset.Util.wixext `
-o "dist\$outputName"
if (-not (Test-Path "dist\$outputName")) {
Write-Error "MSI not generated: dist\$outputName"
exit 1
}
$size = [math]::Round((Get-Item "dist\$outputName").Length / 1MB, 1)
Write-Host "MSI generated: dist\$outputName ($size MB) ✓"
echo "MSI_NAME=$outputName" >> $env:GITHUB_ENV
# ----------------------------------------------------------------
# 10. Upload the MSI artifact
# 10. Sign the MSI with SignPath
# ----------------------------------------------------------------
- name: Install SignPath PowerShell module
shell: pwsh
run: |
Install-Module -Name SignPath -Force -Scope CurrentUser
- name: Sign MSI with SignPath
shell: pwsh
run: |
$msi = Get-ChildItem "$env:GITHUB_WORKSPACE\dist\*.msi" | Select-Object -First 1
if (-not $msi) {
Write-Error "No .msi found in dist/"
exit 1
}
Write-Host "Signing: $($msi.FullName)"
Submit-SigningRequest `
-InputArtifactPath $msi.FullName `
-ApiToken "${{ secrets.SIGNPATH_API_TOKEN }}" `
-OrganizationId "${{ secrets.SIGNPATH_ORGANIZATION_ID }}" `
-ProjectSlug "MSI" `
-SigningPolicySlug "test-signing" `
-OutputArtifactPath $msi.FullName `
-Force `
-WaitForCompletion
Write-Host "Signing complete: $($msi.Name)"
# ----------------------------------------------------------------
# 11. Upload the MSI artifact
# ----------------------------------------------------------------
- name: Upload MSI artifact
uses: actions/upload-artifact@v4
@@ -308,7 +319,6 @@ jobs:
# ----------------------------------------------------------------
# 13. Generate and deploy the GitHub Pages download page
# Toutes les URLs sont connues ici (exe, zip, msi).
# ----------------------------------------------------------------
- name: Checkout (for generate-page.py)
uses: actions/checkout@v4
@@ -325,29 +335,22 @@ jobs:
run: |
set -euo pipefail
REPO="${{ github.repository }}"
# Fetch asset names from the nightly release (source of truth)
ASSETS=$(gh release view nightly --repo "$REPO" --json assets --jq '.assets[].name')
EXE_NAME=$(echo "$ASSETS" | grep '\.exe$' | head -1)
ZIP_NAME=$(echo "$ASSETS" | grep '\.zip$' | head -1)
MSI_NAME=$(echo "$ASSETS" | grep '\.msi$' | head -1 || echo "")
BASE="https://github.com/$REPO/releases/download/nightly"
INSTALLER_URL="$BASE/$EXE_NAME"
PORTABLE_URL="$BASE/$ZIP_NAME"
MSI_URL=""
[ -n "$MSI_NAME" ] && MSI_URL="$BASE/$MSI_NAME"
SHA="${{ github.event.workflow_run.head_sha || github.sha }}"
SHORT="${SHA:0:7}"
DATE=$(date -u '+%Y-%m-%d %H:%M UTC')
RUN_URL="https://github.com/$REPO/actions/runs/${{ github.run_id }}"
RUN_NUMBER="${{ github.run_number }}"
export DATE SHORT REPO SHA RUN_URL RUN_NUMBER
export INSTALLER_URL PORTABLE_URL MSI_URL
python3 source/build-aux/generate-page.py
- name: Add .nojekyll
+25 -35
View File
@@ -14,12 +14,10 @@
# You should have received a copy of the GNU General Public License
# along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
include(cmake/hoto_update_cmake_message.cmake)
cmake_minimum_required(VERSION 3.14...3.19 FATAL_ERROR)
cmake_minimum_required(VERSION 3.5...4.2)
project(qelectrotech
VERSION 0.9.0
VERSION 0.100.0
DESCRIPTION "QET is a CAD/CAE editor focusing on schematics drawing features."
HOMEPAGE_URL "https://qelectrotech.org/"
LANGUAGES CXX)
@@ -27,9 +25,16 @@ project(qelectrotech
include(cmake/copyright_message.cmake)
set(QET_DIR ${PROJECT_SOURCE_DIR})
include(cmake/qet_compilation_vars.cmake)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 REQUIRED COMPONENTS ${QET_COMPONENTS})
qt_standard_project_setup()
# Add sub directories
option(PACKAGE_TESTS "Build the tests" ON)
option(PACKAGE_TESTS "Build the tests" NO)
if(PACKAGE_TESTS)
message("Add sub directory tests")
add_subdirectory(tests)
@@ -43,42 +48,16 @@ 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)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
SET(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(
QT
NAMES
Qt5
COMPONENTS
${QET_COMPONENTS}
REQUIRED
)
find_package(
Qt${QT_VERSION_MAJOR}
COMPONENTS
${QET_COMPONENTS}
REQUIRED)
find_package(SQLite3 REQUIRED)
set(CMAKE_AUTOUIC_SEARCH_PATHS ${QET_DIR}/sources/ui)
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION "${QET_DIR}/lang")
qt5_add_translation(QM_FILES ${TS_FILES})
# als laatse
include(cmake/define_definitions.cmake)
add_executable(
qt_add_executable(
${PROJECT_NAME}
${QET_RES_FILES}
${QET_SRC_FILES}
@@ -86,6 +65,16 @@ add_executable(
${QET_DIR}/qelectrotech.qrc
)
if(QMFILES_AS_RESOURCE)
qt_add_translations(${PROJECT_NAME} TS_FILES ${TS_FILES} RESOURCE_PREFIX "/lang")
else()
qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION "${QET_DIR}/lang")
qt_add_translation(QM_FILES ${TS_FILES})
endif()
find_package(SQLite3 REQUIRED)
target_link_libraries(
${PROJECT_NAME}
PUBLIC
@@ -93,7 +82,7 @@ target_link_libraries(
pugixml::pugixml
SingleApplication::SingleApplication
SQLite::SQLite3
${KF5_PRIVATE_LIBRARIES}
${KF6_PRIVATE_LIBRARIES}
${QET_PRIVATE_LIBRARIES}
)
@@ -126,11 +115,11 @@ target_include_directories(
${QET_DIR}/sources/NameList
${QET_DIR}/sources/NameList/ui
${QET_DIR}/sources/utils
${QET_DIR}/pugixml/src
${QET_DIR}/sources/dataBase
${QET_DIR}/sources/dataBase/ui
${QET_DIR}/sources/factory/ui
${QET_DIR}/sources/print
${QET_DIR}/sources/svg
)
install(TARGETS ${PROJECT_NAME})
@@ -150,6 +139,7 @@ install(FILES LICENSE ELEMENTS.LICENSE CREDIT README ChangeLog DESTINATION share
install(FILES misc/org.qelectrotech.qelectrotech.desktop DESTINATION share/applications)
install(FILES misc/qelectrotech.xml DESTINATION share/mime/packages)
install(FILES misc/qelectrotech.appdata.xml DESTINATION ${QET_APPDATA_PATH})
if(NOT QMFILES_AS_RESOURCE)
install(FILES ${QM_FILES} DESTINATION ${QET_LANG_PATH})
endif()
endif()
+3
View File
@@ -59,6 +59,9 @@ Here are the technical choices made for the software development:
If you wish to be informed of the latest developments, browse the [archive](https://listengine.tuxfamily.org/lists.tuxfamily.org/qet/) of the project mailing list where all commits (changes) are registered. This archive is publicly available, you don't need any account to access it.
### Build QElectroTech under Windows
To build QElectroTech under microsoft Windows, please follow [these instructions (french)](md/fr/fr_window_build_summary.md)
# Features
+45 -2
View File
@@ -16,6 +16,7 @@
Name="QElectroTech"
Manufacturer="QElectroTech Team"
Version="$(var.Version)"
ProductCode="$(var.ProductCode)"
UpgradeCode="A1B2C3D4-E5F6-7890-ABCD-EF1234567890"
Language="1033"
Codepage="1252"
@@ -28,6 +29,7 @@
<!-- In-place upgrade: automatically uninstalls the previous version -->
<MajorUpgrade
DowngradeErrorMessage="A newer version of QElectroTech is already installed."
AllowSameVersionUpgrades="yes"
Schedule="afterInstallInitialize" />
<!-- Installation directory -->
@@ -37,6 +39,8 @@
<!-- ============================================================
All application files harvested in one pass from files\**
(Lancer QET.bat has been removed from the artifact before
this build — see windows-msi.yml step "Remove Lancer QET.bat")
============================================================ -->
<ComponentGroup Id="CG_AllFiles" Directory="INSTALLDIR">
<Files Include="$(var.FilesDir)\**" />
@@ -44,13 +48,17 @@
<!-- ============================================================
Desktop + Start Menu shortcuts
Point directly to qelectrotech.exe with all required arguments.
No .bat wrapper needed.
============================================================ -->
<ComponentGroup Id="CG_Shortcuts" Directory="INSTALLDIR">
<Component Id="C_ShortcutDesktop" Guid="F1A2B3C4-D5E6-7890-5678-012345678901">
<Shortcut Id="DesktopShortcut"
Directory="DesktopFolder"
Name="QElectroTech"
Target="[INSTALLDIR]Lancer QET.bat"
Target="[INSTALLDIR]bin\qelectrotech.exe"
Arguments="--common-elements-dir=&quot;[INSTALLDIR]elements/&quot; --common-tbt-dir=&quot;[INSTALLDIR]titleblocks/&quot; --lang-dir=&quot;[INSTALLDIR]lang/&quot; -style windowsvista"
Icon="qet.ico"
WorkingDirectory="INSTALLDIR" />
<RegistryValue Root="HKCU"
@@ -59,11 +67,13 @@
Type="integer" Value="1"
KeyPath="yes" />
</Component>
<Component Id="C_ShortcutStartMenu" Guid="A2B3C4D5-E6F7-8901-6789-123456789012">
<Shortcut Id="StartMenuShortcut"
Directory="ProgramMenuFolder"
Name="QElectroTech"
Target="[INSTALLDIR]Lancer QET.bat"
Target="[INSTALLDIR]bin\qelectrotech.exe"
Arguments="--common-elements-dir=&quot;[INSTALLDIR]elements/&quot; --common-tbt-dir=&quot;[INSTALLDIR]titleblocks/&quot; --lang-dir=&quot;[INSTALLDIR]lang/&quot; -style windowsvista"
Icon="qet.ico"
WorkingDirectory="INSTALLDIR" />
<RegistryValue Root="HKCU"
@@ -72,6 +82,7 @@
Type="integer" Value="1"
KeyPath="yes" />
</Component>
</ComponentGroup>
<!-- ============================================================
@@ -93,6 +104,38 @@
============================================================ -->
<Icon Id="qet.ico" SourceFile="$(var.FilesDir)\ico\qelectrotech.ico" />
<!-- ============================================================
CustomAction: set elements\ subtree read-only after install
Pattern used: two-step SetProperty + Exec
1. CA_ResolveElementsPath (immediate, SetProperty):
copies the runtime value of INSTALLDIR into the property
ELEMENTS_READONLY_CMD, building the full powershell command.
2. CA_SetElementsReadOnly (deferred, Execute="deferred"):
runs the command stored in ELEMENTS_READONLY_CMD.
This is the correct WiX v4/v7 pattern for passing a
directory path (resolved at runtime) to a deferred CA.
============================================================ -->
<!-- Step 1: build the powershell command with the resolved INSTALLDIR -->
<CustomAction Id="CA_ResolveElementsPath"
Property="CA_SetElementsReadOnly"
Value="powershell.exe -NonInteractive -NoProfile -WindowStyle Hidden -Command &quot;Get-ChildItem -LiteralPath '[INSTALLDIR]elements' -Recurse -File | ForEach-Object { `$_.IsReadOnly = `$true }&quot;" />
<!-- Step 2: deferred execution of the powershell command -->
<CustomAction Id="CA_SetElementsReadOnly"
BinaryRef="Wix4UtilCA_X86"
DllEntry="WixQuietExec"
Execute="deferred"
Impersonate="no"
Return="ignore" />
<InstallExecuteSequence>
<Custom Action="CA_ResolveElementsPath" After="InstallFiles" Condition="NOT Installed AND NOT REMOVE" />
<Custom Action="CA_SetElementsReadOnly" After="CA_ResolveElementsPath" Condition="NOT Installed AND NOT REMOVE" />
</InstallExecuteSequence>
<!-- ============================================================
Main feature (everything included)
============================================================ -->
+10 -5
View File
@@ -62,10 +62,15 @@ 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})
else()
add_definitions(-DBUILD_WITHOUT_KF5)
if(BUILD_WITH_KF6 AND BUILD_KF6)
message("KF6_GIT_TAG :" ${KF6_GIT_TAG})
endif()
if(NOT BUILD_WITH_KF6)
add_definitions(-DBUILD_WITHOUT_KF6)
endif()
message("QET_COMPONENTS :" ${QET_COMPONENTS})
message("QT_VERSION_MAJOR :" ${QT_VERSION_MAJOR})
message("Qt version :" ${Qt6_VERSION})
if(QMFILES_AS_RESOURCE)
add_definitions(-DQMFILES_AS_RESOURCE)
endif()
+5 -2
View File
@@ -31,5 +31,8 @@ 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 KF6
option(BUILD_WITH_KF6 "Build with KF6" ON)
# Use translations as a Qt resource
option(QMFILES_AS_RESOURCE "Use .qm files as Qt resource" ON)
+22 -34
View File
@@ -1,4 +1,4 @@
# Copyright 2006 The QElectroTech Team
# Copyright 2006-2026 The QElectroTech Team
# This file is part of QElectroTech.
#
# QElectroTech is free software: you can redistribute it and/or modify
@@ -14,54 +14,42 @@
# You should have received a copy of the GNU General Public License
# along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
message(" - fetch_kdeaddons")
option(BUILD_KF6 "Build KF6 libraries, use system ones otherwise" OFF)
if(BUILD_KF6)
block(PROPAGATE KF6_GIT_TAG)
message(STATUS " - fetch_kdeaddons")
set(KDE_SKIP_TEST_SETTINGS ON)
set(KCOREADDONS_USE_QML OFF)
set(KWIDGETSADDONS_USE_QML OFF)
set(BUILD_TESTING OFF)
set(BUILD_DESIGNERPLUGIN OFF)
set(BUILD_QCH OFF)
set(BUILD_SHARED_LIBS OFF)
if(DEFINED BUILD_WITH_KF5)
Include(FetchContent)
option(BUILD_KF5 "Build KF5 libraries, use system ones otherwise" YES)
if(BUILD_KF5)
if(NOT DEFINED KF5_GIT_TAG)
#https://qelectrotech.org/forum/viewtopic.php?pid=13924#p13924
set(KF5_GIT_TAG v5.77.0)
if(NOT DEFINED KF6_GIT_TAG)
set(KF6_GIT_TAG v6.22.0)
endif()
# 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(
kcoreaddons
GIT_REPOSITORY https://invent.kde.org/frameworks/kcoreaddons.git
GIT_TAG ${KF5_GIT_TAG})
GIT_TAG ${KF6_GIT_TAG})
FetchContent_MakeAvailable(kcoreaddons)
FetchContent_Declare(
kwidgetsaddons
GIT_REPOSITORY https://invent.kde.org/frameworks/kwidgetsaddons.git
GIT_TAG ${KF5_GIT_TAG})
GIT_TAG ${KF6_GIT_TAG})
FetchContent_MakeAvailable(kwidgetsaddons)
endblock()
else()
find_package(KF5CoreAddons REQUIRED)
find_package(KF5WidgetsAddons REQUIRED)
find_package(KF6CoreAddons REQUIRED)
find_package(KF6WidgetsAddons REQUIRED)
endif()
set(KF5_PRIVATE_LIBRARIES
KF5::WidgetsAddons
KF5::CoreAddons
set(KF6_PRIVATE_LIBRARIES
KF6::CoreAddons
KF6::WidgetsAddons
)
endif()
+5 -9
View File
@@ -1,4 +1,4 @@
# Copyright 2006 The QElectroTech Team
# Copyright 2006-2026 The QElectroTech Team
# This file is part of QElectroTech.
#
# QElectroTech is free software: you can redistribute it and/or modify
@@ -14,18 +14,14 @@
# You should have received a copy of the GNU General Public License
# along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
message(" - fetch_pugixml")
Include(FetchContent)
option(BUILD_PUGIXML "Build pugixml library, use system one otherwise" YES)
option(BUILD_PUGIXML "Build pugixml library, use system one otherwise" OFF)
if(BUILD_PUGIXML)
Include(FetchContent)
message(" - fetch pugixml")
FetchContent_Declare(
pugixml
GIT_REPOSITORY https://github.com/zeux/pugixml.git
GIT_TAG v1.11.4)
GIT_TAG v1.15)
FetchContent_MakeAvailable(pugixml)
else()
+2 -5
View File
@@ -1,4 +1,4 @@
# Copyright 2006 The QElectroTech Team
# Copyright 2006-2026 The QElectroTech Team
# This file is part of QElectroTech.
#
# QElectroTech is free software: you can redistribute it and/or modify
@@ -16,9 +16,6 @@
message(" - fetch_singleapplication")
# https://github.com/itay-grudev/SingleApplication/issues/18
#qmake
#DEFINES += QAPPLICATION_CLASS=QGuiApplication
set(QAPPLICATION_CLASS QApplication)
Include(FetchContent)
@@ -26,6 +23,6 @@ Include(FetchContent)
FetchContent_Declare(
SingleApplication
GIT_REPOSITORY https://github.com/itay-grudev/SingleApplication.git
GIT_TAG v3.2.0)
GIT_TAG v3.5.4)
FetchContent_MakeAvailable(SingleApplication)
-25
View File
@@ -1,25 +0,0 @@
# Copyright 2006 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/>.
if(${CMAKE_VERSION} VERSION_LESS 3.14)
message(
"_____________________________________________________________________")
message("to update Cmake on linux:")
message("https://github.com/Kitware/CMake/")
message("linux => cmake-3.19.1-Linux-x86_64.sh")
message(" sudo ./cmake.sh --prefix=/usr/local/ --exclude-subdir")
message("windows good luck :)")
endif()
+13 -6
View File
@@ -17,6 +17,8 @@
message(" - qet_compilation_vars")
set(QET_COMPONENTS
Core
Gui
LinguistTools
PrintSupport
Xml
@@ -110,6 +112,12 @@ set(QET_SRC_FILES
${QET_DIR}/sources/borderproperties.h
${QET_DIR}/sources/bordertitleblock.cpp
${QET_DIR}/sources/bordertitleblock.h
# ${QET_DIR}/sources/colorbutton.cpp
# ${QET_DIR}/sources/colorbutton.h
# ${QET_DIR}/sources/colorcombobox.cpp
# ${QET_DIR}/sources/colorcombobox.h
# ${QET_DIR}/sources/colorcomboboxdelegate.cpp
# ${QET_DIR}/sources/colorcomboboxdelegate.h
${QET_DIR}/sources/conductorautonumerotation.cpp
${QET_DIR}/sources/conductorautonumerotation.h
${QET_DIR}/sources/conductornumexport.cpp
@@ -418,10 +426,6 @@ set(QET_SRC_FILES
${QET_DIR}/sources/PropertiesEditor/propertieseditorwidget.cpp
${QET_DIR}/sources/PropertiesEditor/propertieseditorwidget.h
${QET_DIR}/pugixml/src/pugiconfig.hpp
${QET_DIR}/pugixml/src/pugixml.cpp
${QET_DIR}/pugixml/src/pugixml.hpp
${QET_DIR}/sources/qetgraphicsitem/conductor.cpp
${QET_DIR}/sources/qetgraphicsitem/conductor.h
${QET_DIR}/sources/qetgraphicsitem/conductortextitem.cpp
@@ -500,6 +504,9 @@ set(QET_SRC_FILES
${QET_DIR}/sources/SearchAndReplace/ui/searchandreplacewidget.cpp
${QET_DIR}/sources/SearchAndReplace/ui/searchandreplacewidget.h
${QET_DIR}/sources/svg/qetsvg.cpp
${QET_DIR}/sources/svg/qetsvg.h
${QET_DIR}/sources/titleblock/dimension.cpp
${QET_DIR}/sources/titleblock/dimension.h
${QET_DIR}/sources/titleblock/dimensionwidget.cpp
@@ -713,6 +720,8 @@ set(QET_SRC_FILES
${QET_DIR}/sources/xml/terminalstripitemxml.cpp
${QET_DIR}/sources/xml/terminalstripitemxml.h
${QET_DIR}/sources/xml/terminalstriplayoutpatternxml.cpp
${QET_DIR}/sources/xml/terminalstriplayoutpatternxml.h
)
set(TS_FILES
@@ -739,13 +748,11 @@ set(TS_FILES
${QET_DIR}/lang/qet_pt.ts
${QET_DIR}/lang/qet_pt_BR.ts
${QET_DIR}/lang/qet_ro.ts
${QET_DIR}/lang/qet_rs.ts
${QET_DIR}/lang/qet_ru.ts
${QET_DIR}/lang/qet_sk.ts
${QET_DIR}/lang/qet_sl.ts
${QET_DIR}/lang/qet_sr.ts
${QET_DIR}/lang/qet_sv.ts
${QET_DIR}/lang/qet_tr.ts
${QET_DIR}/lang/qet_uk.ts
${QET_DIR}/lang/qet_zh.ts
)
+405 -354
View File
File diff suppressed because it is too large Load Diff
+405 -354
View File
File diff suppressed because it is too large Load Diff
+405 -354
View File
File diff suppressed because it is too large Load Diff
+405 -355
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+401 -350
View File
File diff suppressed because it is too large Load Diff
+405 -354
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+405 -354
View File
File diff suppressed because it is too large Load Diff
+405 -354
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+400 -349
View File
File diff suppressed because it is too large Load Diff
+400 -349
View File
File diff suppressed because it is too large Load Diff
+401 -350
View File
File diff suppressed because it is too large Load Diff
+406 -355
View File
File diff suppressed because it is too large Load Diff
+401 -350
View File
File diff suppressed because it is too large Load Diff
+405 -354
View File
File diff suppressed because it is too large Load Diff
+400 -349
View File
File diff suppressed because it is too large Load Diff
+401 -350
View File
File diff suppressed because it is too large Load Diff
+405 -354
View File
File diff suppressed because it is too large Load Diff
+400 -349
View File
File diff suppressed because it is too large Load Diff
+405 -354
View File
File diff suppressed because it is too large Load Diff
+405 -354
View File
File diff suppressed because it is too large Load Diff
+401 -350
View File
File diff suppressed because it is too large Load Diff
+405 -354
View File
File diff suppressed because it is too large Load Diff
+400 -349
View File
File diff suppressed because it is too large Load Diff
+405 -354
View File
File diff suppressed because it is too large Load Diff
+400 -349
View File
File diff suppressed because it is too large Load Diff
+400 -349
View File
File diff suppressed because it is too large Load Diff
+400 -349
View File
File diff suppressed because it is too large Load Diff
+401 -350
View File
File diff suppressed because it is too large Load Diff
+182 -4761
View File
File diff suppressed because it is too large Load Diff
+401 -350
View File
File diff suppressed because it is too large Load Diff
+401 -350
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

+101
View File
@@ -0,0 +1,101 @@
Compiler QElectroTech sous microsoft Windows 10 et 11 avec MSYS2
================================
Ce document décrit les étapes nécessaire afin de compilé QElectroTech sous Windows avec Qt6 et cmake en utilisant MSYS2.
# MSYS2
L'ensemble des outils nécessaire au développement et à la compilation de QElectroTech sous Windows sera installé par lintermédiaire de [MSYS2](https://www.msys2.org/). Cela comprend entre autre le framework [Qt6](https://www.qt.io/development/qt-framework/qt6), les outils cmake, les dépendances ([kde framework](https://develop.kde.org/docs/), [sqlite](https://sqlite.org/), [pugixml](https://pugixml.org/)), les outils de compilation [minGW](https://www.mingw-w64.org/)...
>Il sera nécessaire d'utiliser [winget](https://learn.microsoft.com/fr-fr/windows/package-manager/winget/), celui-ci est présent par défaut sous Windows 11, dans le cas de Windows 10, winget peut necessité d'être activé manuellement
# Installer GIT et MSYS2 avec winget
Avec power shell.
```
winget install Git.Git
```
puis
```
winget install MSYS2.MSYS2
```
## Mise à jour de MSYS2
Lors de la première utilisation de MSYS2 il est nécessaire de mettre celui-ci à jour.
Lancer "MSYS2 MSYS" depuis le menu démarré de Windows.
Une fenêtre avec un shell s'ouvre, dans celui-ci lancer la commande :
```
pacman -Syu
```
A la fin de la mise à jour MSYS2 MSYS se fermera automatiquement. Ouvrez le à nouveau et relancé la commande
```
pacman -Syu
```
## Installation des outils de devellopement
Toujours dans le shell MSYS2 MSYS lancer la commande suivante.
```
pacman -S mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-qt6-svg mingw-w64-ucrt-x86_64-qt6-base mingw-w64-ucrt-x86_64-sqlite3 mingw-w64-ucrt-x86_64-pugixml mingw-w64-ucrt-x86_64-kcoreaddons mingw-w64-ucrt-x86_64-kwidgetsaddons mingw-w64-ucrt-x86_64-extra-cmake-modules mingw-w64-ucrt-x86_64-gdb mingw-w64-ucrt-x86_64-qt6-translations mingw-w64-ucrt-x86_64-qt6-tools
```
> La quantité de paquets à installer est conséquent, en fonction de votre connexion internet cela peut prendre plusieurs dizaine de minute
L'ensemble des outils est mantenant installé 😀
# Installer Qt creator
Télécharger [l'installateur online de Qt](https://www.qt.io/development/download-qt-installer-oss) et lancer l'installation en suivant les indications de ce dernier.
>Dans le cas où vous comptez utilisé Qt Creator uniquement pour développez QElectroTech, lors de l'installation choisissez l'option "installation personnalisée" puis dans la page suivante sélectionné uniquement Qt Creator.
## Configurer Qt creator
Ouvrir Qt creator puis rendez vous dans "édition -> préférence -> kit"
### Versions de Qt
- Cliquer sur _ajouter_
- Renseigner _Chemin de qmake_ (exemple C:\\msys64\\ucrt64\\bin\\qmake.exe).
- Dans le champ _Nom :_ ajouter (msys2).
![](assets/windows_msys2_setup/qt_version.png)
### Compilateurs
- Cliquer sur _ajouter_ puis choisir _MinGW_.
- Renseigner _Emplacement du compilateur C_ (exemple C:\\msys64\\ucrt64\\bin\\g++.exe).
- Dans le champ _Nom :_ ajouter (msys2).
![](assets/windows_msys2_setup/compiler.png)
### Débogueurs
- Cliquer sur _ajouter_
- Renseigner _Chemin :_ (exemple C:\\msys64\\ucrt64\\bin\\gdb.exe).
- Dans le champ _Nom :_ ajouter (msys2).
![](assets/windows_msys2_setup/debugger.png)
### cmake
- Outils -> _Ajouter_
- Renseigner _Chemin :_ (exemple C:\\msys64\\ucrt64\\bin\\cmake.exe).
- Dans le champ _Nom :_ ajouter (msys2).
![](assets/windows_msys2_setup/cmake.png)
### KIT
Maintenant que tous les prérequis sont fait nous allons crée un kit utilisant les outils fournis par MSYS2. Cliquer sur _Ajouter_, un nouveau kit _manuel_ apparaît, nommer celui-ci par exemple _Qt6 msys2_ puis renseigner le compilateur, le débogueur, la version de Qt et Outils CMake en choisissant à chaque fois ceux que nous venons de créer.
puis cliquer sur _appliquer_.
![](assets/windows_msys2_setup/kit.png)
Bravo 🥳🥳 vous avez terminé l'installation de la totalité des outils de développement.
# Clonez le dépôts de QElectrotech
Clonez le dépôt de QElectroTech comme vous le faite habituellement, sinon utilisez les commandes suivante dans power shell.
Crée et/ou se rendre dans le dossier dans lequel vous voulez clonez le dépôt (dans l'exemple nous allons crée un dossier QElectroTech dans C:)
```
mkdir C:\QElectroTech
cd C:\QElectroTech
git clone --recursive https://github.com/qelectrotech/qelectrotech-source-mirror.git
```
Une fois le dépôt cloné lancer Qt creator puis choisir d'ouvrir un projet existant, en choisissant le _CMakeLists.txt_ se trouvant à la racine du projet QElectroTech, enfin dans l'assistant de création de projet choisir comme kit le kit que nous avons créer précédemment.
+13
View File
@@ -0,0 +1,13 @@
Compiler QElectroTech sous microsoft Windows 10 et 11
================================
Compiler QElectroTech pour et/ou sous Windows peut être effectué avec plusieurs méthode différente.
Ce document énumère uniquement les différentes méthode possible
N'est mentionné que les étapes nécessaire afin de compilé QElectroTech sous Windows avec Qt6 et cmake. Ce document ne traite pas la compilation avec Qt5 et qmake.
>QElectroTech 0.100 est la dernière version à utiliser Qt5. Les version suivante sont développé avec Qt6 et utilise cmake au lieu de qmake.
Il existe deux méthodes pour cela :
1. [Utiliser msys2 (méthode recommandé)](fr_window_build_msys2.md)
2. Télécharger et compiler l'ensemble des dépendances (non rédigé)
Submodule pugixml deleted from 5a1892b321
@@ -26,12 +26,7 @@
#include "xmlprojectelementcollectionitem.h"
#include <QFutureWatcher>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
#include <QtConcurrentMap>
#else
#include <QtConcurrentRun>
#endif
/**
@brief ElementsCollectionModel::ElementsCollectionModel
Constructor
@@ -295,13 +290,13 @@ void ElementsCollectionModel::loadCollections(bool common_collection,
this, &ElementsCollectionModel::loadingProgressRangeChanged);
connect(watcher, &QFutureWatcher<void>::finished,
this, &ElementsCollectionModel::loadingFinished);
connect(watcher, &QFutureWatcher<void>::finished, watcher, &QFutureWatcher<void>::deleteLater);
connect(
watcher,
&QFutureWatcher<void>::finished,
watcher,
&QFutureWatcher<void>::deleteLater);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
m_future = QtConcurrent::map(m_items_list_to_setUp, setUpData);
#else
qDebug() << "Help code for QT 6 or later";
#endif
watcher->setFuture(m_future);
}
@@ -835,14 +835,8 @@ 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()
@@ -803,13 +803,13 @@ bool ElementsLocation::setXml(const QDomDocument &xml_document) const
QString path_ = collectionPath(false);
QRegularExpression rx("^(.*)/(.*\\.elmt)$");
if (rx.exactMatch(path_))
if (auto regex_match = rx.match(path_); regex_match.hasMatch())
{
return project()
->embeddedElementCollection()
->addElementDefinition(
rx.cap(1),
rx.cap(2),
regex_match.captured(1),
regex_match.captured(2),
xml_document.documentElement());
}
else
@@ -20,7 +20,7 @@
#include "../NameList/nameslist.h"
#include "../diagramcontext.h"
#include "pugixml/src/pugixml.hpp"
#include "pugixml.hpp"
#include <QIcon>
#include <QString>
@@ -87,11 +87,7 @@ void ElementsTreeView::startElementDrag(const ElementsLocation &location)
{
if (! location.exist()) return;
#if QT_VERSION < QT_VERSION_CHECK(6, 2, 0)
QDrag* drag = new QDrag(this);
#else
QScopedPointer<QDrag> drag(new QDrag(this));
#endif
auto drag = new QDrag{this};
QString location_str = location.toString();
QMimeData *mime_data = new QMimeData();
@@ -361,7 +361,7 @@ void FileElementCollectionItem::setUpIcon()
setIcon(QET::Icons::Folder);
} else {
if (m_path.endsWith(".qetmak")) {
setIcon(QIcon());
setIcon(QET::Icons::PartRectangle);
} else {
ElementsLocation loc(collectionPath());
setIcon(loc.icon());
+1 -1
View File
@@ -17,7 +17,7 @@
*/
#ifndef NAMES_LIST_H
#define NAMES_LIST_H
#include "pugixml/src/pugixml.hpp"
#include "pugixml.hpp"
#include <QtXml>
/**
@@ -18,6 +18,7 @@
#include "terminalstripdrawer.h"
#include <QPainter>
#include <QHash>
namespace TerminalStripDrawer {
+1 -6
View File
@@ -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(level, 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
m_real_terminal.swapItemsAt(i, std::min(static_cast<qsizetype>(level), m_real_terminal.size()-1));
return true;
}
return false;
+1 -4
View File
@@ -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")),
@@ -35,11 +35,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 +89,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_);
-4
View File
@@ -55,11 +55,7 @@ BorderTitleBlock::BorderTitleBlock(QObject *parent) :
m_titleblock_template_renderer = new TitleBlockTemplateRenderer(this);
m_titleblock_template_renderer -> setTitleBlockTemplate(QETApp::defaultTitleBlockTemplate());
// disable the QPicture-based cache from Qt 4.8 to avoid rendering errors and crashes
#if QT_VERSION < QT_VERSION_CHECK(4, 8, 0) // ### Qt 6: remove
#else
m_titleblock_template_renderer -> setUseCache(false);
#endif
// dimensions par defaut du schema
importBorder(BorderProperties());
-7
View File
@@ -72,14 +72,7 @@ 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;
-7
View File
@@ -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())
+1 -7
View File
@@ -47,14 +47,8 @@ ElementQueryWidget::ElementQueryWidget(QWidget *parent) :
m_button_group.addButton(ui->m_coil_cb, 4);
m_button_group.addButton(ui->m_protection_cb, 5);
m_button_group.addButton(ui->m_thumbnail_cb, 6);
#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)
-9
View File
@@ -1514,14 +1514,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());
@@ -1532,7 +1524,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);
}
+1 -1
View File
@@ -17,7 +17,7 @@
*/
#ifndef DIAGRAM_CONTEXT_H
#define DIAGRAM_CONTEXT_H
#include "pugixml/src/pugixml.hpp"
#include "pugixml.hpp"
#include <QDomElement>
#include <QHash>
+2 -47
View File
@@ -210,17 +210,10 @@ void DiagramView::handleElementDrop(QDropEvent *event)
return;
}
QPointF drop_pos;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
drop_pos = mapToScene(event->pos());
#else
drop_pos = event->position();
#endif
if (location.path().endsWith(".qetmak")) {
diagram()->setEventInterface(new DiagramEventAddMacro(location, diagram(), drop_pos));
diagram()->setEventInterface(new DiagramEventAddMacro(location, diagram(), event->position()));
} else {
diagram()->setEventInterface(new DiagramEventAddElement(location, diagram(), drop_pos));
diagram()->setEventInterface(new DiagramEventAddElement(location, diagram(), event->position()));
}
//Set focus to the view to get event
@@ -290,17 +283,8 @@ void DiagramView::handleTextDrop(QDropEvent *e) {
iti -> setHtml (e -> mimeData() -> text());
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
m_diagram->undoStack().push(new AddGraphicsObjectCommand(
iti, m_diagram, mapToScene(e->pos())));
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
m_diagram->undoStack().push(new AddGraphicsObjectCommand(
iti, m_diagram, e->position()));
#endif
}
/**
@@ -458,14 +442,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 +492,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 +553,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);
}
@@ -624,14 +587,7 @@ void DiagramView::mouseReleaseEvent(QMouseEvent *e)
QMenu *menu = new QMenu(this);
menu->addAction(act);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
menu->popup(e->globalPos());
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
menu->popup(e->pos());
#endif
}
m_free_rubberbanding = false;
@@ -1355,7 +1311,6 @@ void DiagramView::createTemplateFromSelection()
QFile file(full_path);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file);
out.setCodec("UTF-8");
out << macro_doc.toString(4);
file.close();
qDebug() << "Template successfully saved to:" << full_path;
-14
View File
@@ -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
}
}
}
-21
View File
@@ -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();
@@ -519,14 +519,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*$");
+3 -2
View File
@@ -220,7 +220,7 @@ void DynamicTextFieldEditor::fillInfoComboBox()
QStringList strl;
auto type = elementEditor()->elementScene()->elementData().m_type;
if(type & ElementData::AllReport) {
if((type & ElementData::AllReport) || (type == ElementData::ConductorDefinition)) {
strl = QETInformation::folioReportInfoKeys();
}
else {
@@ -365,7 +365,8 @@ void DynamicTextFieldEditor::on_m_text_from_cb_activated(int index) {
void DynamicTextFieldEditor::on_m_composite_text_pb_clicked()
{
bool isReport = false;
if (elementEditor()->elementScene()->elementData().m_type & ElementData::AllReport) {
auto type = elementEditor()->elementScene()->elementData().m_type;
if ((type & ElementData::AllReport) || (type == ElementData::ConductorDefinition)) {
isReport = true;
}
@@ -133,6 +133,7 @@ void ElementPropertiesEditorWidget::setUpInterface()
ui->m_base_type_cb->addItem (tr("Renvoi de folio précédent"), ElementData::PreviousReport);
ui->m_base_type_cb->addItem (tr("Bornier"), ElementData::Terminal);
ui->m_base_type_cb->addItem (tr("Vignette"), ElementData::Thumbnail);
ui->m_base_type_cb->addItem (tr("Définition de conducteur"), ElementData::ConductorDefinition);
// Slave option
ui->m_state_cb->addItem(tr("Normalement ouvert"), ElementData::NO);
@@ -188,6 +189,9 @@ void ElementPropertiesEditorWidget::updateTree()
case ElementData::PreviousReport:
ui->m_tree->setDisabled(true);
break;
case ElementData::ConductorDefinition:
ui->m_tree->setDisabled(true);
break;
case ElementData::Master:
ui->m_tree->setEnabled(true);
break;
@@ -287,11 +291,9 @@ void ElementPropertiesEditorWidget::on_m_base_type_cb_currentIndexChanged(int in
ui->m_master_gb->setVisible(master);
ui->m_terminal_gb->setVisible(terminal);
#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
ui->tabWidget->setTabVisible(1,
(type_ == ElementData::Simple ||
type_ == ElementData::Master));
#endif
updateTree();
}
+24 -2
View File
@@ -737,9 +737,10 @@ bool QETElementEditor::checkElement()
QList<QETWarning> errors;
// Warning #1: Element haven't got terminal
// (except for report, because report must have one terminal and this checking is do below)
// (except for report and conductor definition, because they must have one terminal and this checking is done below)
if (!m_elmt_scene -> containsTerminals() &&
!(m_elmt_scene->elementData().m_type & ElementData::AllReport)) {
!(m_elmt_scene->elementData().m_type & ElementData::AllReport) &&
m_elmt_scene->elementData().m_type != ElementData::ConductorDefinition) {
warnings << qMakePair(
tr("Absence de borne", "warning title"),
tr(
@@ -771,6 +772,27 @@ bool QETElementEditor::checkElement()
}
}
// Check conductor definition element
if (m_elmt_scene->elementData().m_type == ElementData::ConductorDefinition)
{
int terminal =0;
for(auto qgi : m_elmt_scene -> items()) {
if (qgraphicsitem_cast<PartTerminal *>(qgi)) {
terminal ++;
}
}
// Error: Conductor definition must have exactly one terminal
if (terminal != 1) {
errors << qMakePair (tr("Nombre de bornes incorrect"),
tr("<br><b>Erreur</b> :"
"<br>Les définitions de conducteur ne peuvent posséder qu'une seule borne."
"<br><b>Solution</b> :"
"<br>Vérifier que l'élément ne possède qu'une seule borne"));
}
}
if (!errors.count() && !warnings.count()) {
return(true);
}
+4 -1
View File
@@ -61,7 +61,7 @@ void TerminalEditor::updateForm()
ui->m_y_dsb->setValue(m_part->property("y").toReal());
ui->m_orientation_cb->setCurrentIndex(ui->m_orientation_cb->findData(m_part->property("orientation")));
ui->m_name_le->setText(m_part->terminalName());
ui->m_type_cb->setCurrentIndex(ui->m_orientation_cb->findData(m_part->terminalType()));
ui->m_type_cb->setCurrentIndex(ui->m_type_cb->findData(m_part->terminalType()));
activeConnections(true);
}
@@ -122,6 +122,9 @@ void TerminalEditor::init()
ui->m_type_cb->addItem(tr("Générique"), TerminalData::Generic);
ui->m_type_cb->addItem(tr("Bornier intérieur"), TerminalData::Inner);
ui->m_type_cb->addItem(tr("Bornier extérieur"), TerminalData::Outer);
ui->m_type_cb->addItem(tr("NO (contact SW)"), TerminalData::No);
ui->m_type_cb->addItem(tr("NC (contact SW)"), TerminalData::Nc);
ui->m_type_cb->addItem(tr("Commun (contact SW)"), TerminalData::Common);
}
/**
+1
View File
@@ -335,6 +335,7 @@ void TextEditor::setUpWidget(QWidget *parent)
m_size_sb = new QSpinBox(parent);
m_size_sb->setObjectName(QString::fromUtf8("m_size_sb"));
m_size_sb->setMinimum(4);
gridLayout->addWidget(m_size_sb, 2, 1, 1, 1);
@@ -567,14 +567,7 @@ void ElementPictureFactory::setPainterStyle(const QDomElement &dom, QPainter &pa
pen.setCapStyle(Qt::SquareCap);
//Get the couples style/value
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) // ### Qt 6: remove
const QStringList styles = dom.attribute("style").split(";", QString::SkipEmptyParts);
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 5.14 or later")
#endif
const QStringList styles = dom.attribute("style").split(";", Qt::SkipEmptyParts);
#endif
QRegularExpression rx("^(?<name>[a-z-]+):(?<value>[a-zA-Z-]+)$");
if (!rx.isValid())
+3 -9
View File
@@ -188,11 +188,9 @@ void MachineInfo::send_info_to_debug()
QDirIterator it1(QETApp::commonElementsDir().toLatin1(),nameFilters, QDir::Files, QDirIterator::Subdirectories);
while (it1.hasNext())
{
if(it1.next() > 0 )
{
it1.next();
commomElementsDir ++;
}
}
qInfo()<< " Common Elements count:"<< commomElementsDir << "Elements";
@@ -200,22 +198,18 @@ void MachineInfo::send_info_to_debug()
QDirIterator it2(QETApp::customElementsDir().toLatin1(), nameFilters, QDir::Files, QDirIterator::Subdirectories);
while (it2.hasNext())
{
if(it2.next() > 0 )
{
it2.next();
customElementsDir ++;
}
}
qInfo()<< " Custom Elements count:"<< customElementsDir << "Elements";
int companyElementsDir = 0;
QDirIterator it3(QETApp::companyElementsDir().toLatin1(), nameFilters, QDir::Files, QDirIterator::Subdirectories);
while (it3.hasNext())
{
if(it3.next() > 0 )
{
it3.next();
companyElementsDir ++;
}
}
qInfo()<< " Company Elements count:"<< companyElementsDir << "Elements";
qInfo()<< "";
-15
View File
@@ -174,24 +174,9 @@ int main(int argc, char **argv)
QCoreApplication::setApplicationName("QElectroTech");
//Creation and execution of the application
//HighDPI
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) // ### Qt 6: remove
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
#endif
#if QT_VERSION > QT_VERSION_CHECK(5, 7, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
qputenv("QT_ENABLE_HIGHDPI_SCALING", "1");
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(QetSettings::hdpiScaleFactorRoundingPolicy());
#endif
SingleApplication app(argc, argv, true);
+6 -3
View File
@@ -192,14 +192,16 @@ void ProjectPrintWindow::requestPaint()
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
#ifdef Q_OS_WIN
auto screen = this->screen();
if(screen)
{
#ifdef QT_DEBUG
qDebug() << "--";
qDebug() << "DiagramPrintDialog::print printer_->resolution() before " << m_printer->resolution();
qDebug() << "DiagramPrintDialog::print screennumber " << QApplication::desktop()->screenNumber();
qDebug() << "DiagramPrintDialog::print screennumber " << screen->name();
#endif
QScreen *srn = QApplication::screens().at(QApplication::desktop()->screenNumber());
qreal dotsPerInch = (qreal)srn->logicalDotsPerInch();
qreal dotsPerInch = (qreal)screen->logicalDotsPerInch();
m_printer->setResolution(dotsPerInch);
#ifdef QT_DEBUG
@@ -207,6 +209,7 @@ void ProjectPrintWindow::requestPaint()
qDebug() << "DiagramPrintDialog::print printer_->resolution() after" << m_printer->resolution();
qDebug() << "--";
#endif
}
#endif
#endif
+17 -1
View File
@@ -49,7 +49,19 @@ bool ElementData::fromXml(const QDomElement &xml_element)
return false;
}
m_type = typeFromString(xml_element.attribute(QStringLiteral("link_type"), QStringLiteral("simple")));
// --- HIER STARTET UNSER DEBUG-BLOCK ---
// Wir holen den String aus der XML und speichern ihn kurz zwischen
QString raw_type_string = xml_element.attribute(QStringLiteral("link_type"), QStringLiteral("simple"));
qDebug() << "\n=== NEUES BAUTEIL WIRD GELADEN ===";
qDebug() << "[XML Parser] Roher 'link_type' String aus der .elmt Datei:" << raw_type_string;
// Jetzt übergeben wir ihn an deine Übersetzungs-Funktion
m_type = typeFromString(raw_type_string);
qDebug() << "[XML Parser] Übersetzter ElementData-Typ:" << typeToString(m_type);
// --- HIER ENDET UNSER DEBUG-BLOCK ---
kindInfoFromXml(xml_element);
m_informations.fromXml(xml_element.firstChildElement(QStringLiteral("elementInformations")),
QStringLiteral("elementInformation"));
@@ -323,6 +335,8 @@ QString ElementData::typeToString(ElementData::Type type)
return QStringLiteral("terminal");
case ElementData::Thumbnail:
return QStringLiteral("thumbnail");
case ElementData::ConductorDefinition:
return QStringLiteral("conductor_definition");
default:
qDebug() << "ElementData::typeToString : type don't exist"
<< "return failsafe value 'simple'";
@@ -346,6 +360,8 @@ ElementData::Type ElementData::typeFromString(const QString &string)
return ElementData::Terminal;
} else if (string == QLatin1String("thumbnail")) {
return ElementData::Thumbnail;
} else if (string == QLatin1String("conductor_definition")) {
return ElementData::ConductorDefinition;
}
//Return simple if nothing match
+2 -1
View File
@@ -41,7 +41,8 @@ class ElementData : public PropertiesInterface
Master = 8,
Slave = 16,
Terminal = 32,
Thumbnail = 64};
Thumbnail = 64,
ConductorDefinition = 128};
Q_ENUM(Type)
Q_DECLARE_FLAGS(Types, Type)
+12
View File
@@ -174,6 +174,12 @@ QString TerminalData::typeToString(TerminalData::Type type)
return QString("Inner");
case Outer :
return QString("Outer");
case No :
return QString("No");
case Nc :
return QString("Nc");
case Common :
return QString("Common");
}
return QString("Generic");
}
@@ -193,6 +199,12 @@ TerminalData::Type TerminalData::typeFromString(const QString &string)
return TerminalData::Inner;
} else if (string == "Outer") {
return TerminalData::Outer;
} else if (string == "No") {
return TerminalData::No;
} else if (string == "Nc") {
return TerminalData::Nc;
} else if (string == "Common") {
return TerminalData::Common;
} else {
qDebug() << "TerminalData::typeFromString, argument string is invalid"
" failsafe type 'TerminalData::Generic' is returned";
+4 -1
View File
@@ -41,7 +41,10 @@ class TerminalData : public PropertiesInterface
enum Type {
Generic,
Inner,
Outer
Outer,
No, ///< Normally Open terminal (for SW contacts)
Nc, ///< Normally Closed terminal (for SW contacts)
Common ///< Common terminal (for SW contacts)
};
Q_ENUM(Type)
+6
View File
@@ -29,6 +29,7 @@
XRefProperties::XRefProperties()
{
m_show_power_ctc = true;
m_show_terminal_name = true;
m_display = Cross;
m_snap_to = Bottom;
m_prefix_keys << "power" << "delay" << "switch";
@@ -48,6 +49,7 @@ void XRefProperties::toSettings(QSettings &settings,
const QString prefix) const
{
settings.setValue(prefix % "showpowerctc", m_show_power_ctc);
settings.setValue(prefix % "showterminalname", m_show_terminal_name);
QString display = m_display == Cross? "cross" : "contacts";
settings.setValue(prefix % "displayhas", display);
QString snap = m_snap_to == Bottom? "bottom" : "label";
@@ -78,6 +80,7 @@ void XRefProperties::fromSettings(const QSettings &settings,
const QString prefix)
{
m_show_power_ctc = settings.value(prefix % "showpowerctc", true).toBool();
m_show_terminal_name = settings.value(prefix % "showterminalname", true).toBool();
QString display = settings.value(prefix % "displayhas", "cross").toString();
display == "cross"? m_display = Cross : m_display = Contacts;
QString snap = settings.value(prefix % "snapto", "label").toString();
@@ -107,6 +110,7 @@ QDomElement XRefProperties::toXml(QDomDocument &xml_document) const
xml_element.setAttribute("type", m_key);
xml_element.setAttribute("showpowerctc", m_show_power_ctc? "true" : "false");
xml_element.setAttribute("showterminalname", m_show_terminal_name? "true" : "false");
QString display = m_display == Cross? "cross" : "contacts";
xml_element.setAttribute("displayhas", display);
QString snap = m_snap_to == Bottom? "bottom" : "label";
@@ -137,6 +141,7 @@ QDomElement XRefProperties::toXml(QDomDocument &xml_document) const
*/
bool XRefProperties::fromXml(const QDomElement &xml_element) {
m_show_power_ctc = xml_element.attribute("showpowerctc") == "true";
m_show_terminal_name = xml_element.attribute("showterminalname", "true") == "true";
QString display = xml_element.attribute("displayhas", "cross");
display == "cross"? m_display = Cross : m_display = Contacts;
QString snap = xml_element.attribute("snapto", "label");
@@ -188,6 +193,7 @@ QHash<QString, XRefProperties> XRefProperties::defaultProperties()
bool XRefProperties::operator ==(const XRefProperties &xrp) const{
return (m_show_power_ctc == xrp.m_show_power_ctc
&& m_show_terminal_name == xrp.m_show_terminal_name
&& m_display == xrp.m_display
&& m_snap_to == xrp.m_snap_to
&& m_prefix == xrp.m_prefix
+4
View File
@@ -57,6 +57,9 @@ class XRefProperties : public PropertiesInterface
void setShowPowerContac (const bool a) {m_show_power_ctc = a;}
bool showPowerContact () const {return m_show_power_ctc;}
void setShowTerminalName (const bool a) {m_show_terminal_name = a;}
bool showTerminalName () const {return m_show_terminal_name;}
void setDisplayHas (const DisplayHas dh) {m_display = dh;}
DisplayHas displayHas () const {return m_display;}
@@ -81,6 +84,7 @@ class XRefProperties : public PropertiesInterface
private:
bool m_show_power_ctc;
bool m_show_terminal_name;
DisplayHas m_display;
SnapTo m_snap_to;
Qt::AlignmentFlag m_xref_pos;
+2 -33
View File
@@ -183,16 +183,7 @@ bool QET::orthogonalProjection(
// determine le point d'intersection des deux droites = le projete orthogonal
QPointF intersection_point;
#if TODO_LIST
#pragma message("@TODO remove code for QT 5.14 or later")
#endif
QLineF::IntersectType it = line.
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
intersect // ### Qt 6: remove
#else
intersects
#endif
(perpendicular_line, &intersection_point);
QLineF::IntersectType it = line.intersects(perpendicular_line, &intersection_point);
// ne devrait pas arriver (mais bon...)
if (it == QLineF::NoIntersection) return(false);
@@ -545,16 +536,8 @@ QString QET::joinWithSpaces(const QStringList &string_list) {
QStringList QET::splitWithSpaces(const QString &string) {
// les chaines sont separees par des espaces non echappes
// = avec un nombre nul ou pair de backslashes devant
#if TODO_LIST
#pragma message("@TODO remove code for QT 5.14 or later")
#endif
QStringList escaped_strings = string.split(QRegularExpression("[^\\]?(?:\\\\)* "),
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) // ### Qt 6: remove
QString
#else
Qt
#endif
::SkipEmptyParts);
Qt::SkipEmptyParts);
QStringList returned_list;
foreach(QString escaped_string, escaped_strings) {
@@ -684,14 +667,7 @@ bool QET::writeXmlFile(QDomDocument &xml_doc, const QString &filepath, QString *
}
QTextStream out(&file);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
out.setCodec("UTF-8");
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
out.setEncoding(QStringConverter::Utf8);
#endif
out.setGenerateByteOrderMark(false);
out << xml_doc.toString(4);
if (!file.commit())
@@ -822,14 +798,7 @@ bool QET::writeToFile(QDomDocument &xml_doc, QFile *file, QString *error_message
QTextStream out(file);
out.seek(0);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
out.setCodec("UTF-8");
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
out.setEncoding(QStringConverter::Utf8);
#endif
out.setGenerateByteOrderMark(false);
out << xml_doc.toString(4);
if (opened_here) {
-7
View File
@@ -204,14 +204,7 @@ void QETApp::setLanguage(const QString &desired_language) {
QString languages_path = languagesPath();
// load Qt library translations
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
QString qt_l10n_path = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
QString qt_l10n_path = QLibraryInfo::path(QLibraryInfo::TranslationsPath);
#endif
if (!qtTranslator.load("qt_" + desired_language, qt_l10n_path))
{
qWarning() << "failed to load"
@@ -451,16 +451,8 @@ void GraphicsTablePropertiesEditor::setUpEditConnection()
m_edit_connection << connect(ui->m_table_left_margin, QOverload<int>::of(&QSpinBox::valueChanged), this, &GraphicsTablePropertiesEditor::apply);
m_edit_connection << connect(ui->m_table_right_margin, QOverload<int>::of(&QSpinBox::valueChanged), this, &GraphicsTablePropertiesEditor::apply);
m_edit_connection << connect(ui->m_table_bottom_margin, QOverload<int>::of(&QSpinBox::valueChanged), this, &GraphicsTablePropertiesEditor::apply);
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) // ### Qt 6: remove
m_edit_connection << connect(m_table_button_group, QOverload<int>::of(&QButtonGroup::buttonClicked), this, &GraphicsTablePropertiesEditor::apply);
m_edit_connection << connect(m_header_button_group, QOverload<int>::of(&QButtonGroup::buttonClicked), this, &GraphicsTablePropertiesEditor::apply);
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 5.15 or later")
#endif
m_edit_connection << connect(m_table_button_group, QOverload<int>::of(&QButtonGroup::idClicked), this, &GraphicsTablePropertiesEditor::apply);
m_edit_connection << connect(m_header_button_group, QOverload<int>::of(&QButtonGroup::idClicked), this, &GraphicsTablePropertiesEditor::apply);
#endif
m_edit_connection << connect(ui->m_display_n_row_sb, QOverload<int>::of(&QSpinBox::valueChanged), this, &GraphicsTablePropertiesEditor::apply);
m_edit_connection << connect(ui->m_display_n_row_sb, QOverload<int>::of(&QSpinBox::valueChanged), this, &GraphicsTablePropertiesEditor::updateInfoLabel);
}
-7
View File
@@ -1725,14 +1725,7 @@ QSet<Conductor *> Conductor::relatedPotentialConductors(const bool all_diagram,
for (Conductor *c : other_conductors_list_t) {
other_conductors += c->relatedPotentialConductors(all_diagram, t_list);
}
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) // ### Qt 6: remove
other_conductors += other_conductors_list_t.toSet();
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 5.14 or later")
#endif
other_conductors += QSet<Conductor*>(other_conductors_list_t.begin(),other_conductors_list_t.end());
#endif
}
}
+398 -92
View File
@@ -17,6 +17,8 @@
*/
#include "crossrefitem.h"
#include <QTimer>
#include "../autoNum/assignvariables.h"
#include "../diagram.h"
#include "../diagramposition.h"
@@ -25,6 +27,7 @@
#include "element.h"
#include "elementtextitemgroup.h"
#include "qgraphicsitemutility.h"
#include "terminal.h"
//define the height of the header.
static int header = 5;
@@ -221,9 +224,12 @@ void CrossRefItem::updateLabel()
prepareGeometryChange();
m_bounding_rect = QRectF();
//init the painter
QPainter qp;
qp.begin(&m_drawing);
// Build geometry and m_hovered_contacts_map using a QImage-backed
// painter so font metrics match the screen painter in paint().
// m_update_map=true allows the draw functions to populate the map;
// paint() calls them with m_update_map=false so the map is stable.
QImage dummy(1, 1, QImage::Format_ARGB32_Premultiplied);
QPainter qp(&dummy);
QPen pen_;
pen_.setWidthF(0.5);
qp.setPen(pen_);
@@ -232,17 +238,21 @@ void CrossRefItem::updateLabel()
//Draw cross or contact, only if master element is linked.
if (! m_element->linkedElements().isEmpty())
{
m_update_map = true;
XRefProperties::DisplayHas dh = m_properties.displayHas();
if (dh == XRefProperties::Cross)
drawAsCross(qp);
else if (dh == XRefProperties::Contacts)
drawAsContacts(qp);
m_update_map = false;
}
qp.end();
autoPos();
update();
// Schedule a second update after the scene has finished laying out,
// so the initial render uses the correct bounding rect.
QTimer::singleShot(0, this, [this]{ update(); });
}
/**
@@ -310,7 +320,26 @@ void CrossRefItem::paint(
{
Q_UNUSED(option)
Q_UNUSED(widget)
m_drawing.play(painter);
// Draw directly — no QPicture involved anywhere.
// QPicture::play() + nested drawPicture() (m_hdr_no_ctc/m_hdr_nc_ctc)
// caused a use-after-free crash (QRegion::begin, Qt5Gui+0x49af60)
// confirmed by analysis of 19+ coredumps.
// m_update_map=false: draw functions do not overwrite m_hovered_contacts_map.
if (m_element->linkedElements().isEmpty()) return;
QPen pen_;
pen_.setWidthF(0.5);
painter->save();
painter->setPen(pen_);
painter->setFont(QETApp::diagramTextsFont(5));
m_update_map = false;
XRefProperties::DisplayHas dh = m_properties.displayHas();
if (dh == XRefProperties::Cross)
drawAsCross(*painter);
else if (dh == XRefProperties::Contacts)
drawAsContacts(*painter);
painter->restore();
}
/**
@@ -320,7 +349,24 @@ void CrossRefItem::paint(
void CrossRefItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
event->accept();
QetGraphicsItem::showItem(m_hovered_contact);
// Find the element under the click position directly from the map,
// rather than relying on m_hovered_contact which may have been reset
// by hoverMoveEvent between the two clicks of the double-click.
QPointF pos = event->pos();
Element *target = m_hovered_contact;
if (!target) {
for (auto it = m_hovered_contacts_map.begin();
it != m_hovered_contacts_map.end(); ++it) {
if (it.value().contains(pos)) {
target = it.key();
break;
}
}
}
QetGraphicsItem::showItem(target);
}
/**
@@ -432,49 +478,41 @@ void CrossRefItem::linkedChanged()
/**
@brief CrossRefItem::buildHeaderContact
Draw the QPicture of m_hdr_no_ctc and m_hdr_nc_ctc
Draw NO and NC contact symbols directly onto painter.
Previously used QPicture (m_hdr_no_ctc/m_hdr_nc_ctc) which caused
use-after-free crashes via nested QPicture::play() calls.
*/
void CrossRefItem::buildHeaderContact()
void CrossRefItem::buildHeaderContact(QPainter &painter, QPointF no_pos, QPointF nc_pos)
{
if (!m_hdr_no_ctc.isNull() && !m_hdr_nc_ctc.isNull()) return;
//init the painter
QPainter qp;
QPen pen_;
pen_.setWidthF(0.2);
painter.save();
painter.setPen(pen_);
//draw the NO contact
if (m_hdr_no_ctc.isNull()) {
qp.begin(&m_hdr_no_ctc);
qp.setPen(pen_);
qp.drawLine(0, 3, 5, 3);
//draw the NO contact header symbol
painter.drawLine(no_pos.x()+0, no_pos.y()+3, no_pos.x()+5, no_pos.y()+3);
QPointF p1[3] = {
QPointF(5, 0),
QPointF(10, 3),
QPointF(15, 3),
QPointF(no_pos.x()+5, no_pos.y()+0),
QPointF(no_pos.x()+10, no_pos.y()+3),
QPointF(no_pos.x()+15, no_pos.y()+3),
};
qp.drawPolyline(p1,3);
qp.end();
}
painter.drawPolyline(p1, 3);
//draw the NC contact
if (m_hdr_nc_ctc.isNull()) {
qp.begin(&m_hdr_nc_ctc);
qp.setPen(pen_);
//draw the NC contact header symbol
QPointF p2[3] = {
QPointF(0, 3),
QPointF(5, 3),
QPointF(5, 0)
QPointF(nc_pos.x()+0, nc_pos.y()+3),
QPointF(nc_pos.x()+5, nc_pos.y()+3),
QPointF(nc_pos.x()+5, nc_pos.y()+0)
};
qp.drawPolyline(p2,3);
painter.drawPolyline(p2, 3);
QPointF p3[3] = {
QPointF(4, 0),
QPointF(10, 3),
QPointF(15, 3),
QPointF(nc_pos.x()+4, nc_pos.y()+0),
QPointF(nc_pos.x()+10, nc_pos.y()+3),
QPointF(nc_pos.x()+15, nc_pos.y()+3),
};
qp.drawPolyline(p3,3);
qp.end();
}
painter.drawPolyline(p3, 3);
painter.restore();
}
/**
@@ -492,11 +530,95 @@ void CrossRefItem::setUpCrossBoundingRect(QPainter &painter)
QStringList no_str, nc_str;
// Helper lambda: build "[13-14] pos" string for an element.
// For power contacts (e.g. 3-pole contactor), all named terminals
// are collected (e.g. "[1-2-3-4-5-6] pos").
// For other contacts (NO/NC/SW), only the first 2 named terminals
// are used — users are expected to name and order their terminals
// in the element editor.
// Helper lambda: build "[13-14] pos" for an element.
// - Power: all terminals sorted numerically → "[1-2-3-4-5-6] pos"
// - SW with typed terminals: show relevant pair per column (handled below)
// - Others: first 2 named terminals
auto buildLabel = [this](Element *elmt, bool is_no_col) -> QString {
const bool is_power =
elmt->kindInformations()["type"].toString() == "power";
const bool is_sw =
elmt->kindInformations()["state"].toString() == "SW";
QStringList tnames;
if (is_sw) {
// Check if terminals have explicit No/Nc/Common types
bool has_typed = false;
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (t->terminalType() == TerminalData::No ||
t->terminalType() == TerminalData::Nc ||
t->terminalType() == TerminalData::Common) {
has_typed = true; break;
}
}
if (has_typed) {
QString no_name, nc_name, common_name;
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (!t->name().isEmpty()) {
if (t->terminalType() == TerminalData::No) no_name = t->name();
else if (t->terminalType() == TerminalData::Nc) nc_name = t->name();
else if (t->terminalType() == TerminalData::Common) common_name = t->name();
}
}
// NO column: show NO+Common pair; NC column: show NC+Common pair
if (is_no_col)
tnames << no_name << common_name;
else
tnames << nc_name << common_name;
} else {
// Fallback: first 2 named terminals
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
const QString tn = t->name();
if (!tn.isEmpty()) { tnames << tn; if (tnames.size() >= 2) break; }
}
}
} else {
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
const QString tn = t->name();
if (!tn.isEmpty()) {
tnames << tn;
if (!is_power && tnames.size() >= 2) break;
}
}
if (is_power) {
std::sort(tnames.begin(), tnames.end(),
[](const QString &a, const QString &b){
int i_a = a.size();
while (i_a > 0 && a[i_a-1].isDigit()) --i_a;
int i_b = b.size();
while (i_b > 0 && b[i_b-1].isDigit()) --i_b;
bool a_ok = false, b_ok = false;
int ai = a.mid(i_a).toInt(&a_ok);
int bi = b.mid(i_b).toInt(&b_ok);
if (a_ok && b_ok && a.left(i_a) == b.left(i_b))
return ai < bi;
return a < b;
});
}
}
QString pos = elementPositionText(elmt, true);
if (!tnames.isEmpty() && m_properties.showTerminalName())
return QStringLiteral("[") + tnames.join("-") + QStringLiteral("] ") + pos;
return pos;
};
for (auto elmt : NOElements()) {
no_str.append(elementPositionText(elmt, true));
no_str.append(buildLabel(elmt, true));
}
for (auto elmt : NCElements()) {
nc_str.append(elementPositionText(elmt, true));
nc_str.append(buildLabel(elmt, false));
}
//There is no string to display, we return now
@@ -509,9 +631,10 @@ void CrossRefItem::setUpCrossBoundingRect(QPainter &painter)
QRectF no_bounding;
for (auto str : no_str)
{
QRectF bounding = painter.boundingRect(QRectF (), Qt::AlignCenter, str);
no_bounding = no_bounding.united(bounding);
QRectF bounding = painter.boundingRect(QRectF(0, 0, 500, 20), Qt::AlignLeft, str);
no_bounding.setHeight(no_bounding.height() + bounding.height());
if (bounding.width() > no_bounding.width())
no_bounding.setWidth(bounding.width());
}
//Adjust according to the NO
if (no_bounding.height() > default_bounding.height() - header)
@@ -523,9 +646,10 @@ void CrossRefItem::setUpCrossBoundingRect(QPainter &painter)
QRectF nc_bounding;
for (auto str : nc_str)
{
QRectF bounding = painter.boundingRect(QRectF (), Qt::AlignCenter, str);
nc_bounding = nc_bounding.united(bounding);
QRectF bounding = painter.boundingRect(QRectF(0, 0, 500, 20), Qt::AlignLeft, str);
nc_bounding.setHeight(nc_bounding.height() + bounding.height());
if (bounding.width() > nc_bounding.width())
nc_bounding.setWidth(bounding.width());
}
//Adjust according to the NC
if (nc_bounding.height() > default_bounding.height() - header)
@@ -549,7 +673,8 @@ void CrossRefItem::drawAsCross(QPainter &painter)
{
//calculate the size of the cross
setUpCrossBoundingRect(painter);
m_hovered_contacts_map.clear();
m_drawed_contacts = 0;
if (m_update_map) m_hovered_contacts_map.clear();
//Bounding rect is empty that mean there's no contact to draw
if (boundingRect().isEmpty()) return;
@@ -559,12 +684,11 @@ void CrossRefItem::drawAsCross(QPainter &painter)
painter.drawLine(br.width()/2, 0, br.width()/2, br.height()); //vertical line
painter.drawLine(0, header, br.width(), header); //horizontal line
//Add the symbolic contacts
buildHeaderContact();
QPointF p((m_bounding_rect.width()/4) - (m_hdr_no_ctc.width()/2), 0);
painter.drawPicture (p, m_hdr_no_ctc);
p.setX((m_bounding_rect.width() * 3/4) - (m_hdr_nc_ctc.width()/2));
painter.drawPicture (p, m_hdr_nc_ctc);
//Add the symbolic contacts (drawn directly, no QPicture)
static const qreal hdr_symbol_width = 15.0;
QPointF no_pos((m_bounding_rect.width()/4) - (hdr_symbol_width/2), 0);
QPointF nc_pos((m_bounding_rect.width() * 3/4) - (hdr_symbol_width/2), 0);
buildHeaderContact(painter, no_pos, nc_pos);
//and fill it
fillCrossRef(painter);
@@ -581,7 +705,7 @@ void CrossRefItem::drawAsContacts(QPainter &painter)
return;
m_drawed_contacts = 0;
m_hovered_contacts_map.clear();
if (m_update_map) m_hovered_contacts_map.clear();
QRectF bounding_rect;
//Draw each linked contact
@@ -605,7 +729,7 @@ void CrossRefItem::drawAsContacts(QPainter &painter)
else if (type == "delayOff") option += DelayOff;
else if (type == "delayOnOff") option += DelayOnOff;
QRectF br = drawContact(painter, option, elmt);
QRectF br = drawContact(painter, option, elmt, i);
bounding_rect = bounding_rect.united(br);
}
}
@@ -624,9 +748,81 @@ void CrossRefItem::drawAsContacts(QPainter &painter)
@param elmt : the element to display text (the position of the contact)
@return The bounding rect of the draw (contact + text)
*/
QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, int pole_index)
{
QString str = elementPositionText(elmt);
// Collect terminal names from the element definition (.elmt)
// e.g. name="13" and name="14" on each terminal
// For power contacts, sort numerically and pick the pair for pole_index.
// For SW contacts with typed terminals (No/Nc/Common), filter by role.
QStringList terminal_names;
const bool is_power_ctc =
elmt->kindInformations()["type"].toString() == "power";
const bool is_sw = (flags & SW) && !(flags & NOC);
// Check if SW terminals have explicit No/Nc/Common types
bool sw_has_typed_terminals = false;
if (is_sw) {
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (t->terminalType() == TerminalData::No ||
t->terminalType() == TerminalData::Nc ||
t->terminalType() == TerminalData::Common) {
sw_has_typed_terminals = true;
break;
}
}
}
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
const QString tname = t->name();
if (!tname.isEmpty())
terminal_names << tname;
}
if (is_power_ctc) {
// Sort terminals alphanumerically so names like "R1","R2"... or "1","2"...
// are ordered correctly. Extract trailing digits for numeric comparison;
// fall back to full string comparison when no digits are found.
std::sort(terminal_names.begin(), terminal_names.end(),
[](const QString &a, const QString &b){
// Extract trailing numeric part
int i_a = a.size();
while (i_a > 0 && a[i_a-1].isDigit()) --i_a;
int i_b = b.size();
while (i_b > 0 && b[i_b-1].isDigit()) --i_b;
bool a_ok = false, b_ok = false;
int ai = a.mid(i_a).toInt(&a_ok);
int bi = b.mid(i_b).toInt(&b_ok);
if (a_ok && b_ok && a.left(i_a) == b.left(i_b))
return ai < bi;
return a < b;
});
// Pick the pair for this pole: pole 0 → [0,1], pole 1 → [2,3], etc.
int idx = pole_index * 2;
if (idx + 1 < terminal_names.size())
terminal_names = QStringList() << terminal_names[idx] << terminal_names[idx+1];
else
terminal_names.clear();
} else if (is_sw && sw_has_typed_terminals) {
// Build [NO_name, Common_name, NC_name] from typed terminals
QString no_name, nc_name, common_name;
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (!t->name().isEmpty()) {
if (t->terminalType() == TerminalData::No) no_name = t->name();
else if (t->terminalType() == TerminalData::Nc) nc_name = t->name();
else if (t->terminalType() == TerminalData::Common) common_name = t->name();
}
}
// drawText expects: [0]=NC, [1]=NO, [2]=Common
// (drawText uses [1] for NO top-left, [0] for NC bottom-left, [2] for Common right)
terminal_names.clear();
terminal_names << nc_name << no_name << common_name;
}
int offset = m_drawed_contacts*10;
QRectF bounding_rect = QRectF(0, offset, 24, 10);
@@ -643,15 +839,17 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
painter.drawLine(0, offset+6, 8, offset+6);
painter.drawLine(16, offset+6, 24, offset+6);
///take example of this code for display the terminal text
/*QFont font = QETApp::diagramTextsFont(4);
font.setBold(true);
painter.setFont(font);
// Draw terminal names on each side of the contact symbol
// terminal_names[0] on the left, terminal_names[1] on the right
if (!terminal_names.isEmpty() && m_properties.showTerminalName()) {
painter.setFont(QETApp::diagramTextsFont(4));
QRectF bt(0, offset, 24, 10);
int txt = 10 + m_drawed_contacts;
painter.drawText(bt, Qt::AlignLeft|Qt::AlignTop, QString::number(txt));
painter.drawText(bt, Qt::AlignRight|Qt::AlignTop, QString::number(txt));
painter.setFont(QETApp::diagramTextsFont(5));*/
if (terminal_names.size() >= 1)
painter.drawText(bt, Qt::AlignLeft|Qt::AlignTop, terminal_names[0]);
if (terminal_names.size() >= 2)
painter.drawText(bt, Qt::AlignRight|Qt::AlignTop, terminal_names[1]);
painter.setFont(QETApp::diagramTextsFont(5));
}
//draw open contact
if (flags &NO) {
@@ -729,14 +927,8 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
painter.drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, str);
bounding_rect = bounding_rect.united(text_rect);
if (m_hovered_contacts_map.contains(elmt))
{
if (m_update_map)
m_hovered_contacts_map.insert(elmt, bounding_rect);
}
else
{
m_hovered_contacts_map.insert(elmt, bounding_rect);
}
++m_drawed_contacts;
}
@@ -768,6 +960,26 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
};
painter.drawPolyline(p2, 3);
// Draw terminal names for switch contact (3 terminals)
// terminal_names[0] = NO side (top left)
// terminal_names[1] = NC side (bottom left)
// terminal_names[2] = common side (right)
if (!terminal_names.isEmpty() && m_properties.showTerminalName()) {
painter.setFont(QETApp::diagramTextsFont(4));
// Sort order from parseTerminal (top->bottom, left->right):
// [0]=12 (NO, top-left), [1]=14 (common, top-center), [2]=13 (NC, bottom-center)
if (terminal_names.size() >= 1)
painter.drawText(QRectF(0, offset, 8, 8),
Qt::AlignLeft|Qt::AlignTop, terminal_names[1]); // 12 NO left
if (terminal_names.size() >= 2)
painter.drawText(QRectF(16, offset+4, 8, 6),
Qt::AlignRight|Qt::AlignTop, terminal_names[2]); // 14 common right
if (terminal_names.size() >= 3)
painter.drawText(QRectF(0, offset+9, 8, 6),
Qt::AlignLeft|Qt::AlignTop, terminal_names[0]); // 13 NC left-bottom
painter.setFont(QETApp::diagramTextsFont(5));
}
//Draw the half ellipse off delay
if (flags &Delay)
{
@@ -799,12 +1011,8 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
str);
bounding_rect = bounding_rect.united(text_rect);
if (m_hovered_contacts_map.contains(elmt)) {
if (m_update_map)
m_hovered_contacts_map.insert(elmt, bounding_rect);
}
else {
m_hovered_contacts_map.insert(elmt, bounding_rect);
}
//a switch contact take place of two normal contact
m_drawed_contacts += 2;
@@ -835,12 +1043,8 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
str);
bounding_rect = bounding_rect.united(text_rect);
if (m_hovered_contacts_map.contains(elmt)) {
if (m_update_map)
m_hovered_contacts_map.insert(elmt, bounding_rect);
}
else {
m_hovered_contacts_map.insert(elmt, bounding_rect);
}
++m_drawed_contacts;
}
return bounding_rect;
@@ -866,7 +1070,60 @@ void CrossRefItem::fillCrossRef(QPainter &painter)
m_hovered_contact == elmt ? pen.setColor(Qt::blue) :pen.setColor(Qt::black);
painter.setPen(pen);
// Collect terminal names for NO column.
// Power: all terminals sorted numerically.
// SW with typed terminals: NO+Common pair.
// Others: first 2 named terminals.
const bool is_power_no =
elmt->kindInformations()["type"].toString() == "power";
const bool is_sw_no =
elmt->kindInformations()["state"].toString() == "SW";
QStringList tnames;
if (is_sw_no) {
bool has_typed = false;
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (t->terminalType() == TerminalData::No ||
t->terminalType() == TerminalData::Nc ||
t->terminalType() == TerminalData::Common) {
has_typed = true; break;
}
}
if (has_typed) {
QString no_name, common_name;
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (!t->name().isEmpty()) {
if (t->terminalType() == TerminalData::No) no_name = t->name();
else if (t->terminalType() == TerminalData::Common) common_name = t->name();
}
}
tnames << no_name << common_name;
} else {
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
const QString tn = t->name();
if (!tn.isEmpty()) { tnames << tn; if (tnames.size() >= 2) break; }
}
}
} else {
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
const QString tn = t->name();
if (!tn.isEmpty()) {
tnames << tn;
if (!is_power_no && tnames.size() >= 2) break;
}
}
}
QString terminal_label;
if (!tnames.isEmpty() && m_properties.showTerminalName())
terminal_label = QStringLiteral("[") + tnames.join("-") + QStringLiteral("]");
QString str = elementPositionText(elmt, true);
if (!terminal_label.isEmpty())
str = terminal_label + QStringLiteral(" ") + str;
QRectF bounding = painter.boundingRect(
QRectF(no_top_left,
QSize(middle_cross, 1)),
@@ -874,13 +1131,11 @@ void CrossRefItem::fillCrossRef(QPainter &painter)
str);
painter.drawText(bounding, Qt::AlignLeft, str);
if (m_hovered_contacts_map.contains(elmt))
{
m_hovered_contacts_map.insert(elmt, bounding);
}
else
{
m_hovered_contacts_map.insert(elmt, bounding);
if (m_update_map) {
QString pos_str = elementPositionText(elmt, true);
QRectF pos_rect = painter.boundingRect(bounding, Qt::AlignRight, pos_str);
pos_rect.adjust(-2, -1, 2, 1); // extend hit area slightly
m_hovered_contacts_map.insert(elmt, pos_rect);
}
no_top_left.ry() += bounding.height();
@@ -895,7 +1150,60 @@ void CrossRefItem::fillCrossRef(QPainter &painter)
:pen.setColor(Qt::black);
painter.setPen(pen);
// Collect terminal names for NC column.
// Power: all terminals sorted numerically.
// SW with typed terminals: NC+Common pair.
// Others: first 2 named terminals.
const bool is_power_nc =
elmt->kindInformations()["type"].toString() == "power";
const bool is_sw_nc =
elmt->kindInformations()["state"].toString() == "SW";
QStringList tnames_nc;
if (is_sw_nc) {
bool has_typed = false;
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (t->terminalType() == TerminalData::No ||
t->terminalType() == TerminalData::Nc ||
t->terminalType() == TerminalData::Common) {
has_typed = true; break;
}
}
if (has_typed) {
QString nc_name, common_name;
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (!t->name().isEmpty()) {
if (t->terminalType() == TerminalData::Nc) nc_name = t->name();
else if (t->terminalType() == TerminalData::Common) common_name = t->name();
}
}
tnames_nc << nc_name << common_name;
} else {
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
const QString tn = t->name();
if (!tn.isEmpty()) { tnames_nc << tn; if (tnames_nc.size() >= 2) break; }
}
}
} else {
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
const QString tn = t->name();
if (!tn.isEmpty()) {
tnames_nc << tn;
if (!is_power_nc && tnames_nc.size() >= 2) break;
}
}
}
QString terminal_label;
if (!tnames_nc.isEmpty() && m_properties.showTerminalName())
terminal_label = QStringLiteral("[") + tnames_nc.join("-") + QStringLiteral("]");
QString str = elementPositionText(elmt, true);
if (!terminal_label.isEmpty())
str = terminal_label + QStringLiteral(" ") + str;
QRectF bounding = painter.boundingRect(
QRectF(nc_top_left,
QSize(middle_cross, 1)),
@@ -903,13 +1211,11 @@ void CrossRefItem::fillCrossRef(QPainter &painter)
str);
painter.drawText(bounding, Qt::AlignRight, str);
if (m_hovered_contacts_map.contains(elmt))
{
m_hovered_contacts_map.insert(elmt, bounding);
}
else
{
m_hovered_contacts_map.insert(elmt, bounding);
if (m_update_map) {
QString pos_str = elementPositionText(elmt, true);
QRectF pos_rect = painter.boundingRect(bounding, Qt::AlignRight, pos_str);
pos_rect.adjust(-2, -1, 2, 1); // extend hit area slightly
m_hovered_contacts_map.insert(elmt, pos_rect);
}
nc_top_left.ry() += bounding.height();
+3 -4
View File
@@ -22,7 +22,6 @@
#include <QGraphicsObject>
#include <QMultiMap>
#include <QPicture>
class Element;
class DynamicElementTextItem;
@@ -103,11 +102,11 @@ class CrossRefItem : public QGraphicsObject
private:
void linkedChanged();
void buildHeaderContact();
void buildHeaderContact(QPainter &painter, QPointF no_pos, QPointF nc_pos);
void setUpCrossBoundingRect(QPainter &painter);
void drawAsCross(QPainter &painter);
void drawAsContacts(QPainter &painter);
QRectF drawContact(QPainter &painter, int flags, Element *elmt);
QRectF drawContact(QPainter &painter, int flags, Element *elmt, int pole_index = 0);
void fillCrossRef(QPainter &painter);
void AddExtraInfo(QPainter &painter, const QString&);
QList<Element *> NOElements() const;
@@ -117,10 +116,10 @@ class CrossRefItem : public QGraphicsObject
private:
Element *m_element; //element to display the cross reference
QRectF m_bounding_rect;
QPicture m_drawing, m_hdr_no_ctc, m_hdr_nc_ctc;
QPainterPath m_shape_path;
XRefProperties m_properties;
int m_drawed_contacts;
bool m_update_map = false;
QMultiMap <Element *, QRectF> m_hovered_contacts_map;
Element *m_hovered_contact = nullptr;
DynamicElementTextItem *m_text = nullptr;
@@ -26,7 +26,7 @@
#include "crossrefitem.h"
#include "element.h"
#include "elementtextitemgroup.h"
#include <QTimer>
#include <QDomDocument>
#include <QDomElement>
#include <QGraphicsSceneMouseEvent>
@@ -302,7 +302,8 @@ void DynamicElementTextItem::refreshLabelConnection()
if ((m_text_from == ElementInfo && m_info_name == "label") ||
(m_text_from == CompositeText && m_composite_text.contains("%{label}")))
{
if(m_parent_element.data()->linkType() & Element::AllReport)
if((m_parent_element.data()->linkType() & Element::AllReport) ||
m_parent_element.data()->linkType() == Element::ConductorDefinition)
{
updateReportFormulaConnection();
updateReportText();
@@ -418,7 +419,8 @@ void DynamicElementTextItem::setInfoName(const QString &info_name)
updateXref();
}
if (m_parent_element && (m_parent_element.data()->linkType() & Element::AllReport)) //special treatment for report
if (m_parent_element && ((m_parent_element.data()->linkType() & Element::AllReport) ||
m_parent_element.data()->linkType() == Element::ConductorDefinition)) //special treatment for report
{
if(old_info_name != info_name)
{
@@ -472,7 +474,8 @@ void DynamicElementTextItem::setCompositeText(const QString &text)
updateXref();
}
if (m_parent_element && (m_parent_element.data()->linkType() & Element::AllReport)) //special treatment for report
if (m_parent_element && ((m_parent_element.data()->linkType() & Element::AllReport) ||
m_parent_element.data()->linkType() == Element::ConductorDefinition)) //special treatment for report
{
/*
* May be in some case the old and new composite text both have the var %{label},
@@ -726,7 +729,8 @@ QVariant DynamicElementTextItem::itemChange(QGraphicsItem::GraphicsItemChange ch
if(!m_parent_element.data()->linkedElements().isEmpty())
masterChanged();
}
else if(m_parent_element.data()->linkType() & Element::AllReport)
else if((m_parent_element.data()->linkType() & Element::AllReport) ||
m_parent_element.data()->linkType() == Element::ConductorDefinition)
{
//Get the report formula, and add connection to keep up to date the formula.
if (m_parent_element.data()->diagram() && m_parent_element.data()->diagram()->project())
@@ -1027,7 +1031,8 @@ void DynamicElementTextItem::clearFormulaConnection()
void DynamicElementTextItem::updateReportFormulaConnection()
{
if(!(m_parent_element.data()->linkType() & Element::AllReport))
if(!(m_parent_element.data()->linkType() & Element::AllReport) &&
m_parent_element.data()->linkType() != Element::ConductorDefinition)
return;
removeConnectionForReportFormula(m_report_formula);
@@ -1041,7 +1046,8 @@ void DynamicElementTextItem::updateReportFormulaConnection()
*/
void DynamicElementTextItem::updateReportText()
{
if(!(m_parent_element.data()->linkType() & Element::AllReport))
if(!(m_parent_element.data()->linkType() & Element::AllReport) &&
m_parent_element.data()->linkType() != Element::ConductorDefinition)
return;
if (m_text_from == ElementInfo && m_info_name == "label")
@@ -1098,7 +1104,10 @@ void DynamicElementTextItem::updateLabel()
void DynamicElementTextItem::conductorWasAdded(Conductor *conductor)
{
Q_UNUSED(conductor)
QTimer::singleShot(100, this, [this]() {
setPotentialConductor();
conductorPropertiesChanged();
});
}
/**
@@ -1123,7 +1132,8 @@ void DynamicElementTextItem::conductorWasRemoved(Conductor *conductor)
*/
void DynamicElementTextItem::setPotentialConductor()
{
if(parentElement() && (parentElement()->linkType() & Element::AllReport))
if(parentElement() && ((parentElement()->linkType() & Element::AllReport) || (parentElement()->linkType() == Element::ConductorDefinition) ||
parentElement()->linkType() == Element::ConductorDefinition))
{
if(parentElement()->terminals().isEmpty())
return;
@@ -1156,6 +1166,7 @@ void DynamicElementTextItem::setPotentialConductor()
connect(m_watched_conductor.data(), &Conductor::propertiesChange, this, &DynamicElementTextItem::conductorPropertiesChanged);
}
}
conductorPropertiesChanged();
}
else //This text haven't got a parent element, then ther isn't a conductor in the potential
{
@@ -1172,7 +1183,8 @@ void DynamicElementTextItem::setPotentialConductor()
*/
void DynamicElementTextItem::conductorPropertiesChanged()
{
if(m_parent_element && (m_parent_element.data()->linkType() & Element::AllReport))
if(m_parent_element && ((m_parent_element.data()->linkType() & Element::AllReport) || (m_parent_element.data()->linkType() == Element::ConductorDefinition) ||
m_parent_element.data()->linkType() == Element::ConductorDefinition))
{
if(m_text_from == ElementInfo)
{
@@ -1201,7 +1213,8 @@ QString DynamicElementTextItem::reportReplacedCompositeText() const
{
QString string;
if(m_parent_element.data()->linkType() & Element::AllReport)
if((m_parent_element.data()->linkType() & Element::AllReport) || (m_parent_element.data()->linkType() == Element::ConductorDefinition) ||
m_parent_element.data()->linkType() == Element::ConductorDefinition)
{
string = m_composite_text;

Some files were not shown because too many files have changed in this diff Show More