Commit Graph

9518 Commits

Author SHA1 Message Date
Andre Rummler bb037ee3ff Fix: when pasting the picture or graphics object is nowunder the top left corner instead far away. 2026-09-25 00:24:15 +02:00
Andre Rummler d1256872c2 Fix transformation order. 2026-09-24 15:21:42 +02:00
Andre Rummler 522e008c26 Fix rotation of inserted picture. 2026-09-24 13:37:06 +02:00
Laurent Trinques 7fbbb71aac Merge pull request #1018 from ispyisail/revive/785-info-flags-case-insensitive
Fix element flags written as True or 1 showing unticked
2026-09-24 13:08:28 +02:00
Laurent Trinques 232724bf82 Merge pull request #1017 from ispyisail/revive/752-rotate-texts-no-dialog-in-ctor
Move the rotate-texts dialog out of its undo command
2026-09-24 13:06:23 +02:00
Laurent Trinques 12f826e969 Merge pull request #1016 from ispyisail/revive/729-backup-prompt-drops-files
Fix projects opened during the start-up restore prompt being lost
2026-09-24 13:04:19 +02:00
Laurent Trinques 245aafb0d1 Merge pull request #1015 from ispyisail/revive/526-free-terminal-move
Fix the terminal strip's move button getting stuck disabled (#409)
2026-09-24 13:02:43 +02:00
Laurent Trinques 0d231ef548 Merge pull request #1014 from arummler/fix-explicit-file-open
Fix warning about reading XML without explicit file opening.
2026-09-24 13:00:47 +02:00
ispyisail 3513f692fd Read the remaining element flags through QET::infoFlagIsTrue()
5b0785fcc routed most reads of auto_num_locked, potential_isolating and
exclude_from_bom through QET::infoFlagIsTrue(), which accepts the same
spellings as the parts-list query (true/1/yes/on, trimmed, any case).
Four checkbox reads still compared against a literal lowercase "true":
the "exclude from parts list" box in the folio properties panel, and all
three flags in the element editor.

A value such as "True" or "1" from a hand-written .elmt/.qet was
therefore left out of the parts list, but shown unticked in both panels,
and pressing Apply there wrote back "false" and flipped the flag.

Revives the unconverted part of PR #785.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 22:34:42 +12:00
Laurent Trinques 5a27f3cf80 Merge pull request #1013 from ispyisail/fix/273-skip-open-backup
Fix bugtracker #273: skip crash-recovery backups of an unchanged project
2026-09-24 12:33:18 +02:00
Andre Rummler d3a9adbe44 Fix warning about reading XML without explicit file opening. This was implicit up to now and will go away in future Qt versions. 2026-09-24 12:32:47 +02:00
ispyisail 8ab4e24b61 Take the modal dialog out of RotateTextsCommand's constructor
RotateTextsCommand called QDialog::exec() from inside its constructor, so
the command could not be built without a human answering a dialog. That
made it untestable headlessly, undrivable from any script or test harness,
and it is why bugtracker #312 (PR #707) shipped with its save/reload
round-trip unverified -- the symptom could not be reproduced without a GUI.

The command now takes the angle as a parameter and does no asking. Two
statics carry the interactive half:

  hasSelectedTexts(diagram)  -- is there anything to rotate
  askRotation(rotation)      -- open the dialog, false if cancelled

The single call site in QETDiagramEditor asks first, then builds the
command, so the user-visible behaviour is unchanged: same dialog, same
title, same no-dialog-on-empty-selection. Keeping askRotation() in this
class also keeps the QObject tr() context, so existing translations of
"Orienter les textes sélectionnés" are not invalidated.

Also guards undo()/redo() against a null m_anim_group. When nothing is
selected the constructor calls setObsolete(true) without ever creating the
animation group, and QUndoStack::push() calls redo() before discarding an
obsolete command -- a latent null dereference on that path.

Verified headlessly, which was the point: driving the command through a
scratch --test-ops op on examples/741.qet (67 conductors), rotation
attributes written on save go 0 -> 67 with the #707 fix present and stay
at 0 with it reverted, while the reverted build instead writes userx on
all 67. That is bugtracker #312 reproduced and fixed under test for the
first time.
2026-09-24 22:30:20 +12:00
Laurent Trinques 2d2bf5ca2b Merge pull request #1012 from ispyisail/fix/340-ms-shell-dlg-pdf-font
Fix bugtracker #340: Windows texts in 'MS Shell Dlg 2' render as Arial
2026-09-24 12:27:50 +02:00
ispyisail 2f6ab8f63b Don't drop files handed to a starting instance while the backup prompts are up
QETApp's constructor ends with checkBackupFiles(), which opens modal
dialogs -- the "restore these files?" prompt, and the crash report offer.
Those dialogs run their own event loop, so the constructor does not return
until the user answers them.

main() still has work to do at that point. In particular this, a few lines
later:

    QObject::connect(&app, &SingleApplication::receivedMessage,
                     &qetapp, &QETApp::receiveMessage);

While the prompts are up that connection does not exist yet. A second
instance launched during the window -- double-clicking a project, or
xdg-open, while the first copy is still asking about restore files --
hands its file names over, SingleApplication accepts and delivers them,
and nothing is listening. The message is discarded and the second process
has already exited, so the file is simply lost with no error.

Deferring checkBackupFiles() to the event loop lets the constructor return
promptly. main() finishes wiring up, and the prompts appear immediately
afterwards exactly as before.

Verified with a stale restore file present, sending a project to the
running instance while the restore prompt is displayed: before, the file
was dropped and never appeared, even after answering the prompt; after, it
opens. The restore and backup prompts still appear and still work.

Note this is only observable together with the fix for bugtracker #248 --
before that, no file passed to a running instance was opened under any
circumstances.
2026-09-24 22:26:38 +12:00
ispyisail 2266497b0b Write the move button tooltips in French, the tr() source language
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 22:20:43 +12:00
Shane Ringrose dcb79b6553 fix(terminal-strip): free-terminal move button disabled and unresponsive
Three bugs stacked to produce the symptom in issue #409 (move button does
nothing):

1. selectionChanged() was declared in freeterminaleditor.h but had no body
   and was never connected to the selection model, so the move button had no
   awareness of whether a terminal was selected. The button could appear
   enabled with nothing selected, then silently return early in
   on_m_move_pb_clicked() at the real_t_vector.isEmpty() guard.

2. The dataChanged→setDisabledMove(true) connection was a one-way trap: any
   cell edit (including accidentally opening and closing a type/function
   combo, or toggling LED back to its current value) permanently disabled the
   move button until reload() was called. There was no tooltip explaining why
   the button was greyed out, so the user had no way to recover.

3. FreeTerminalModel::setData() for LED_CELL had no change guard, unlike
   LABEL_CELL which checks label != value. Clicking the LED combo while it
   was already at the same value still emitted dataChanged and triggered the
   disable.

Fix:
- Implement selectionChanged() to enable the move controls only when at
  least one row is selected AND there are no pending (yellow) edits.
- Connect it to both selectionModel::selectionChanged and model::dataChanged
  so the button state is always consistent with actual UI state.
- Route reload()'s re-enable through selectionChanged() instead of calling
  setDisabledMove(false) directly, so the selection state is respected
  immediately after a move.
- Add a tooltip to m_move_pb explaining the disabled state.
- Add the missing change guard to LED_CELL in setData().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-24 22:20:43 +12:00
ispyisail 745b4a936c Fix bugtracker #273: skip crash-recovery backups of an unchanged project
writeBackup() rebuilds the whole project's XML with toXml() on the GUI
thread before handing the file write to a worker thread. On a big project
that freezes the interface for seconds (bugtracker #273, and #329, where
the interval was raised from 2 to 20 minutes to make it rarer). It ran
right after every project was opened, and then every 20 minutes whether
or not anything had changed.

Only write a backup when something changed since the last one: the undo
stack moved, setModified(true) was called, or the embedded element or
title block collections changed. A project just opened from a file starts
clean, since the file is already what a crash would restore. New projects
and projects restored from a backup are backed up at once, as before.

Measured on examples/industrial.qet (50 folios), each backup blocks the
GUI thread for about 0.28 s on a fast machine; with a 2 s test interval,
the old code backed up on every tick, the new code only after a change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 21:57:42 +12:00
ispyisail 7ac3719c37 Fix bugtracker #340: map "MS Shell Dlg" aliases on Qt 6/Windows
"MS Shell Dlg 2" is a Windows font alias, not a font, and projects and
settings saved on Windows carry it. Qt 5's GDI backend let Windows resolve
it to Tahoma; Qt 6's DirectWrite backend does not know the alias and falls
back to Arial, so those texts render heavier on screen and in exported PDFs.

Register the substitutions Windows itself uses, before any application
object exists so the headless export and scripting paths get them too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 21:41:00 +12:00
Laurent Trinques ddfa234943 Merge pull request #1011 from Kellermorph/save-sheet-colour
Remember the sheet background colour between runs
2026-09-24 10:50:34 +02:00
Kellermorph 9af562fcbd Remember the sheet background colour between runs
Picking a sheet (folio) background colour in the diagram editor was lost
on every restart. Diagram::background_color is a static initialised to
white and PaletteGraphicsView's s_custom_bg a static bool, and neither
was ever written anywhere -- Diagram::toXml() carries no colour attribute
either -- so closing and reopening a project always came back on the
default and the choice had to be made again.

Store it in QSettings under diagrameditor/sheet_background_* as a pair of
values rather than one: the colour, and whether it was picked explicitly.
Both halves are needed. "#ffffff, follow the system" and "#ffffff, always
white" are the same colour and two behaviours -- the first is what the
views invert on a dark palette -- so keeping only the colour would
silently turn one into the other on the next start, which is the reported
problem one step removed.

The colour is written as HexRgb on purpose. The SVG export gives
Diagram::background_color an alpha of 0 to render a transparent
background and never puts it back, and that transient value must not be
persisted as a permanently transparent sheet.

Applied from main() after the headless export and scripting branch -- those
return before reaching it and must keep rendering on plain white, the rule
ProjectPrintWindow already enforces for printing -- and before QETApp is
constructed, since that constructor already loads the projects given on
the command line. The GUI export dialog is left alone: it renders through
drawBackground(), so what you see is what you export, as it already was
within a session.

Saved at the moment the colour is applied rather than at shutdown, so
neither the print window's temporary white nor the SVG export's alpha can
reach it. The button's constructor now mirrors the stored state instead of
always claiming "system colour", and the "recently used" list is stored
alongside it.

Covered by tst_sheetbackgroundsetting, which pins the custom flag and the
dropped alpha -- the two rules a single stored colour would lose.
2026-09-24 10:30:01 +02:00
Laurent Trinques c85276f61b Merge pull request #1009 from ispyisail/fix/1002-resize-handle-artefacts
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 6m31s
Fix green fragments left behind when moving or zooming an element (#1002)
2026-09-24 10:17:53 +02:00
ispyisail f3a31c291f Fix green fragments left by text resize handles when moving an element
The two width-resize handles added in #591 were free scene items, moved
with setPos() from inside DynamicElementTextItem::paint(). Moving an item
while the view is painting is outside what QGraphicsView's partial
repaint tracks: the handle was drawn only where the current repaint band
overlapped it, and its old position was not reliably cleared. Moving or
zooming a selected element left green slivers on the folio (#1002).
Since 29d16c333 the handles show on every text of a selected element,
so any ordinary element move triggered it.

Make the handles children of the text instead. Qt then moves and
repaints them together with the element, in local coordinates, and
their position only needs updating when the text's size changes, which
documentSizeChanged reports (text, font, width, undo of a resize).
This also takes paint() out of the handle logic entirely.

Reproduced on Linux (Xvfb), so the issue is not Windows-specific.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 19:46:26 +12:00
Laurent Trinques a06220e8dc Merge pull request #1008 from ispyisail/fix/1004-element-preview-cache
Fix #1004: element preview stays stale after editing until the collection reloads
2026-09-24 09:28:33 +02:00
Laurent Trinques 79990e40ee git submodule update --remote elements 2026-09-24 04:26:56 +02:00
Laurent Trinques e1e7ea4888 Merge pull request #1006 from ispyisail/i18n/ui-phrasebooks-29-languages
Add translation suggestions for the interface to 29 phrase books
2026-09-24 04:24:08 +02:00
Laurent Trinques 54b8fe5df4 Merge pull request #1007 from elevatormind/restore-sqlite-dependencies
Restore last sqlite dependencies
2026-09-24 04:02:28 +02:00
Laurent Trinques be0d214b75 Merge pull request #1005 from ispyisail/fix/1002-conductor-color-equipotential
Fix #1002: conductor colour toolbar swatch only recolours one wire segment
2026-09-24 04:01:13 +02:00
ispyisail 2f988e6e2c Refresh cached element preview after saving from the editor
Editing an element already in the user collection, saving, and closing
the editor left the old thumbnail in the elements panel until the
whole collection was reloaded. ElementsLocation::icon() serves the
preview from two caches keyed by path+uuid -- ElementPictureFactory's
in-memory picture cache and ElementsCollectionCache's on-disk SQLite
cache -- and neither was ever told the file changed.

QETElementEditor::toLocation() writes the new XML and returns;
ElementsCollectionWidget::locationWasSaved() then refreshes the panel
item, but it reads the icon through the same two stale caches, so the
refresh was a no-op. slot_reloadElementDrawings() already shows the
correct invalidation call for ElementPictureFactory; this wires the
same pattern, plus a matching refresh of ElementsCollectionCache's
row, into the save path itself.

Fixes the preview half of #1004. The paste-cursor-jump half of that
report is a live design disagreement between two recent commits from
a different contributor and is written up separately rather than
fixed here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 12:09:01 +12:00
Magnus Hellströmer 9786006ca4 fix(database): align sqlite3 forward declarations 2026-09-23 21:45:16 +02:00
ispyisail 91e8956734 Add translation suggestions for the interface to 29 phrase books
Fills lang/phrase/qet_fr_xx.qph for every language QET ships, following
on from discussion #873. A phrase book is never compiled into what
ships -- a translator opens it in Qt Linguist and sees a suggestion
already filled in, to accept or correct.

Only ever appends. phrasebook.write() skips any source string already
in the file (case-insensitive), so no existing entry -- human or
machine -- is touched. 24 languages had no phrase book at all before
this; the other 5 (da, de, nl, ru, sv) keep every line they had.

Built and run with tools/qet-i18n in the qelectrotech-docker harness
(DeepSeek Flash). Companion to qelectrotech-elements#82, which does the
same for the element collection.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 07:34:47 +12:00
ispyisail 6fad3390df Fix issue #1002: conductor-colour toolbar swatch skips the rest of the potential
ConductorColorToolButton::applyColor() only recoloured the selected
Conductor objects. A wire drawn across a junction is several separate
Conductor segments sharing one electrical potential, so selecting one
segment and picking a colour left the rest of the wire its old colour
-- visible only by falling back to F2/double-click, which already
expands to relatedPotentialConductors() for exactly this reason
(ConductorPropertiesDialog, and QetScriptApi::setConductorProperty).

Expand each selected conductor to its potential before building the
undo command, matching that existing pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 07:08:31 +12:00
Magnus Hellströmer 231ab990c3 Revert "refactor: use VACUUM INTO for database export"
This reverts commit 27dea3ffab.
2026-09-23 20:18:25 +02:00
Laurent Trinques f10275cb22 Merge pull request #997 from ispyisail/revive/659-preserve-links
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 2m15s
Preserve master/slave links when pasting or duplicating a folio
2026-09-23 18:40:34 +02:00
Laurent Trinques 3d0e7a27a3 Merge pull request #998 from ispyisail/revive/785-case-insensitive-flags
Make auto_num_locked/potential_isolating case-insensitive
2026-09-23 18:38:38 +02:00
Laurent Trinques f14278324c Merge pull request #995 from ispyisail/revive/713-collection-cancel-race
Cancel the collection-loading map before waiting on it in ~ElementsCollectionModel
2026-09-23 18:36:17 +02:00
Laurent Trinques d5318eb7b4 Merge pull request #996 from ispyisail/revive/682-nonfinite-coordinates
Reject non-finite values in QET::attributeIsAReal()
2026-09-23 18:34:57 +02:00
Laurent Trinques f10ef3ded7 Merge pull request #994 from ispyisail/revive/664-element-info-orphan
Fix element_info orphan row causing UNIQUE constraint errors on undo
2026-09-23 18:33:05 +02:00
Laurent Trinques 6f7ea63589 Merge pull request #1000 from ispyisail/fix/933-german-source-strings
Fix bugtracker #933: German source string and comments in templates code
2026-09-23 18:26:45 +02:00
Laurent Trinques 9a6029a2bb Merge pull request #1001 from Kellermorph/plc-table-size
Plc table size
2026-09-23 18:24:15 +02:00
Laurent Trinques bc0ca56c7e Merge pull request #999 from ispyisail/fix/ci-missing-libsqlite3-dev
CI: install libsqlite3-dev, required by PR #983's find_package(SQLite3)
2026-09-23 18:22:47 +02:00
Laurent Trinques 1d4c0326d4 Merge pull request #1003 from qelectrotech/revert-986-no-sqlite
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m55s
Revert "fix: harden project database export"
2026-09-23 13:51:07 +02:00
Laurent Trinques d889a384aa Revert "fix: harden project database export" 2026-09-23 13:50:49 +02:00
Kellermorph 3457a88e81 Open the PLC master properties dialog at three times its width
Element::editProperty() gave PropertiesEditorDialog no size of its own,
so the dialog fell back to its sizeHint, which is sized for the compact
general-purpose editors it usually hosts. A PLC master instead shows a
six-column IO table that grows horizontally, and the dialog came out
too narrow to read those columns in.

For a master whose type is PLC, resize the dialog to three times its
natural width before exec(), keeping the natural height. The width is
clamped to the available screen so it cannot run off the display, and
every other element type opens exactly as before.
2026-09-23 10:28:00 +02:00
Kellermorph 4e361f93fe Let the PLC master IO table columns be resized, and remember the layout
The IO table of MasterPropertiesWidget -- the panel that opens when a
PLC master placed on a schematic is edited -- set every section to
QHeaderView::Stretch. Stretch spreads the sections evenly over the
widget and disables section dragging altogether, so the column
boundaries were permanently fixed: they could be neither widened nor
narrowed, and only the width of the whole panel had any effect.

Use QHeaderView::Interactive instead, with movable sections and a set
of default widths, so the columns can be dragged as they are everywhere
else in the application. The layout reached this way is written to
QSettings under masterpropertieswidget/plc-table-header-state on every
sectionResized and sectionMoved, and restored the next time the table
is built -- the same header-state trick the free and linked element
trees of this widget already use, except saved automatically rather
than only from the context menu.
2026-09-23 10:27:50 +02:00
ispyisail 6a8838e719 Fix bugtracker #933: German source string and comments in templates code
sources/ElementsCollection/fileelementcollectionitem.cpp had a German
tr() source string ("Makros") in a project whose source language is
French/English elsewhere. Renamed to "Macros" (identical in both
languages, so no translation catalog change is needed). Translated
three German-language comments in elementscollectionmodel.cpp and
diagramview.cpp to English.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 17:31:38 +12:00
ispyisail 774f803626 CI: install libsqlite3-dev for find_package(SQLite3 REQUIRED)
PR #983 added a hard SQLite3 system dependency to CMakeLists.txt, but
the workflow only installed libqt6sql6-sqlite (Qt's runtime driver
plugin), not the C headers/library find_package(SQLite3) needs. Every
PR based on current master has been failing CI with "Could NOT find
SQLite3" since #983 merged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 17:02:36 +12:00
ispyisail 5b0785fcce Make auto_num_locked/potential_isolating case-insensitive
Reviving the still-relevant part of #785, closed 2026-09-10 purely to
clear a review backlog, not on merit. Investigated fresh against
current master -- one of the original PR's three targets turned out
to already be fixed independently: element_nomenclature_view's SQL
predicate for exclude_from_bom already does
"COALESCE(LOWER(TRIM(ei.exclude_from_bom)), '') NOT IN ('true', '1',
'yes', 'on')" (projectDataBase::createElementNomenclatureView()).

auto_num_locked and potential_isolating had no equivalent: five call
sites across terminal.cpp, terminalnumberingdialog.cpp and
elementinfowidget.cpp compared the raw stored string against the
literal "true" with QString::operator==, silently treating "True",
"TRUE", a trailing space, or any value written by something other
than this app's own checkbox as off -- with no error and no visible
difference from the checkbox being genuinely unticked.

Added QET::infoFlagIsTrue(), matching the same accepted spellings
("true"/"1"/"yes"/"on", case-insensitive, trimmed) the SQL predicate
already uses, and switched all five call sites to it.

Verified the exact comparison logic in isolation, outside any QET
build: 15 cases including "True", "TRUE", padded whitespace, "1",
"yes", "on", and their false counterparts -- all correctly
discriminated. Qt 6.10.2, ctest 13/13.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 16:47:07 +12:00
Laurent Trinques ed0f800ab5 Update linux-build.yml
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 2m20s
2026-09-23 06:40:53 +02:00
ispyisail de9b3eae06 Preserve master/slave links when pasting or duplicating a folio
Reviving #659, closed 2026-09-10 purely to clear a review backlog
(#630), not on merit. Rebuilt fresh against current master rather than
merged from the old branch (elementspanelwidget.cpp had drifted enough
that a textual merge risked silently losing content, as it did earlier
in this same session for a different revival). Builds discussion #607.

Cutting/copying a linked group of elements -- a relay coil with its
contacts, a PLC master with its slave I/O elements -- dropped the
master/slave link entirely. Traced end to end: Element::toXml() writes
each partner's uuid into <link_uuid>, Element::fromXml() reads it back
into a deferred, unresolved buffer (tmp_uuids_link), and the only code
that ever resolves that buffer is initLink(QETProject *) -- called
only from Diagram::refreshContents(), itself only called from full
project load and macro-block insertion. Neither DiagramView::paste()
nor ElementsPanelWidget::duplicateDiagram() ever call it, so
tmp_uuids_link is populated correctly and never resolved: the link is
silently dropped. duplicateDiagram() already knew this and worked
around it by calling clearPendingLinks() -- correct to not link back
to a stale source, but it meant folio duplication never preserved a
link either.

Added Element::initLink(const QList<Element *> &candidates) --
resolves against a caller-supplied list instead of a project-wide
search. The scoping is the subtle part: right after the XML round-trip
and before uuids are renewed, a pasted/duplicated element's
tmp_uuids_link still holds its source's original partner uuid, which
at that exact moment still equals the not-yet-renewed uuid of that
partner's own copy, if it was carried along in the same batch.
Resolving only within the batch is what stops a linked pair pasted
together from matching an original element left elsewhere that
happens to still carry that same soon-to-be-replaced uuid. If only one
half of a linked group is in the batch, its entry finds no match and
is dropped -- the same "leave it unlinked" outcome as before.

Wired into PasteDiagramCommand::redo(), before the existing newUuid()
loop and gated by the same first_redo flag. Wired into
duplicateDiagram() the same way, replacing its clearPendingLinks()
call (initLink() clears tmp_uuids_link internally, matched or not).

Verified live -- the original PR's own test plan left both of these
unchecked, so this closes that gap rather than repeating it. Built a
project with a linked PLC master/slave pair (qet-mcp's link_elements),
then drove the real interaction under Xvfb:

  Ctrl+A, Ctrl+C, Ctrl+V:
    originals   95ad58fc <-> e728632c   (unchanged)
    pasted      513e6bf8 <-> 29aa60b4   (linked to each other)

  Right-click folio > "Copier et coller":
    originals   95ad58fc <-> e728632c   (unchanged)
    duplicated  0a33ccb4 <-> 3264fe66   (linked to each other)

Neither copy links back to an original or comes in unlinked. Qt 6.10.2,
ctest 13/13.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 16:37:45 +12:00
ispyisail 032f2249a3 Reject non-finite values in QET::attributeIsAReal()
Reviving the still-relevant third of #682 (closed 2026-09-18 purely to
clear a review backlog, not on merit). Investigated fresh against
current master rather than merged wholesale -- two of the original
PR's three findings turned out to already be resolved independently:

- Element::valideXml() and Terminal::valideXml() already reject a
  non-finite x/y (qIsFinite checks, with comments citing this exact
  class of bug) -- added by someone else since #682 was written.
  Verified live: a project with x="nan" on an element loads and
  exports cleanly on current master, 3.1s, no hang.
- The illegal-XML-control-byte segfault in QDomDocument::setContent()
  does not reproduce either. Tested both bytes from the original
  report (0x00, 0x0E) against a real Qt6 build: both are now refused
  cleanly (XmlParsingFailed, exit 0), no crash. Qt6's QDom parses
  differently to the Qt5 one #682 was written and tested against.

What's still genuinely open: QET::attributeIsAReal() itself --
QString::toDouble()'s output parameter reports success for "nan"/
"inf"/"-inf", and this shared helper (26+ call sites across the
codebase, per #682's own count) had no finiteness check independent of
element.cpp/terminal.cpp's own since-added ones. Confirmed several
call sites are not behind either of those two gates -- notably
elementpicturefactory.cpp's line/rect/ellipse/circle/arc parsing for a
symbol's own drawing (a corrupted .elmt, not just a corrupted project
file), which was and remains reachable through this helper alone.

Qt 6.10.2, ctest 13/13.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 16:25:39 +12:00