Commit Graph

8614 Commits

Author SHA1 Message Date
Laurent Trinques 2b8cdd3d67 ci: update GitHub Actions to latest major versions 2026-07-06 16:46:54 +02:00
Laurent Trinques be008fb5f6 ci: update GitHub Actions to latest major versions 2026-07-06 16:43:16 +02:00
Laurent Trinques 82cd4d359a macOS fix: add explicit QGraphicsLayoutItem include in helpercell.h
MOC on macOS does not resolve QGraphicsLayoutItem through the bulk
QtWidgets include, causing an 'Undefined interface' error at build time.
Adding an explicit include resolves this. Linux builds are unaffected. Thanks hairykiwi 8ef4e04
2026-07-06 14:45:58 +02:00
Laurent Trinques 6dca56f823 git submodule update SingleApplication to v3.5.6 2026-07-06 13:08:34 +02:00
Laurent Trinques 545006bed2 Merge pull request #533 from geri1701/no-kf5-qt-only
Fix Qt-only build when BUILD_WITH_KF5=OFF
2026-07-05 18:00:35 +02:00
Gerhard Schwanzer dcec0bf7ff Skip actively locked autosave files
Check the QLockFile in staleFiles() before returning a no-KF5 recovery candidate, matching the KAutoSaveFile contract that actively owned autosave files are not stale.

Extend the no-KF5 Catch test so a child process keeps the autosave lock alive while allStaleFiles() runs, then verify recovery after the child is killed.

Assisted-by: pi coding agent / Mika (OpenAI GPT-5.5)
2026-07-05 17:39:38 +02:00
Gerhard Schwanzer 7a97e873d9 Test Qt-only autosave recovery fallback
Add a no-KF5 Catch regression test that leaves a KAutoSaveFile-compatible backup behind from a child process, then verifies stale-file discovery, stale-lock recovery, reading, and cleanup.

Assisted-by: pi coding agent / Mika (OpenAI GPT-5.5)
2026-07-05 15:52:25 +02:00
Gerhard Schwanzer e6124e941a Add Qt-only autosave recovery fallback
Provide a small KAutoSaveFile-compatible implementation for the no-KF5 build path and use it to keep the existing crash-recovery code active when BUILD_WITH_KF5=OFF.

The normal KF5 build still uses the KDE KAutoSaveFile implementation.

Assisted-by: pi coding agent / Mika (OpenAI GPT-5.5)
2026-07-05 13:23:49 +02:00
Gerhard Schwanzer d0cea474a6 Fix Qt-only build without KF5
The BUILD_WITH_KF5 option was checked with DEFINED, so passing -DBUILD_WITH_KF5=OFF still entered the KF5 setup path.

Skip the KF5 setup when disabled and provide small Qt-only replacements for the KDE color widgets used by .ui files in that build mode.

Assisted-by: pi coding agent / Mika (OpenAI GPT-5.5)
2026-07-05 11:10:44 +02:00
Laurent Trinques 3c8ba1b1ca Update ChangeLog with narrative summary of post-0.100 changes
Add a hand-written "Version 0.101 (Unreleased)" section covering the
headless CLI export mode, PDF hyperlink cross-references, diagram
duplication, the Windows MSI versioning fix, the macOS file-open
regression fix, and expanded Korean translation support.

Add git-cliff config for changelog generation

Add cliff.toml, configured to group commits by keyword (fix/feat/
refactor/etc. in FR+EN) since the project history doesn't follow
Conventional Commits. Handles QET's tag format (X.Y / X.Y.Z, no v
prefix), excludes the floating "nightly" tag, dedupes repeated commit
messages per release, and truncates commit bodies to their summary
line to keep the generated changelog valid Markdown.

Usage:
export GITHUB_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxx
git-cliff --config cliff.toml <last_tag>..HEAD --prepend ChangeLog.md.
2026-07-04 11:09:49 +02:00
Laurent Trinques e19f523227 fix(windows-msi): inject rev into MSI Version Build field
Version was static (X.Y.Z.0) across nightlies, so MajorUpgrade never triggered the automatic uninstall; the Windows Installer only compares Major.Minor.Build, ignoring the 4th field.
2026-06-30 18:49:43 +02:00
Laurent Trinques 5dac26d9c7 Merge pull request #524 from ispyisail/fix/collection-unicode-path
Fix #391: collection panel blank when path contains accented chars or is too long (Windows)
2026-06-29 23:11:17 +02:00
Laurent Trinques e7873b40d5 git submodule update --remote pugixml/ 2026-06-27 22:49:23 +02:00
Laurent Trinques cd6c66c3c6 git submodule update --remote elements 2026-06-27 22:01:36 +02:00
Laurent Trinques ef04497ca6 Translate debug message to English 2026-06-27 19:41:28 +02:00
Laurent Trinques 58000283e6 Merge pull request #522 from ispyisail/fix/issue-283-table-center-alignment
fix(#283): restore center alignment when loading table config
2026-06-27 13:58:19 +02:00
Laurent Trinques 73b96dd1b8 Merge pull request #530 from vinicius-ap-santos/improve-ptbr-translation
Improve Brazilian Portuguese translation (pt_BR)
2026-06-24 06:10:57 +02:00
Vinícius Santos 682c6a0ccb Improve Brazilian Portuguese translation (pt_BR)
Fix 9 critical meaning errors, remove anglicisms, standardize
terminology and fill 81 previously untranslated strings.

Details:
- Nombre de phase: Nome da fase -> Numero de fases
- Ajouter un tableau: Ajustes da tabela -> Adicionar uma tabela
- Parcourir: Personalizado -> Navegar
- Desactive: Invalido -> Desativado
- Longueur: Largura -> Comprimento
- Variables de cartouche: Variaveis de armazenamento -> Variaveis do carimbo
- Aller a la correspondance: Ir para a aba -> Ir para a ocorrencia
- Creer de nouveaux folios: Cria um novo projeto -> Criar novas paginas
- Ponter/Deponter les bornes: verb form and meaning corrected
- Removed anglicism resetar -> Restaurar/Redefinir
- conducteur standardized to condutor (was mixed with fio)
- nomenclature standardized to nomenclatura (was lista de nomes)
- WiringListExport: entire feature translated
- TerminalNumberingDialog: entire dialog translated
- QETElementEditor: Mirror, Flip, Fine-Rotation translated
- TerminalEditor: NO/NC contacts -> NA/NF (Brazilian standard)
- Include compiled .qm file
2026-06-23 18:25:22 -03:00
Laurent Trinques d3cf8f2635 Remove github action test-vs2026.yml 2026-06-23 13:44:05 +02:00
plc-user 6aa14a7536 Merge pull request #518 from ispyisail/fix/terminal-data-leak
looks plausible, compiles fine

thank you @ispyisail
2026-06-23 09:39:46 +02:00
plc-user 6ce688a709 Merge pull request #520 from ispyisail/fix/element-editor-first-click-481
fixed:
view jumps, but graphic primitives stay at position
all parts can be moved as before
compile-warning is gone

thank you @ispyisail
2026-06-23 09:26:02 +02:00
Shane Ringrose 28357b0f55 fix(editor): correct initializer list order to silence -Wreorder warning
m_first_move was initialized before _linestyle in the constructor
initializer list, but _linestyle is declared first in the class. Reorder
to match declaration order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 07:04:34 +12:00
plc-user e21f49d2cf Merge pull request #528 from ispyisail/fix/project-props-dialog-modality
Merged as discussed in #527 
Thanks @ispyisail
2026-06-22 20:28:27 +02:00
Shane Ringrose 2a115e4381 fix(editor): suppress spurious first-click element moves (#481)
When an item type is selected for the first time the properties dock
expands, causing the QGraphicsView viewport to shrink. Qt recalculates
scene coordinates and fires one or more synthetic mouseMoveEvents before
the user has actually moved the mouse.

The original code used a single-shot m_first_move flag in
CustomElementGraphicPart, which absorbed exactly one spurious event.
PartText and PartDynamicTextField had no protection at all.

Fix: compare screen-coordinate displacement against
QApplication::startDragDistance() (~4 px). Screen coordinates are
stable across viewport resizes, so the check correctly rejects
synthetic dock-expansion events while allowing genuine drags.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 05:38:11 +12:00
Shane Ringrose bab32b3764 Fix #527 follow-up: use ApplicationModal for app config dialog (configureQET)
Same root cause as ProjectPropertiesDialog: Qt::WindowModal only blocks the
direct parent window, leaving the rest of the MDI area live.  If new_project
or close_project fires while the app settings dialog is open, any raw pointers
derived from the project list become stale.  Switch to ApplicationModal to
block all windows for the duration of the dialog.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 17:48:19 +12:00
Shane Ringrose 5cc2e165bf Fix #527: use ApplicationModal for Project Properties to prevent SIGSEGV
ProjectPropertiesDialog::exec() was using Qt::WindowModal, which only
blocks the parent ProjectView window.  The MDI workspace and its other
subwindows remained interactive, so actions like new_project or
close_project could fire while the dialog's config pages still held raw
QETProject* pointers — leading to a SIGSEGV when Qt's event loop later
dispatched a signal through one of those stale pointers.

Detected by the 8-hour GUI fuzzer: action sequence add_diagram_page →
flood_wires ×18 → new_project while Project Properties was open
produced exit code -11 (SIGSEGV) on the first of 12,717 actions.

Switch to Qt::ApplicationModal so no window can receive input while the
dialog is open.  Project Properties is a short-lived dialog; blocking
the whole application for its duration matches user expectation and
removes the lifetime hazard without requiring QPointer surgery across
four config-page classes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 17:30:20 +12:00
scorpio810 1c764babd1 macOS: buffer QFileOpenEvent during cold launch before QETApp exists
Follow-up to the eventFilter fix: testing showed double-click works
when QET is already running, but a cold launch (app not running yet)
still opens an empty window. Finder/Launch Services can deliver the
QFileOpenEvent to the QApplication's native event loop before main()
reaches the point where QETApp is constructed and its real eventFilter
is installed -- there's a window between 'SingleApplication app(...)'
and 'QETApp qetapp;' during which the event can arrive and be lost.

Add a minimal EarlyFileOpenCatcher installed on app immediately after
it's constructed (before anything else can run an event loop). It only
buffers the file path. Once QETApp exists, main() swaps it out for the
real QETApp::eventFilter and drains anything that was buffered via
qetapp.openFiles(), so no cold-launch QFileOpenEvent is silently
dropped.

Confirmed by manual testing:
- 'open app --args file' on .qet/.elmt/.titleblock: OK (already worked)
- double-click while app already running: OK (already worked)
- double-click cold launch: previously opened an empty window, this
  buffers and replays the event so it now opens the right editor.
2026-06-21 13:04:07 +02:00
scorpio810 8d76354647 macOS: fix QFileOpenEvent routing for .qet/.elmt/.titleblock double-click
Root cause (see issue #218 discussion):
- QETApp::eventFiltrer (Q_OS_DARWIN) was correct in intent but never
  actually installed as an event filter anywhere, and its name didn't
  match the QObject::eventFilter virtual signature, so even if it had
  been installed it would never have been invoked as an override.
  Confirmed dead code.
- main.cpp instead routed Finder's QFileOpenEvent through
  MacOSXOpenEvent -> SingleApplication::sendMessage(), which is the
  secondary-to-primary IPC channel. Called from within the primary
  instance itself, sendMessage() fails silently and the file path is
  dropped, which is exactly what double-click does (Finder doesn't spawn
  a secondary process, the running instance receives the FileOpen event
  directly).
- openFiles() takes a QETArguments, not a QStringList. The dead code's
  openFiles(QStringList() << filename) only worked via an untested
  implicit QStringList -> QList<QString> -> QETArguments conversion
  chain.

Fix:
- Rename eventFiltrer -> eventFilter (qetapp.h/.cpp) so it's a proper
  override of QObject::eventFilter, and make it public so main.cpp can
  install it on QApplication.
- Build the QETArguments explicitly instead of relying on implicit
  conversion.
- main.cpp: drop MacOSXOpenEvent entirely (include + instantiation),
  install qetapp as the Q_OS_MACOS event filter on app right after
  QETApp qetapp; is constructed and before app.exec(). No race window:
  the event loop hasn't started, so no QFileOpenEvent can be delivered
  before the filter is installed.

QETArguments::handleFileArgument() already sorts files by extension
(.elmt, titleblock, else project) and openFiles() already fans out to
openProjectFiles()/openElementFiles()/openTitleBlockTemplateFiles()
accordingly, so this single fix covers .qet, .elmt and .titleblock
double-click/drag-to-dock on macOS, not just .qet.

SingleApplication's sendMessage/receivedMessage flow (used for CLI args
on all platforms) is untouched; this only touches the Q_OS_MACOS block,
so there is no behavior change on Windows/Linux.

Follow-up (not included here): the macOS Info.plist (misc/Info.plist)
has an empty CFBundleShortVersionString, which may affect macOS's
willingness to retain file associations across app updates.
2026-06-21 12:10:13 +02:00
Shane Ringrose 31edf30c61 Fix #391: use wide-char path for pugixml on Windows to handle Unicode paths
pugi::xml_document::load_file(const char*) calls fopen/fopen_s on Windows,
which uses the ANSI codepage — not UTF-8. This silently fails when the
collection path contains accented characters (é, ü, ñ, …) or is longer
than the narrow-API MAX_PATH limit, leaving the collection panel with no
element names or illustrations.

Switch both call sites to toStdWString().c_str() which invokes the
load_file(const wchar_t*) overload. On Windows pugixml calls _wfopen,
the wide Unicode API that handles all valid Unicode paths. On Linux/macOS
the same overload converts wchar_t to UTF-8 internally and calls fopen,
so behaviour is unchanged on those platforms.

Affected files:
  sources/ElementsCollection/fileelementcollectionitem.cpp  (qet_directory load)
  sources/ElementsCollection/elementslocation.cpp           (element .elmt load, both branches)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 22:08:11 +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
Shane Ringrose f55ba568f6 fix(#283): restore center alignment when loading table config
saveConfig() serialises Qt::AlignHCenter as the string "AlignHCenter"
via QMetaEnum::valueToKey(), but loadConfig() matched against
Qt::AlignCenter (0x0084 = AlignHCenter|AlignVCenter) instead of
Qt::AlignHCenter (0x0004).  The two values differ, so the switch fell
through to the default (Right) every time center was saved and reloaded.

Add Qt::AlignHCenter as the primary case and keep Qt::AlignCenter as a
fallthrough for any config files that were hand-edited by users following
the workaround documented in issue #283.

Verified with a standalone Qt test: QMetaEnum::keyToValue("AlignHCenter")
returns 4 (AlignHCenter), which now correctly resolves to combobox index 1
(Center) instead of 2 (Right).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 20:41:25 +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
Shane Ringrose 8a837a11d4 Fix four memory leaks found by AddressSanitizer
- StyleEditor: QGridLayout(this) pre-empted the widget's layout slot,
  causing setLayout(main_layout) to silently fail and orphan main_layout.
  Fix: use QGridLayout() without a parent so setLayout() succeeds.

- ExportDialog: ~ExportDialog() was empty, leaving ExportDiagramLine
  heap objects in diagram_lines_ unfreed. Fix: qDeleteAll(diagram_lines_).

- GenericPanel::getItemForDiagram: when called without the bool* created
  arg, it created a parentless QTreeWidgetItem that callers immediately
  discarded. Fix: return nullptr when created==nullptr and item not found
  (all callers already guard with if (item)).

- ElementScene: m_paste_area (created in initPasteArea) was temporarily
  added/removed from the scene during XML loading but never freed in the
  destructor. Fix: delete it if not currently in the scene.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 01:45:07 +12:00
Shane Ringrose df5f622418 Fix TerminalData memory leak in Terminal destructor
Terminal stores its TerminalData* as member d but never deletes it.
Every Element creation (placing on diagram, loading icon for the
element browser, drag previews) leaks one TerminalData per terminal.
ASan confirmed 112 leaked objects (9856 bytes) in a short session
across four call sites all rooted in Element::parseTerminal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 00:38:07 +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