Commit Graph

8478 Commits

Author SHA1 Message Date
ispyisail 1b8dea3946 Add consent dialog before EPLAN (.edz) import
Shows the licensing/liability warning text agreed on in PR #513
(scorpio810) before the file picker opens. Import stays disabled
until the "I have read and accept these terms" checkbox is ticked.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 21:11:35 +12:00
Shane Ringrose 734391eabf Merge master: add cli_export, pdf_links; EDZ: 10-slot grid, group headers, ToS notice
- Resolve cmake/qet_compilation_vars.cmake conflict: keep both upstream's
  cli_export.cpp/h and pdf_links.cpp/h and the EDZ source additions.

- 10-position grid alignment: pin_y values are multiples of 10 so terminals
  snap cleanly to QET's default grid.  group_gap raised to 10 (one full slot).

- Named connector groups get a header label (group name) placed in the gap
  above the first pin, so the electrician sees block names (XDI, XPOW, …)
  without reading individual terminal designations.

- Device-tag dynamic_text now uses 9pt LABEL_FONT and y = min_y - 9 so it
  clears the element body and is legible at normal zoom.

- Add EPLAN Data Portal Terms of Use disclaimer to sources/import/edz/README.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 21:00:49 +12:00
plc-user 1410e70d13 Merge pull request #521 from ispyisail/fix/iscustom-collection-prefix
Sounds plausible! 
Thank you @ispyisail
2026-06-21 09:24:42 +02:00
Shane Ringrose 7669a95694 fix(collection): isCustomCollection() false-positive on company path
The default user-collection path ends in "elements" and the default
company-collection path ends in "elements-company".  Both
FileElementCollectionItem::isCustomCollection() and
ElementsLocation::isCustomCollection() used startsWith(customDir),
so "…/elements-company/…" matched "…/elements" and returned true.

This caused ElementsCollectionModel::addLocation() to insert a
newly-saved user-collection element as a child of the company-
collection branch in the tree, making it appear in the wrong panel.

Fix: require the path to equal the directory root exactly, or to
start with the directory root followed by '/'.

  path == dir || path.startsWith(dir + QLatin1Char('/'))

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 12:10:22 +12:00
Shane Ringrose 8268df8a80 edz: name terminals terminalNr.designation (e.g. "XDI.2")
Previous logic resolved duplicate connectionDesignation values by
appending sanitised description text or numeric suffixes, which was
fragile and produced names an electrician could not easily map back to
the physical wiring.

New scheme:
  • terminalNr present  → "XDI.2", "XRO1.3", "XPOW.1" …
  • terminalNr absent   → designation as-is ("L1/U1", "UDC+", "PE") —
    these are busbar / power connections and are already globally unique
  • collision (malformed data) → numeric suffix as safety net

The description (connectiondescription) remains as the human-readable
label beside the terminal symbol, exactly as suggested by plc-user.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 08:58:30 +12:00
Shane Ringrose 03e6e76b3f edz: fix missing-field-initializer warning in EdzElementBuilder
EdzPin gained a third field (group) but the fallback initializer at
line 70 still listed only two fields, triggering -Wmissing-field-initializers.
Added the explicit QString() for group.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 03:02:31 +12:00
Shane Ringrose 2aeeb74e72 edz: group terminals by physical connector (terminalNr), drop separator lines
EPLAN 2022-style part.xml files (e.g. IFM AL1122) use a numeric
functiondefgroup attribute and do not carry the text functiondefinition
block name that the previous grouping logic relied on.  Those parts
fell back to grouping by pin designation, producing symbols with all
pin "1"s stacked together, then all "2"s, etc. — the bug reported in
PR #513.

Fix: read terminalNr first (the physical M12/connector socket identifier,
e.g. "X01", "X31") as the primary group key; fall back to functiondefinition
text for older EPLAN formats that omit terminalNr.  Pins within each
connector group are still sorted by designation using natural sort.

Also remove the dashed inter-group separator lines; the existing 5 px
gap between groups provides sufficient visual separation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 02:57:14 +12:00
Laurent Trinques ee53f20303 Merge pull request #517 from ispyisail/fix/snap-xcb-cursor-missing
snap: stage libxcb-cursor0 to fix xcb plugin crash on Ubuntu 24.04 (issue #373)
2026-06-20 16:44:59 +02:00
Shane Ringrose 756cfd98c0 Fix terminal grouping: use functiondefinition block, not pin designation
The previous grouping compared `connectionDesignation` values (e.g. "1",
"2", "3") to detect group boundaries.  Those values are pin position
numbers within a terminal block, not functional group identifiers, so
devices like the ABB ACS880 produced 11+ tiny single-pin "groups" instead
of the ~8 functional blocks (AC-IN, Motor-OUT, DC-Bus, Brake-Resistor,
Analog-I/O, Digital-I/O, ...) the reviewer identified.

Fix:
- Add `group` field to EdzPin, populated from the `functiondefinition`
  attribute on <functiontemplate> (newer EPLAN) or its parent <function>
  element (older EPLAN).
- Sort pins by group first (preserving XML appearance order per group),
  then by designation within each group using natural sort.
- Use `groupKey()` — group when present, designation as fallback — for
  the group-break detection that drives separator lines and Y-gaps.

Parts without any functiondefinition data retain the previous
designation-based behaviour unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 01:54:23 +12:00
plc-user 71ff7f925d Merge pull request #515 from ispyisail/fix/valgrind-uninit-machineinfo-firstshow
Thank you @ispyisail
2026-06-20 13:19:39 +02:00
Shane Ringrose 2fdc07b81b snap: stage libxcb-cursor0 to fix xcb plugin load failure on Ubuntu 24.04
Qt 5.15.x added libxcb-cursor0 as a hard runtime dependency of the xcb
platform plugin (libqxcb.so).  The kf5-5-110-qt-5-15-11-core22 content
snap does not bundle this library, so when the snap runs on an Ubuntu 24.04
host the dlopen() of the plugin fails with:

  qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in ""
  even though it was found.

Staging libxcb-cursor0 from the Ubuntu 22.04 archive satisfies the
dependency without changing the snap base, Qt version, or any other
dependency.  No ABI mismatch: the plugin and the staged library are
both built against the core22 (22.04) ABI.

Fixes issue #373.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 22:51:11 +12:00
Shane Ringrose 27534a379b Group terminals by designation with visual separator lines
Pins are already sorted by designation (see EdzPart::parse natural sort).
This change makes same-designation groups visually obvious in the element
symbol by:

  - Inserting a 5 px gap between consecutive terminals whose designation
    differs, so each group reads as a distinct block.
  - Drawing a thin dashed horizontal line through each gap, mirroring
    the grouped-I/O style shown in typical manufacturer datasheets.

The body rectangle and bounding box grow automatically to accommodate the
extra gaps, so no fixed sizes change.  Unique terminal names (added in the
previous commit) are unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 22:44:06 +12:00
plc-user 32bb247d68 Merge pull request #514 from ispyisail/fix/thread-safe-datadir-configdir
Thank you @ispyisail
2026-06-20 12:39:12 +02:00
Shane Ringrose 207c0c9544 Fix copyright years and deduplicate terminal names
Two issues raised in PR review:

1. Copyright year: new files carried "2006" (the project's founding year).
   Updated to "2006-2026" to reflect the actual authorship period.

2. Duplicate terminal names: EPLAN parts can have multiple connection
   templates sharing the same connectionDesignation (e.g. a drive where
   both wire entries of terminal "1" are labelled "1").  QET requires
   unique terminal names for wiring and terminal-diagram generation.

   Resolution order in EdzElementBuilder::build():
   - Unique designation → used as-is.
   - Duplicated designation with distinct sanitised description →
     "designation_description" (e.g. "1_L_P").
   - Otherwise → numeric suffix: "1", "1_2", "1_3", …

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 22:39:11 +12:00
Shane Ringrose c586a2d3a3 Fix three uninitialised-value bugs found by Valgrind
1. machine_info.h: zero-initialise Screen struct members
   Max_width, Max_height, count, width[] and height[] were bare
   int32_t with no initialiser. The comparisons in
   init_get_Screen_info() read them before any write, producing
   undefined behaviour flagged by Valgrind as 'Conditional jump
   or move depends on uninitialised value(s)'.

2. main.cpp: pre-initialise MachineInfo on the main thread
   MachineInfo::instance() was first called inside QtConcurrent::run(),
   causing its constructor (which calls qApp->screens()) to run on a
   background thread. QScreen methods are not thread-safe in Qt5.
   Calling instance() once on the main thread before the worker
   launches guarantees the singleton is fully built first; subsequent
   calls from the worker just return the cached pointer.

3. qetdiagrameditor.h: move m_first_show before the QActionGroup members
   C++ initialises members in declaration order. m_first_show was
   declared after the QActionGroup members (line 256 vs 168). During
   construction of m_row_column_actions_group(this), Qt dispatches a
   QObject parent-change event that reaches QETDiagramEditor::event(),
   which reads m_first_show before it has been initialised.
   Moving the declaration to the top of the first private: block
   ensures it is initialised before any member that can trigger events.

All three found via Valgrind --tool=memcheck on Ubuntu 22.04 / Qt 5.15.3.
Relates-to: PR #514 (same QtConcurrent thread-safety pattern).
2026-06-20 22:31:06 +12:00
Shane Ringrose f301196f61 Rename static locals to match original variable names per review
Reviewer requested configdir/datadir instead of cached for consistency
with the surrounding code style.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 22:26:32 +12:00
Shane Ringrose f2d297b0d8 Fix thread-unsafe QStandardPaths calls in dataDir/configDir
QStandardPaths::writableLocation() is not thread-safe in Qt5.
ElementsCollectionModel::reload() launches:

  QtConcurrent::map(m_items_list_to_setUp, setUpData)

Each worker calls FileElementCollectionItem::setUpData()
→ collectionPath() → isCollectionRoot()
→ QETApp::userMacrosDir() → QETApp::dataDir()
→ QStandardPaths::writableLocation()  ← SIGSEGV (null deref)

The crash was confirmed by Valgrind (address 0x0, inside
libQt5Core's writableLocation internals).

Fix: replace the bare QStandardPaths calls in dataDir() and
configDir() with a C++11 static-local lambda.  The compiler
guarantees the lambda body runs exactly once across all threads
(magic statics, ISO C++11 §6.7).  After the first (main-thread)
call the result is returned lock-free.

Relates-to: #492 (same QtConcurrent lifetime pattern fixed in
QETProject::writeBackup by PR #512).
2026-06-20 20:09:48 +12:00
Shane Ringrose d56bca66da Harden EPLAN import: format detection, trim SDK, docs (M4)
- EdzArchive checks the archive magic up front: gives a clear message for
  zip-format .edz (not yet supported) and unrecognised data, instead of an
  opaque 7z decode error.
- Trim the vendored LZMA SDK headers to the decode closure actually used
  (removes 21 unused encoder/multithread/Xz/Aes headers; 18 .c + 18 .h remain).
- Add sources/import/edz/README.md documenting the feature, the data mapping
  and the bundled SDK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 08:14:53 +12:00
Shane Ringrose aa6a0b941a Add English translations for the EPLAN import strings
The new import action and dialogs used French source strings (QET convention)
but had no English translation, so they showed French in the English UI. Add
the five strings to qet_en.ts (menu action, dialog title, file filter, error
box title and message).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 08:03:00 +12:00
Shane Ringrose e4b4ba875b Bundle LZMA SDK for .edz extraction; drop 7z CLI dependency (M3)
Replaces the QProcess->7z shim with the public-domain LZMA SDK (23.01) 7-Zip
reader, vendored under sources/import/edz/lzma/ (decode-only subset). New
edzsevenzip.cpp wraps SzArEx_Open/SzArEx_Extract and writes entries via Qt, so
EdzArchive no longer needs an external 7-Zip at runtime. Enables the C language
in CMake for the vendored sources.

Verified: the bundled decoder extracts all three ifm sample .edz and the
generated elements still match the Python oracle exactly (byte-correct decode),
with no 7z on the path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 07:49:14 +12:00
Shane Ringrose f4ff6c81f9 Add EdzImporter + collection-panel import action (M2)
Wires EPLAN .edz import into the elements panel. EdzImporter orchestrates
EdzArchive -> EdzPart -> EdzElementBuilder and writes the generated .elmt into a
destination collection folder (named by order number). A right-click
"Importer une piece EPLAN (.edz)..." action on a writable collection directory
opens a file picker, runs the importer into that folder's fileSystemPath() and
reloads the panel; errors surface via QetMessageBox. Modeled on newElement().

EdzImporter verified headless against the Python oracle for KG6000/MFH200/
R1D200; the panel wiring is built/tested via the WSL Qt5+KF5 build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 07:34:52 +12:00
Shane Ringrose 7cc5790f7c Add EdzPart + EdzElementBuilder: .edz part -> .elmt (M1)
Port of the edz2qet.py prototype's mapping to C++. EdzPart parses an EPLAN
part.xml into a portable model (identity/metadata, localized names, connection
list); EdzElementBuilder turns that into a QET element (generic symbol: body
rectangle + one west-facing terminal per pin, per-pin labels, localized <name>s,
and elementInformations for the BOM).

Pins are natural-sorted by designation so they stack 1,2,3,4 regardless of the
order EPLAN lists them (MFH200 lists 1,3,4,2). Output verified structurally
identical (uuids aside) to the Python oracle for three ifm samples — KG6000
(4-pin), MFH200 (4-pin, reordered) and R1D200 (5-pin, incl. Dutch name) — and
the generated element loads in the QET editor with correct UTF-8.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 07:11:09 +12:00
Shane Ringrose 82884d1cf7 Add EdzArchive: extract EPLAN .edz packages (M0)
First piece of native EPLAN Data Portal (.edz) import. A .edz is a 7-Zip
archive; QET previously had no archive handling. EdzArchive unpacks one to a
temporary directory and locates the contained part.xml.

The extraction backend is isolated behind extractWithSevenZipCli() so it can be
replaced with a bundled decompressor later without touching callers; this M0
step shells out to a 7-Zip CLI via QProcess. Verified on three ifm sample
parts (KG6000, MFH200, R1D200 — all 7z), plus the corrupt/missing-file paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 07:05:42 +12:00
plc-user a8e2a7acff concat QString with '%' 2026-06-19 17:06:58 +02:00
plc-user 53096fa3be Merge pull request #512 from ispyisail/fix-492-backup-uaf
Although I haven’t encountered the problems described myself (nor do I need to!), the changes and additions look plausible and compile without errors or warnings, so I’m approving this PR!

But as a remark:
I'm not particularly familiar with the Qt functions used here. But when I see that there is a version-specific implementation for Qt5 and only a debug-message mentioned for Qt6, it makes me wonder:
Is it implemented differently there, or is it not even needed there — which I don't think is the case...
If possible, "we" should also include something for Qt6 and later versions in another PR.
Do you know what’s needed for Qt6, @ispyisail ?
2026-06-19 12:35:47 +02:00
ispyisail bf4d3353ae Fix #492: wait for async backup before destroying QETProject
writeBackup() fires QtConcurrent::run(QET::writeToFile, ..., &m_backup_file)
fire-and-forget: the QFuture was discarded and nothing kept m_backup_file
alive until the worker finished. If the QETProject was destroyed first, the
worker wrote through the freed member -> use-after-free crash in
QET::writeToFile (intermittent; ~1/6 on short-lived CLI runs).

Store the QFuture and waitForFinished() in ~QETProject (and before
setFilePath() re-points the managed backup file). Also skip launching a new
backup while one is still running, so two threads never write m_backup_file
at once.

The Qt6 path is still a TODO stub and the QtConcurrent block is KF5-only, so
this affects only the Qt5/KF5 build that actually has the backup code.
2026-06-19 08:34:02 +12:00
plc-user bd37f12edc upgrade 'osifont.ttf' from upstream 2026-06-18 15:20:25 +02:00
plc-user b873b05245 concat QString with '%' 2026-06-18 14:51:56 +02:00
plc-user eb02e1dce0 fix warning: 'if does not guard...' 2026-06-18 14:33:10 +02:00
plc-user ac1e8b3502 fix warning: name 'label_11' already used... 2026-06-18 14:30:35 +02:00
plc-user 215962873b fix warning: unused variable "pdfExport" (should be fixed properly by original author by evaluating function-result) 2026-06-18 14:26:32 +02:00
plc-user 02cc4f043b Merge pull request #511 from ispyisail/fix-highlight-unused-reset
With this change we can close #159 as well!
Thank you @ispyisail
2026-06-18 12:42:00 +02:00
ispyisail 8791d2d202 Highlight reset: only clear the red unused-highlight
Per review (plc-user): scope the reset to items currently painted with the
red Dense4Pattern instead of clearing every item's background. This avoids
clobbering other backgrounds (e.g. the amber "show this dir" highlight)
and skips needless item updates on large collections.
2026-06-18 22:29:42 +12:00
ispyisail 4625964bb1 Fix #159: reset unused-element highlight when elements become used again
ElementsCollectionModel::highlightUnusedElement() only ever painted the
currently-unused elements red; it never cleared the background of items
that were no longer unused. So when an element was re-added to a project
and saved, its red 'unused' highlight persisted until the model was
rebuilt from scratch.

Reset every item's background before re-applying the highlight to the
current unused set.
2026-06-18 21:09:49 +12:00
plc-user 342ac3626d fix also German binary-translation 2026-06-17 14:11:11 +02:00
Laurent Trinques 19a75186b7 Merge pull request #506 from ispyisail/fix/snap-pin-sources
snap: pin qet-tb-generator source to tag v1.31
2026-06-17 13:24:10 +02:00
plc-user 9122f5d687 fix German translation 2026-06-16 21:13:14 +02:00
plc-user 482fd32dc2 element-editor: no warning on save, when element without terminals is frontview (and fix indention) 2026-06-16 20:40:08 +02:00
plc-user ae42198882 Merge pull request #508 from ispyisail/fix-tbgen-drop-legacy-appdata
Drop dead ~/Application Data/qet fallback for qet_tb_generator (Windows)
As discussed in #199 ...
2026-06-16 12:37:07 +02:00
Shane Ringrose a666d2d0ae qet_tb_generator: drop dead ~/Application Data/qet fallback (Windows)
The Windows search list for the qet_tb_generator plugin included
`~/Application Data/qet/qet_tb_generator.exe` as a fallback. That legacy
junction path is the same inaccessible location the standard-directories
change moved QET away from, and it never matched a pip install anyway:
`pip install qet_tb_generator` puts the executable in the Python Scripts
directory (`...\PythonXX\Scripts` on PATH, or
`%APPDATA%\Python\PythonXX\Scripts` for --user installs), not in
`QETApp::dataDir()`.

Pip installs are already found via QStandardPaths::findExecutable (PATH),
and manual binary drops via dataDir()/binary and the working directory.
The legacy entry only matched old manual drops into the inaccessible
folder, so remove it.

Refs qelectrotech-source-mirror#199

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 22:11:22 +12:00
Shane Ringrose 15e7d39243 snap: pin qet-tb-generator source to tag v1.31
Without a source-tag or source-commit, snapcraft pulls the latest
HEAD of qet_tb_generator-plugin on every build.  This makes builds
non-reproducible and risks breakage whenever the upstream repo changes.

Pin to the only published release tag (v1.31, commit d6ee3cf) so
the snap always builds against a known-good version of the plugin.

Closes #202

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 10:15:09 +12:00
Laurent Trinques 5574b4d3e9 CMake: guard Linux-only install rules with UNIX AND NOT APPLE
The .desktop, MIME package, and appdata install rules are
freedesktop.org conventions and only apply on Linux. Wrapping
them in if(UNIX AND NOT APPLE) prevents a configure failure on
macOS and Windows where QET_APPDATA_PATH and QET_MIME_PACKAGE_PATH
are not defined.

Also replace the hardcoded share/mime/packages path with
${QET_MIME_PACKAGE_PATH} for consistency with
paths_compilation_installation.cmake.

No change to Linux build behaviour.
2026-06-14 13:58:37 +02:00
Laurent Trinques 918db632ed Update windows-msi.yml 2026-06-13 10:56:07 +02:00
Laurent Trinques f465b33e2b Update windows-msi.yml 2026-06-13 10:45:41 +02:00
Laurent Trinques 492528136e Update windows-msi.yml 2026-06-13 10:40:37 +02:00
Laurent Trinques 564a0e64a4 Update windows-msi.yml 2026-06-13 10:31:58 +02:00
Laurent Trinques 7f5a42a055 Update windows-msi.yml 2026-06-13 10:14:05 +02:00
Laurent Trinques 14d4aa772b Update windows-msi.yml 2026-06-13 09:59:17 +02:00
Laurent Trinques 81419bd27d Merge pull request #498 from ispyisail/fix-lang-path-fallback
Find translations when lang/ is beside bin/, not inside it (fixes #86)
2026-06-13 04:49:14 +02:00
Laurent Trinques ebefc269af Update Changelog file 2026-06-12 19:43:57 +02:00