Commit Graph

8629 Commits

Author SHA1 Message Date
Laurent Trinques 88962570a8 Merge pull request #569 from DieterMayerOSS/pr/parttext-alignment
Element editor: optional alignment for static texts
2026-07-28 17:45:43 +02:00
Laurent Trinques f3e8cce6c8 Merge pull request #567 from DieterMayerOSS/pr/factory-drop-pugixml-pass
ElementFactory: read link_type from the cached QDom, drop the pugixml pass
2026-07-28 17:43:01 +02:00
Laurent Trinques ea846cb24e Merge pull request #566 from DieterMayerOSS/pr/hash-contains
Replace hash.keys().contains(k) with hash.contains(k) (35 call sites)
2026-07-28 17:41:23 +02:00
Laurent Trinques ca8d907e70 Merge pull request #570 from DieterMayerOSS/pr/xmlcollection-linear-lookup
Fix the Qt6 load-time regression: linear child lookup in XmlElementCollection
2026-07-28 17:39:59 +02:00
Laurent Trinques b52b1d5984 Merge pull request #565 from DieterMayerOSS/pr/qt6-partslist-printdialog
Restore two more Qt5-only code paths on Qt6 (parts list, print dialog)
2026-07-28 17:38:44 +02:00
Laurent Trinques f854fe0553 Merge pull request #564 from DieterMayerOSS/pr/restore-qt6-autosave
Restore crash-recovery autosave on Qt6
2026-07-28 17:37:07 +02:00
Laurent Trinques db759344fd Merge pull request #563 from DieterMayerOSS/pr/log-handler-before-startup
Install the log message handler before the application starts
2026-07-28 17:35:11 +02:00
Laurent Trinques e7dfa4bc64 Merge pull request #568 from DieterMayerOSS/pr/qdom-invaliddata-policy
Harden QDom serialization against invalid data (CVE-2026-15037)
2026-07-28 17:33:43 +02:00
Dieter Mayer de82b2738f XmlElementCollection::child(): linear sibling walk instead of item(i)
The child lookup iterated parent_element.childNodes() via item(i), and
QDomNodeList::item() walks the sibling chain from the start on every
call - making the loop quadratic in the number of children, with an
extra QList allocation and a second pass on top. This lookup runs
several times per element instance while loading a project, against the
"import" category that holds every embedded definition, so the cost
scales with (instances x embedded definitions).

Replace it with a firstChildElement()/nextSiblingElement() walk with an
early return. Same semantics (first tag+name match in document order).

Measured on the Kaefer_1303 reference project (3.9 MB, 23 folios,
432 instances, media of 6 runs, Windows/MinGW, same GCC for both):

           before      after
  Qt5      5.116 s     5.068 s
  Qt6      6.683 s     4.640 s   (-31 %)

This removes the entire Qt6 load-time regression discussed in #553 -
Qt6 goes from +31 % slower to 8 % faster than Qt5 on the very project
that exposed it (Qt6''s QDom makes the quadratic pattern much more
expensive than Qt5''s did). Smaller projects gain too (3.4 MB example:
-9 % on Qt6).

(cherry picked from commit 0d4ef8eca27601c37ba2b75d7c058e9d8e2beea4)
2026-07-28 14:08:27 +02:00
Dieter Mayer 787335582b Element editor: optional alignment for static texts (#549)
Static texts (PartText) gain an optional alignment, exposed via the
existing AlignmentTextDialog behind a new "Alignement" button in the
static text editor:

- The horizontal part aligns the lines of a multi-line text relative
  to each other (centered block labels no longer need one hand-placed
  text per line).
- The full alignment defines the anchor: when the content or font
  changes later, the selected corner/center of the bounding rect keeps
  its place instead of always growing right/down from the top-left
  (same prepareAlignment/finishAlignment logic as DiagramTextItem).

Format: the <text> node takes the same optional Halignment/Valignment
attributes as dynamic_text, written only when they differ from the
historical top-left behaviour - existing .elmt files are untouched and
round-trip byte-identical. The saved x/y stay the baseline-left of the
text block in all cases; ElementPictureFactory only needs the line
alignment (the anchor is editor-side behaviour), so rendered elements
match the editor exactly.

German translations for the three new strings included (qet_de stays
complete, 2686/2686).

Verified headless: a project embedding a two-line text once with
Halignment=AlignHCenter and once without exports to SVG with the short
line centered under the long one (x 102.5 vs 126.5) in the aligned
block, and identical x for both lines in the legacy block. Editor-side
anchor behaviour follows the proven DiagramTextItem implementation but
was not manually exercised in the GUI yet.
2026-07-28 13:58:51 +02:00
Dieter Mayer 1caa8920ae Harden QDom serialization against invalid data (CVE-2026-15037)
Set QDomImplementation::setInvalidDataPolicy(ReturnNullNode) at startup.
Qt before 6.12 defaults to accepting invalid data when building QDom
nodes, so untrusted text inserted into comments, CDATA sections or
processing instructions could break out of its node on serialization
(XML injection, low severity). Qt 6.12 flips the default to
ReturnNullNode; opting in explicitly gives the same behavior on any
Qt 5/6 version, so no version guard is needed.

Verified: --resave of a 5-folio project produces valid XML with all
folios intact, and --export-pdf still works on the result.

(cherry picked from commit 7804ef3864a0b8643cfe13b68b8e0e6235508ab6)
2026-07-28 13:58:34 +02:00
Dieter Mayer 139c47d1ce ElementFactory: read link_type from the cached QDom, drop pugixml pass
createElement() ran a full pugixml parse of the element definition for
every instance, only to read the link_type attribute for the subclass
dispatch - on a big example project (191 instances) 49 ms of the load,
measured with temporary instrumentation. The QDom definition is already
cached and the ctor uses it anyway; reading the attribute there costs
~17 ms in total, so the net win is ~30 ms - within run-to-run noise
end-to-end, but it removes an entire redundant parser pass per element.

Behavior unchanged: an absent and an empty link_type both fell through
to SimpleElement before and still do.

(cherry picked from commit d63275971f558c8ac59f35bf4d13d7e424682342)
2026-07-28 13:58:33 +02:00
Dieter Mayer 96f6fa44ad Replace hash.keys().contains(k) with hash.contains(k) (35 call sites)
QHash/QMap::keys() allocates a list of every key on each call, then
contains() searches it linearly - an accidental O(n) plus allocation
where a direct O(1) lookup was meant. 35 occurrences across 7 files,
found while profiling project load times (context: #553/#560).

The hot one is ElementPictureFactory::getPictures(), which runs once
per element instance on project load: on the 3399 KiB example project
(191 instances, 129 cache hits) the keys() detour cost 45 ms of the
1.34 s total - measured, not estimated; the fix reproducibly shaves
~35-45 ms off that load. The remaining call sites are UI paths
(search&replace, dynamic text model, undo commands) where the waste
scales with selection/model size.

No behavior change: for QHash/QMap, keys().contains(k) and
contains(k) are equivalent by definition.

(cherry picked from commit 0a7f8f072fa68de7c01a9fc134a4bc8e16d62062)
2026-07-28 13:58:33 +02:00
Dieter Mayer 4f7340691a Restore two more Qt5-only code paths on Qt6 (parts list, print dialog)
Two further empty Qt6 guard branches found by ispyisail in
qelectrotech#553:

- Element editor parts list: the QGraphicsItem* was only stored into
  the list item on Qt5, so on Qt6 selecting a part in the list silently
  stopped selecting it on the canvas. QVariant::fromValue() works on
  both (Qt itself declares the metatype), guard removed.

- Print dialog: setEnabledOptions() is a Qt4-era API removed in Qt6;
  setOptions() is the modern spelling with the same replace-the-set
  semantics and exists on both, guard removed.

Both builds (Qt 5.15.2 and Qt 6.11.1) compile clean.

(cherry picked from commit 759d1c078e23664482850bad2e91d668b93aadcf)
2026-07-28 13:58:32 +02:00
Dieter Mayer 6d1b4cfa61 Restore crash-recovery autosave on Qt6
QETProject::writeBackup() was Qt5-only: the Qt6 branch of the guard was
an empty placeholder, so on Qt6 no backup was ever written - a silent
data-loss risk (a crash loses everything since the last manual save),
found by ispyisail in qelectrotech#553.

The Qt5-style QtConcurrent::run(function, reference-args) call did not
survive the Qt6 API change; a lambda capturing the (implicitly shared)
document copy behaves identically on both, so the version guard goes
away entirely.

Verified at runtime on the Qt6/Windows build: opening a project creates
the autosave triple (.qetautosave + .lock + .path) with valid XML
content, and a clean exit removes it again. The CLI keeps backups
disabled via setBackupEnabled(false), unchanged.

(cherry picked from commit 0f65ae8c4b2782fbfe97111bc8b369cc105ec592)
2026-07-28 13:58:31 +02:00
Dieter Mayer 9a13fef959 Install the log message handler before the application starts
qInstallMessageHandler() ran inside the startup worker thread, which is
scheduled after QETApp construction - but QETApp's constructor performs
the entire startup (collections, editor, opening projects passed on the
command line). Everything logged during that window went to the default
handler, i.e. stderr, which is invisible in a Windows GUI session: the
daily log file ended right after the machine-info block, and exactly
the interesting lines - the elements-collection timer and the project
load timer from #560 - never reached it.

Install the handler synchronously before SingleApplication instead;
the worker keeps the old-log cleanup and machine-info dump. The CLI
path returns earlier and intentionally keeps plain stderr logging.

Verified: a GUI session now logs the full timeline, e.g.
  12:37:03.623 Elements collection reload
  12:37:03.776 ... finished to be loaded in 0.151 seconds
  12:37:04.744 Project "..." (726 KiB) opened in 1.583 seconds
(cherry picked from commit 72b1a1d9ec2e3fd3787224759797978d710880bd)
2026-07-28 13:58:30 +02:00
Laurent Trinques 66129fd15c Merge pull request #560 from ispyisail/feature/project-load-timing
Log project load times, split by phase
2026-07-27 17:15:02 +02:00
ispyisail 9e070f9bce Log project load times, split by phase
Requested in #553 to compare Qt5 and Qt6 builds. QET already reports how
long the elements collection takes to load (ElementsCollectionWidget::
reload); this adds the equivalent for opening a project.

The phases are reported separately rather than as a single total. Reading
the XML and building the objects is mostly independent of the Qt version,
whereas refreshing the diagrams is graphics-scene work -- a single number
would mix the two and could suggest a Qt version makes no difference when
the part that changed is simply not where the time goes. Measured on the
example projects, XML parsing is 3-7% of the total and diagram
construction 77-83%, so the distinction matters in practice.

QETProject::openFile() reports the total with the parse/build split, and
readProjectXml() reports the build phases:

  Project content built in 1.391 seconds (elements collection 0.009,
    diagrams 1.153, terminal strips 0, refresh 0.196, database 0.033)
  Project "example.qet" (3399 KiB) opened in 1.505 seconds
    (xml parsing 0.11, content 1.395)

Logged with qInfo(), matching the existing collection timer, so it lands
in the normal log without a debug build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 17:37:49 +12:00
plc-user 0608a585d6 update binary-translation-files for FR and DE 2026-07-26 18:52:08 +02:00
Laurent Trinques baf294251f Merge pull request #557 from ispyisail/fix/qmake-build-without-kf5
qmake: allow building without KDE Frameworks 5 (CONFIG+=no_kf5)
2026-07-26 18:28:50 +02:00
Laurent Trinques 80d3b17358 Update en translations files 2026-07-26 17:05:02 +02:00
Laurent Trinques 5aef8fcca5 git submodule update --remote elements 2026-07-26 16:44:39 +02:00
Laurent Trinques 72a6534a73 Update translations files 2026-07-26 16:44:02 +02:00
Laurent Trinques 0263fed6cf Merge pull request #558 from ispyisail/fix/clear-broken-stylesheet
Clear the application stylesheet when using system colors
2026-07-26 15:32:11 +02:00
Laurent Trinques 9cbd9b429c Merge pull request #559 from ispyisail/fix/554-resolve-data-paths-from-binary
Fix language (and data paths) when opening a .qet by double-click on Windows
2026-07-26 15:00:55 +02:00
Laurent Trinques c3d511cdcd Merge pull request #555 from ispyisail/feature/edz-translatable-strings
edz: make EdzArchive error strings translatable (+ German and French)
2026-07-26 14:45:43 +02:00
ispyisail 3ffa0194c7 cmake: Windows translations live in lang/, not l10n/
QET_LANG_PATH was "l10n/" for WIN32, a value that appears nowhere else in
the tree: the MSI shortcuts pass --lang-dir="[INSTALLDIR]lang/", and the
windows-build workflow copies the .qm files into files/lang/. So the
compiled-in default pointed at a directory no packaging creates, leaving
the command-line argument to do all the work.

Align it with what is actually shipped, so the binary-relative lookup
added in the previous commit can find the translations on its own.
2026-07-26 22:41:44 +12:00
ispyisail d4ec9f9c65 Resolve relative compiled-in data paths from the binary, not the CWD
commonElementsDir(), commonTitleBlockTemplatesDir() and languagesPath()
return the compile-time path verbatim when it is not marked
*_RELATIVE_TO_BINARY_PATH. On Windows those paths are relative
("./elements/", "./titleblocks/", "./lang/"), so they resolve against the
process working directory.

That only holds when QET is started from its own installation folder.
Opening a document from a file manager sets the working directory to the
document's folder, and the data is then looked for next to the user's
file. The shortcuts hide this by passing --common-elements-dir,
--common-tbt-dir and --lang-dir explicitly; anything that launches the
binary without them does not (see #554).

Add resolveConfiguredDataPath(): absolute paths are returned unchanged,
and a relative one is tried against the working directory first (so any
setup relying on the old behaviour keeps working), then next to the
executable, then in its parent -- the layout used by the Windows
packaging, where the binary sits in bin/ with the data beside it. This is
the same fallback the no-compile-option branch already performs for
issue #86, which was unreachable whenever the compile option is set.

The *_RELATIVE_TO_BINARY_PATH defines are left alone; they are only set
for macOS in qelectrotech.pro, and the CMake guard that would set them
tests a variable that is never defined.
2026-07-26 22:41:44 +12:00
ispyisail 69c595fdb8 Windows installer: pass the data directories to the .qet file association
The MSI registers both shortcuts with the arguments QET needs to find its
data ("Point directly to qelectrotech.exe with all required arguments"),
but the QElectroTech.Document\shell\open\command registry value was
written without them:

    "[INSTALLDIR]bin\qelectrotech.exe" "%1"

Launching from the Start Menu therefore works, while double-clicking a
.qet file does not: Explorer sets the working directory to the document's
folder, and the compiled-in data paths are relative, so nothing is found
there. The most visible symptom is the interface always coming up in
French, because no translation loads and the source strings are French.

Give the file association the same arguments as the shortcuts.

Reported by mr-rfh in #554, who diagnosed it and arrived at exactly this
registry value by hand.
2026-07-26 22:41:44 +12:00
ispyisail b688baf3b6 Clear the application stylesheet when using system colors
QETApp::useSystemPalette(true) installed a one-rule application stylesheet
whose only declaration was invalid CSS:

    QAbstractScrollArea#mdiarea {
        background-color -> setPalette(initial_palette_);
    }

That is not a CSS declaration but a note-to-self, committed in e6c32bc0
("Background set to use System Palette", 2014) when a hardcoded

        background-color:#D5D2D1;

was replaced with a reminder to derive the color from the palette instead.
Qt's CSS parser silently skips invalid declarations, and at the time the
same rule still carried valid background-image/-repeat/-position
properties, so the block kept working and nothing looked wrong. Those
properties were dropped later, leaving a rule with no valid declarations
at all.

The rule has therefore styled nothing for some time. It is not harmless
though: a non-empty application stylesheet wraps every widget in
QStyleSheetStyle, which overrides per-widget QWidget::setStyle(). QET
does not currently call QWidget::setStyle() anywhere, so nothing is
visibly broken today, but it blocks that API for future work — it was
found while prototyping a palette-based dark mode (see #553).

Replace it with an explicit setStyleSheet(QString()). The behavior of the
"use system colors" branch is unchanged: it already dropped whatever
style.css had loaded (by overwriting it with the inert rule), and the
qApp->setPalette(initial_palette_) call on the line above is what actually
supplies the system colors — which is what the 2014 note was asking for.

The style.css path (use == false) is untouched.

Reported by DieterMayerOSS in #553.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 22:28:32 +12:00
ispyisail 781bde88a6 qmake: allow building without KDE Frameworks 5 (CONFIG+=no_kf5)
The CMake build has supported building without KF5 since the Qt-only
replacements landed in sources/ui/nokde (BUILD_WITH_KF5=OFF). The qmake
build had no equivalent: qelectrotech.pro hard-coded

    QT += ... KWidgetsAddons KCoreAddons ...

so a machine without KF5 fails at configure time with

    Project ERROR: Unknown module(s) in QT: KWidgetsAddons KCoreAddons

This is the error reported in discussion #393, where a contributor gave up
trying to build on Windows with Qt 5.15.2 / MinGW. Deleting the two modules
by hand (the obvious workaround) then fails at link time with undefined
references to KAutoSaveFile, because the nokde sources are never compiled.

Mirror the four things CMake does when BUILD_WITH_KF5=OFF:

  - define BUILD_WITHOUT_KF5 (the sources already guard on it)
  - add sources/ui/nokde to INCLUDEPATH
  - compile the three nokde replacements (KAutoSaveFile, KColorButton,
    KColorCombo)
  - drop KWidgetsAddons/KCoreAddons from QT

The default build is unchanged: without CONFIG+=no_kf5 the KF5 modules are
still required and sources/ui/nokde is not on the include path.

Usage:

    qmake CONFIG+=no_kf5 qelectrotech.pro

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 22:12:56 +12:00
ispyisail 7cb9695962 edz: make EdzArchive error strings translatable (+ DE/FR)
The six user-facing error strings in EdzArchive::extract() were hard-coded
QStringLiteral, so they could not be translated (reported by plc-user in
PR #513).

Wrap them in tr() via Q_DECLARE_TR_FUNCTIONS — EdzArchive is not a QObject,
so this gives it its own translation context without pulling in a moc
dependency. The *.part.xml glob stays a QStringLiteral: it is a filename
pattern, not user-facing text.

Adds German and French translations for all six. German wording for
"Cannot read %1" is plc-user's ("Kann %1 nicht lesen.").

Note for review: the rest of QET uses French source strings translated to
en/de in the .ts files, whereas these strings are English in the source
(as merged in PR #513). This commit keeps them English rather than
rewriting strings already reviewed; happy to flip them to French sources
for consistency if preferred.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 21:04:25 +12:00
Laurent Trinques e9a53dbec2 git submodule update --remote elements 2026-07-26 05:44:21 +02:00
Laurent Trinques f9f03c79ea Skip signing the Qt6 MSI, and guard against triggering a signing request from a fork (no SignPath secrets there).
cf. DieterMayerOSS:fix/msi-signing-fork-guard (d3f60c88)
2026-07-24 10:20:01 +02:00
plc-user 7247257a9b add German translations 2026-07-23 19:51:55 +02:00
Laurent Trinques a8c4612fd4 Update translations files and git submodule update --remote elements 2026-07-23 14:08:11 +02:00
Laurent Trinques d02514141c Update qelectrotech.pro for https://github.com/qelectrotech/qelectrotech-source-mirror/pull/513 2026-07-23 14:02:26 +02:00
Laurent Trinques 42d2c824d2 Merge pull request #513 from ispyisail/feature/edz-import
Import EPLAN Data Portal parts (.edz) into element collections
2026-07-23 13:58:56 +02:00
Laurent Trinques 324c818007 Merge pull request #551 from Kellermorph/master
German Translation
2026-07-20 20:17:13 +02:00
Kellermorph 0a4b337115 German Translation 2026-07-20 20:15:28 +02:00
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
Laurent Trinques adb3473a2c Improve Windows Qt5/Qt6 CI test, enable -platform windows:fontengine=freetype add new .bat for Qt6 installer and portable version 2026-07-19 21:15:13 +02:00
Laurent Trinques 3c1eb9b970 New Windows Qt5/Qt6 CI test, enable -platform windows:fontengine=freetype add new .bat for Qt6 installer and portable version 2026-07-19 19:26:25 +02:00
Laurent Trinques fd402bb9b5 Update windows-msi.yml 2026-07-19 19:00:06 +02:00
Laurent Trinques 8e736c59ba New Windows Qt5/Qt6 CI test, enable -platform windows:fontengine=freetype 2026-07-19 18:55:26 +02:00
Laurent Trinques 6941edcdf5 New Windows Qt5/Qt6 CI test 2026-07-19 16:48:42 +02:00
Laurent Trinques e1aa65f1ee Revert "Set QET version to 0.200.1 when built with Qt6"
This reverts commit 608ca984a4.
2026-07-19 13:26:34 +02:00
scorpio810 629bd7cdef Add SQLite3::SQLite3 compat alias, use modern target name
CMake's bundled FindSQLite3 module only creates the SQLite3::SQLite3
target since CMake 4.3; earlier versions (still used by most current
Linux distros) only provide SQLite::SQLite3, which CMake >= 4.3 now
flags as deprecated.

Add the missing alias ourselves right after find_package(SQLite3)
when it isn't already provided, so the project can link against the
modern SQLite3::SQLite3 name on every supported CMake version without
triggering the deprecation warning on newer ones.
2026-07-19 12:30:19 +02:00
Laurent Trinques 88e42a435b Merge pull request #519 from ispyisail/fix/asan-memory-leaks
Fix four memory leaks found by AddressSanitizer
2026-07-19 11:51:47 +02:00
Laurent Trinques 100e924aed Fix: Windows Qt6 CI test 2026-07-19 11:26:19 +02:00