Commit Graph

10337 Commits

Author SHA1 Message Date
ispyisail 1e56ed05b0 Merge pull request #1409 from bhangart/fix/mirror-to-english
Mirror qet_en.ts instead of qet_fr.ts now that English is the source
2026-10-09 22:19:06 +13:00
Beat Hangartner 7cff54dd19 Mirror qet_en.ts instead of qet_fr.ts now that English is the source
#1379 added misc/i18n/mirror_source_language.py and an INSTALL.md rule
to run it on lang/qet_fr.ts after update_translations, while French was
the source language. Since #1390 the code is English and qet_fr.ts is an
ordinary translation, so following the rule would copy English text into
empty French entries. The repository test still checked qet_fr.ts and
kept passing.

The rule, the script's description and the repository test now name
lang/qet_en.ts. The script refuses a file whose <TS> header names a
language different from its sourcelanguage (qet_fr.ts says fr_FR / en),
exits 2 and leaves it untouched; files without a sourcelanguage, like
the fixtures, are accepted as before. The 6 entries master added since
the conversion are mirrored, so qet_en.ts is complete again.

Tests: 19 in misc/i18n/test_mirror_source_language.py. The repository
test fails on master (6 entries to mirror); with the check_languages()
call removed, the two refusal tests fail and a run on qet_fr.ts goes
through.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:42:00 +02:00
Laurent Trinques 8e7904d7b0 Merge pull request #1407 from bhangart/fix/tbt-editor-close-crash
Stop listening to its children before the template editor is destroyed
2026-10-09 10:05:27 +02:00
Laurent Trinques e89c69719a Improve terminal_strip 2026-10-09 09:18:40 +02:00
Beat Hangartner 0e2738b246 Stop listening to its children before the template editor is destroyed
Closing a title block template editor crashed it. The editor deletes
itself on close; QWidget's destructor then deletes its children, and
some of them signal the editor as they go:

- the scene deselects its cells, so the view emits
  selectedCellsChanged() to the editor's slot of the same name (when a
  cell was selected);
- the undo stack is cleared, so it emits cleanChanged() to
  updateEditorTitle() (when the template was edited).

Both slots then run on an object whose destructor has already run. A
Debug build aborts ("Called object is not of the correct type (class
destructor may have already run)"); a release build calls a member
function of a destroyed object.

Disconnect every child of the editor from it in the editor's
destructor, before the base destructors delete the children.

No file-format change, no change while the editor is open.

Tests: none automated; the editor needs the whole application.
Checked by hand (see the PR).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Beat Hangartner <beat@hangartners.ch>
2026-10-09 08:49:24 +02:00
Laurent Trinques bdeef6d306 Merge pull request #1403 from ispyisail/fix/mcp-macos-no-copy
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 2m1s
Fix assistant server tools failing on macOS: run the original program (#1178)
2026-10-09 01:54:06 +02:00
ispyisail d895be6569 qet-mcp: run the original QElectroTech on macOS, not a private copy (#1178)
Every headless call failed with the macOS package: _launch_executable()
copied the program out of the signed .app bundle, and macOS kills such a
copy at launch (exit 137). The copy has also lost the path to the bundle's
Qt frameworks. Windows already ran the original for the same kind of
reason (DLLs beside the program); macOS now does too.

That is safe for the same reason as on Windows: the export flags and
--run return before QElectroTech constructs SingleApplication, so there
is no running instance to hand the call to. Linux keeps the private copy.

Reported and tested by DarkBiscuits on macOS 26.7 with the r10796 DMG:
with this one change and QT_QPA_PLATFORM=offscreen, every tool works
headless with no Dock icon.

test_qet_mcp: 421 tests pass on Linux against a master build with
QET_BINARY/QET_ELEMENTS/QET_EXAMPLES and scripting on, none skipped;
test_which_systems_copy fails with darwin removed from the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:49:23 +13:00
Laurent Trinques 36cfa938e9 Improve terminal_strip 2026-10-08 23:32:29 +02:00
ispyisail 3420ce2ee6 Merge pull request #1390 from ispyisail/feature/english-source
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m39s
Make English the source language of the interface text
2026-10-09 06:55:54 +13:00
ispyisail 3e1f3ce950 Regenerate the English source conversion on master 8b5a09f3fc
Generated by tools/qet-en-source/regenerate.sh, not edited by hand. The
tree is the fresh conversion; this commit only joins it to the PR's
history so the branch is never force-pushed.
2026-10-09 06:12:32 +13:00
ispyisail 80c8e297ec Regenerate the English source conversion on master 8b5a09f3fc
Generated by tools/qet-en-source/regenerate.sh, not edited by hand. The
tree is the fresh conversion; this commit only joins it to the PR's
history so the branch is never force-pushed.
2026-10-09 05:05:49 +13:00
Laurent Trinques 8b5a09f3fc Merge pull request #1399 from IBSYSLevi/feature/information-fields-visibility
Show all element information fields for terminals
2026-10-08 16:32:19 +02:00
Laurent Trinques 1058c82390 Merge pull request #1398 from saschbe/fix/local-small-handles
Add smaller resize handle sizes
2026-10-08 16:29:07 +02:00
Laurent Trinques 0c7811d29a Merge pull request #1396 from IBSYSLevi/feature/terminal-potential-grouping
Reapply "Feature: Terminal potential grouping"
2026-10-08 16:26:49 +02:00
Laurent Trinques 1bfaef4d4d Merge pull request #1395 from IBSYSLevi/fix/moving-elements-with-keys
Swap Ctrl/Alt arrow shortcuts in the diagram editor
2026-10-08 16:26:13 +02:00
Laurent Trinques b719502060 Terminal strip: natural sort for automatic ordering and terminal lists
"Position automatique" sorted labels as plain strings, so XAUZ1:10
came before XAUZ1:2. Add QETUtils::naturalLessThan (digit runs compared
by value, case insensitive; QCollator numeric mode is ignored by some
Qt backends) and use it in SortTerminalStripCommand, FreeTerminalModel
and the terminal strip tree dock.
2026-10-08 16:19:44 +02:00
saschbe 31ba717a94 Add smaller resize handle sizes 2026-10-08 15:39:03 +02:00
Levi Jetzer f9bdc73c2f Show all element information fields for terminals
Terminal elements only offered eight information fields in the
diagram editor (formula, label, comment, designation, manufacturer,
manufacturer reference, machine manufacturer reference, supplier).
That subset was chosen in faf4235be, before the size, quantity and
auxiliary fields existed, so a terminal could not be given a width,
height, depth or type.
2026-10-08 15:29:05 +02:00
Levi Jetzer aba7a3d8b5 Reapply "Feature: Terminal potential grouping"
This reverts commit 1793dcb9a8.
2026-10-08 15:00:13 +02:00
Levi Jetzer 110a16348a Swap Ctrl/Alt arrow shortcuts in the diagram editor
The element editor moves the selection in fine steps with
Ctrl+arrow. The diagram editor used Alt+arrow for its fine
movement and Ctrl+arrow for aligning a text group, so the same
modifier did different things in the two editors.

Swap the two so the diagram editor matches the element editor:
- Ctrl+arrow: fine movement (unchanged step, still configurable via
  diagrameditor/key_fine_Xgrid and key_fine_Ygrid)
- Alt+arrow: align the selected text group (Left/Up/Right)

Only the modifier checks change, the behaviour of each action is
untouched.
2026-10-08 14:24:07 +02:00
Laurent Trinques 7c6a0411d4 CI: try to fix windows-build
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m39s
2026-10-08 10:29:20 +02:00
Laurent Trinques a022d03338 Update QET64.nsi 2026-10-08 09:42:43 +02:00
ispyisail 9baa853cf3 Merge pull request #1392 from bhangart/fix/grid-size-guard
Never use a folio grid step below 1 from the settings
2026-10-08 19:20:19 +13:00
ispyisail 6da144da34 Merge pull request #1391 from bhangart/fix/element-sort-comparator
Sort elements and terminals by position with a strict weak ordering
2026-10-08 19:20:15 +13:00
Beat Hangartner ede7bd99e1 Never use a folio grid step below 1 from the settings
The folio grid steps, diagrameditor/Xgrid and Ygrid, are read from the
settings in six places with a plain toInt() and used as they come. The
preferences page cannot store anything below 1, but a hand-edited or
damaged settings file can hold 0, a negative number or text:

- Diagram::snapToGrid() divides by the step, so 0 is a SIGFPE on the
  first click that places a symbol;
- Diagram::drawBackground() runs "while (g_x % xGrid)" and then loops
  "gx += xGrid", so 0 crashes every repaint and a negative step never
  ends;
- the paste, duplicate and align paths divide by it or multiply with it.

Add foliogrid.h, a header-only helper: FolioGrid::step() reads a
settings entry and returns the built-in step (Diagram::xGrid, 10) when
the entry is missing, not a number, below 1 or above what an int
holds (QVariant::toInt() wraps such a value around). Every reader of
the two keys goes through it; the settings page, which only writes the
spin box values, is unchanged.

No file-format change, and no change for any step the preferences page
can produce.

Tests: tst_foliogrid covers the helper: 1, 10, "7", int max are kept;
missing, 0, -5, "ten", "", "nan", 99999999999 (as text and as a
number) and int max + 1 fall back; 7.9 rounds to 8 as before; and the
two settings keys through a QSettings scope of the test's own. The
helper is new, so the test cannot fail on master; the six readers are
the replacements in the diff.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Beat Hangartner <beat@hangartners.ch>
2026-10-08 06:04:35 +02:00
Beat Hangartner 1023b38f8e Sort elements and terminals by position with a strict weak ordering
Two position comparisons handed to std::sort were not strict weak
orderings, which std::sort requires; with the wrong kind of comparator
the sort is undefined behaviour (libstdc++ can read past the range,
MSVC debug builds assert "invalid comparator").

- comparPos(), used when renumbering the elements of a project, ended
  with "<=" on x and y, so two elements at the same position (pasted at
  the origin, placed by a script, stacked symbols) were each "before"
  the other.
- The terminal numbering dialog compared positions with a 1 px
  tolerance ("within 1 px counts as aligned, then compare the other
  axis"), which is not transitive: terminals at x = 2, 1.1 and 0.2 give
  a < b, b < c and c < a.

Move the comparisons into positionorder.h, a header-only helper:
xThenY()/yThenX() with "<", and roundedXThenY()/roundedYThenX(), which
round the positions to whole pixels first so that items a fraction of a
pixel apart still count as aligned, as the tolerance meant to, while
staying transitive. comparPos() keeps its folio and row-letter stages
and calls xThenY() for the last one.

No file-format change. Elements at distinct positions sort exactly as
before; the terminal order changes only for terminals less than a pixel
apart that straddle a half-pixel boundary.

Tests: tst_positionorder checks each order on a grid of awkward
positions by brute force (irreflexive, asymmetric, transitive, with a
transitive equivalence), the three-terminal cycle, twenty items at one
position, and that std::sort leaves the list sorted and intact. The
helper is new, so the test cannot fail on master; the call sites are
the two replacements in the diff.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Beat Hangartner <beat@hangartners.ch>
2026-10-08 06:03:31 +02:00
ispyisail 51675c8a65 Merge pull request #1380 from bhangart/fix/image-stale-crop-on-load
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 2m15s
Drop a picture's stale crop when loading a project
2026-10-08 13:51:14 +13:00
ispyisail c7c8b98e01 Merge pull request #1386 from bhangart/fix/sequential-placeholder-order
Replace %sequ_10 before %sequ_1 in label formulas
2026-10-08 13:46:16 +13:00
ispyisail 02031fbdde Merge pull request #1385 from bhangart/fix/legacy-seqhf-attribute
Recognise seqhf_1 when loading sequential numbers saved the old way
2026-10-08 13:46:09 +13:00
ispyisail 8145cd3f7a Merge pull request #1384 from bhangart/fix/borderproperties-qreal-sizes
Read the default border's column width and row height as decimals
2026-10-08 13:46:02 +13:00
ispyisail 9517f80abc Merge pull request #1383 from bhangart/fix/conductor-size-settings
Read the default conductor width from the settings as a decimal
2026-10-08 13:45:55 +13:00
ispyisail 1beb993ea4 Merge pull request #1382 from bhangart/fix/folio-freeze-flags-reload
Read a folio's freeze flags back as the words they are saved as
2026-10-08 13:45:48 +13:00
ispyisail 09f7a66693 Merge pull request #1361 from bhangart/ci/windows-tests
CI: build and run the unit tests in the Windows job
2026-10-08 13:45:40 +13:00
ispyisail 5ca5d89623 Merge pull request #1387 from bhangart/fix/logomanager-readonly-init
Initialise the read-only flag of the title block logo manager
2026-10-08 13:45:33 +13:00
ispyisail f2c0fdebba Merge pull request #1388 from bhangart/fix/part-rotation-init
Initialise the rotation of a new line and polygon in the element editor
2026-10-08 13:45:25 +13:00
ispyisail dd973fb220 Merge pull request #1379 from bhangart/feature/fr-translation-mirror
Serve the French UI from qet_fr.ts instead of the code text
2026-10-08 13:40:41 +13:00
ispyisail 05b4c8449c Merge pull request #1389 from ispyisail/fix/english-missing-strings
Fix 9 messages always shown in French; add missing English
2026-10-08 13:23:43 +13:00
ispyisail 82c77ea250 Expect the English wording in the dark-picture checkbox test
The interface text is now English at the source, so the untranslated
checkbox and tooltip read in English.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 12:50:13 +13:00
ispyisail f5962ecd06 Make English the source language of the interface text
Every tr() source string in the code and the forms is now the English
text, and French is a translation (qet_fr.ts) like the other languages.
Generated, not hand-edited: master ef795a21e5 converted by
qet-en-source (harness 4c4ddc9).

3144 messages get an English source, 665 already had one, 0 left French; 8 shared-wording pairs disambiguated.
C++: 2904 literals in 233 files. Forms: 809 strings in 65 files. All 34 .ts files re-keyed; qet_fr.ts filled.
lupdate check: PASS. What each of 38 languages shows, old vs new: same 118266, English instead of French 38433, comment fallback 51, differences 0.
2026-10-08 12:50:13 +13:00
ispyisail 57c650e6e0 Fix 9 messages always shown in French; add missing English
Nine messages were never passed through tr(), so every user saw them in
French: the undo entries for grouping and ungrouping terminals, for
editing an arc (3), an ellipse, a rectangle and a PLC table in the
element editor, and the error box when integrating a title block
template fails. They are now wrapped, text unchanged.

qet_en.ts also lacked English for 63 messages added since the last
translations update (Customize dialog, mouse gestures, toolbar contents,
mirror, element scale, paste element information, paper format).
update_translations was run on qet_en.ts with lupdate's guessing
heuristics off, then the 72 new entries were translated. Most of the
diff is lupdate refreshing line numbers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit d0657f36db)
2026-10-08 12:49:39 +13:00
ispyisail d0657f36db Fix 9 messages always shown in French; add missing English
Nine messages were never passed through tr(), so every user saw them in
French: the undo entries for grouping and ungrouping terminals, for
editing an arc (3), an ellipse, a rectangle and a PLC table in the
element editor, and the error box when integrating a title block
template fails. They are now wrapped, text unchanged.

qet_en.ts also lacked English for 63 messages added since the last
translations update (Customize dialog, mouse gestures, toolbar contents,
mirror, element scale, paste element information, paper format).
update_translations was run on qet_en.ts with lupdate's guessing
heuristics off, then the 72 new entries were translated. Most of the
diff is lupdate refreshing line numbers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 12:29:08 +13:00
Beat Hangartner c7a9ddbf8c CI: build and run the unit tests in the Windows job
The Windows job configured with -DPACKAGE_TESTS=OFF, so no test ever ran
on Windows. Build the tests and run ctest after the build, with the
offscreen platform. ctest prints no output for failed tests in the MSYS2
shell, so each failed test runs again with QtTest writing its log to a
file, which is printed in its own log group. The ctest log is added to
the artifact uploaded on failure.

No change to the app or its files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 23:48:58 +02:00
Beat Hangartner fc39acaf75 Replace %sequ_10 before %sequ_1 in label formulas
AssignVariables::assignSequence() replaced the sequential-number
placeholders from 1 upwards with a plain QString::replace(). "%sequ_1"
is also the start of "%sequ_10", so with ten or more sequences a
formula's %sequ_10 became the first value followed by a "0". The same
held for every family (%sequf_, %seqt_, %seqtf_, %seqh_, %seqhf_,
%seqw_, %seqa_).

Run the loop from the highest number down to 1, so the longer
placeholder is gone before the shorter one is looked for.

No file-format change. Labels with fewer than ten sequences come out
exactly as before.

Tests: tst_tensequentialnumbers runs the binary's --export-bom on a
fixture whose element has the formula %sequ_10-%sequ_1 and the unit
values A to J, and expects the label "J-A". On master the label is
"A0-A".

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Beat Hangartner <beat@hangartners.ch>
2026-10-07 23:38:34 +02:00
Beat Hangartner e3a1a67c66 Recognise seqhf_1 when loading sequential numbers saved the old way
Before the <sequentialNumbers> element, an element's or conductor's
sequential numbers were saved as the attributes sequ_1, sequf_1,
seqt_1, seqtf_1, seqh_1 and seqhf_1. The readers check for one of them
to take the old route, and all three lists had the same slip: sequf_1
twice, seqhf_1 never (Element::fromXml, Conductor::fromXml, and
readSequence() in the project database). A file whose only old
sequence was the hundred-folio one took the new route, found no
<sequentialNumbers>, and lost it; the database built its labels from
an empty sequence instead of refusing the fast path as it does for the
other five attributes.

Name seqhf_1 in the three lists.

No file-format change: nothing is written differently, and a file with
any of the other five attributes loads exactly as before.

Tests: tst_legacysequentialattributes runs the binary's --resave on a
fixture whose element and conductor carry only seqhf_1 (and a second
pair carrying sequ_1 as a control) and reads the <sequentialNumbers>
written back. The two seqhf_1 cases fail on master.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Beat Hangartner <beat@hangartners.ch>
2026-10-07 23:38:34 +02:00
Beat Hangartner 6674bae057 Read the default border's column width and row height as decimals
BorderProperties::toXml() writes colsize and rowsize with "%1", so a
project whose default border has a width such as 60.5 saves it as
"60.5". fromXml() read both back with toInt(), which is 0 for a
decimal, and the folio clamps 0 to its 5 px minimum. New folios of such
a project got 5 px columns and rows. The folio's own reader,
BorderTitleBlock::borderFromXml(), already uses toDouble().

Read the two sizes with toDouble(&ok) and keep the previous value when
the attribute is missing, not a number, or not finite.

No file-format change: toXml() is untouched, and whole-number sizes,
which every example project has, load exactly as before.

Tests: tst_borderpropertiesxml compiles borderproperties.cpp alone and
reads 50, 60.5, 61.3 and 61.7 back, round-trips 60.5/80.25 through
toXml(), and leaves the value alone for a missing, text, nan or inf
attribute. Seven cases fail on master.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Beat Hangartner <beat@hangartners.ch>
2026-10-07 23:38:34 +02:00
Beat Hangartner 7933e89dc0 Read the default conductor width from the settings as a decimal
ConductorProperties::toSettings() writes the conductor width with
QString::number(cond_size), e.g. "1.4", and fromSettings() read it back
with toInt(), which is 0 for any value that is not a whole number. A
default width of 1.4 set in the configuration became 0 on the next
start, so every new conductor was drawn with a pen of width 0.

Read it with toDouble(), and fall back to 1 when the stored value is
not a positive finite number (a hand-edited or truncated settings
file), as the other fallbacks in fromSettings() do.

No file-format change: this is the settings file only; the project
file's condsize attribute was already read with toDouble().

Tests: tst_conductorsizesetting compiles conductorproperties.cpp alone
and round-trips 2, 1.4, 0.4, 61.3 and 61.7 through toSettings() and
fromSettings() in a QSettings scope of its own; text, 0, -1, nan, inf
and a missing value give 1. Nine of the cases fail on master.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Beat Hangartner <beat@hangartners.ch>
2026-10-07 23:38:34 +02:00
Beat Hangartner 6b83fdf7b3 Read a folio's freeze flags back as the words they are saved as
Diagram::toXml() writes freezeNewElement and freezeNewConductor as
"true"/"false", but Diagram::fromXml() read them with toInt(), which is
0 for both words. So "freeze new elements" and "freeze new conductors"
were off again on every folio after a project was saved and reopened,
since the day the flags were added.

Compare the attribute with "true" instead. The project-level flags in
QETProject already do this.

No file-format change: the attributes are written exactly as before,
and a file without them still loads with both flags off.

Tests: tst_foliofreezeflags runs the binary's --resave on a fixture
with one folio per combination (both, elements only, none, attributes
missing) and reads the saved attributes back. The two frozen folios
fail on master.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Beat Hangartner <beat@hangartners.ch>
2026-10-07 23:38:34 +02:00
Beat Hangartner 863c5e07ba Initialise the read-only flag of the title block logo manager
TitleBlockTemplateLogoManager never sets read_only_: its constructor
leaves the member uninitialised, and setReadOnly() returns early when
the new value equals the member. The template editor calls
setReadOnly() right after creating the manager, so whenever the
garbage value happened to match, the call did nothing: the add, delete
and rename buttons stayed enabled on a read-only template, and
isReadOnly() returned garbage.

Give the member a default value, false, in the header, matching the
buttons' state after construction. Only
sources/titleblock/templatelogomanager.h changes.

No behaviour change for an editable template: setReadOnly(false) on a
manager that is already editable still returns early. No file-format
change.

Tests: none of their own; an uninitialised read is what a sanitizer
finds, and this one is the same kind as the BorderTitleBlock and
ConductorProfile fixes (#1358, #1359). ctest on macOS, and all
example projects through --resave and --export-svg, unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Beat Hangartner <beat@hangartners.ch>
2026-10-07 23:38:34 +02:00
Beat Hangartner 0795281d79 Initialise the rotation of a new line and polygon in the element editor
PartLine and PartPolygon keep their rotation in m_rot, which nothing
sets until setRotation() is called. rotation() reads it, and
setRotation() computes the difference from it, so the first rotate of
a freshly drawn line or polygon in the element editor starts from
garbage: qRound(m_rot * 100.0) on an arbitrary value is undefined
behaviour, and the angle stored afterwards is arbitrary too.
PartRectangle and PartEllipse already start at 0.

Give m_rot a default value of 0 in both headers, as PartEllipse has.
Only the two headers change.

No file-format change, and no change for a part loaded from a file or
rotated once, since both set m_rot before reading it.

Tests: none of their own; an uninitialised read is what a sanitizer
finds, and this one is the same kind as the BorderTitleBlock and
ConductorProfile fixes (#1358, #1359). ctest on macOS, and all
example projects through --resave and --export-svg, unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Beat Hangartner <beat@hangartners.ch>
2026-10-07 23:38:34 +02:00
Beat Hangartner be48b59e8f Drop a picture's stale crop when loading a project
Before #1310, undoing a crop left the crop rectangle in place, so a
project saved afterwards has a <crop> but shows the uncropped picture.
Since #1368 the picture shown is computed from the original and the
crop, so the first mirror or colour key of such a picture applied the
old crop again, and undoing that edit showed the cropped picture.

The picture shown is always exactly the size of the crop. fromXml() now
drops a crop that does not match it: the crop becomes the whole
original, and when the shown picture is not the original's size either,
the shown picture becomes the original. Valid files load and save as
before.

New tst_imagestalecrop: a valid crop is kept; a stale one is dropped
for a shown picture of the original's size and of another size, where a
later crop cuts the picture that was shown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:54:42 +02:00