Commit Graph

9754 Commits

Author SHA1 Message Date
Andre Rummler 2b813a0d91 Fix Qt6 package name which was forgotten when removing the variable used to differentiate between Qt5 nd Qt6. 2026-09-28 23:03:11 +02:00
Andre Rummler 574d6dcd56 Fix captor operator [=] which needs to be written explciitly [this] in C++20. 2026-09-28 23:01:42 +02:00
Andre Rummler 7facb1cbf8 Fix cast which becomes a warning in C++20. 2026-09-28 23:00:58 +02:00
Laurent Trinques bc5ef180b6 Merge pull request #1105 from ispyisail/feat/stable-symbol-uuid
Give symbols in older projects the same uuid every time they open
2026-09-28 16:42:25 +02:00
Laurent Trinques cdcff93190 Update CMakeLists.txt 2026-09-28 16:42:05 +02:00
Laurent Trinques 69a468af63 Merge branch 'master' into feat/stable-symbol-uuid 2026-09-28 16:33:24 +02:00
Laurent Trinques f981ec7dc8 Merge pull request #1111 from ispyisail/feat/1070-rotate-whole-group
Rotate a selected group as one piece
2026-09-28 14:55:04 +02:00
Laurent Trinques 7456a48681 Merge pull request #1110 from ispyisail/feat/1070-pick-one-member
Click again on an item of a selected group to select it alone
2026-09-28 14:54:27 +02:00
Laurent Trinques 5a09d67f64 Merge pull request #1109 from ispyisail/fix/second-save-changes-nothing
Fix a just-saved project changing again on its next save
2026-09-28 14:52:54 +02:00
ispyisail e25ae3cbfb Test the second save on every example, a single-space value and accents
Review of #1109 (scorpio810):

- tst_resaveunchanged now resaves every .qet in examples/ twice instead
  of two of them (24 rows, ~40 s). Dropping the load-time trim now also
  fails affuteuse_250h.qet, which the two-file version missed.
- New case singleSpaceValueKept: a title-block property set to one
  space survives two saves (#973), and an accented property comes back
  unchanged. Fails if qetproject.cpp stops parsing with
  PreserveSpacingOnlyNodes.
- New tst_diagramcontext: the QDom reader (projects) and the pugixml
  reader (element definitions in the collection) return the same value
  for plain, stray-spaced, accented and non-Latin text. Fails if the
  pugixml path decodes as Latin-1 or either reader stops trimming.

The pugixml reader still reads a single-space value as "": pugixml drops
whitespace-only text unless parse_ws_pcdata is set, as it did before
this PR. That reader only sees element definitions, never a project, so
#973's title-block values do not go through it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 23:29:51 +13:00
ispyisail a8f940505d Rotate a selected group as one piece
Discussion #1070 proposed that rotate, like move, copy and delete, works on
the whole group once one of its items is clicked. Rotate (Space) turned
each member on its own spot instead, so rotating a group pulled it apart:
two grouped texts side by side ended up each turned in place, no longer
side by side.

When the selection is exactly one whole group -- wires aside, which follow
their symbols -- Rotate now turns it as one piece around its centre, as
"Pivoter le groupe" (Shift+Space) already does
(ItemGroups::soleWholeGroup()). Any other selection, including a single
member picked out of its group, rotates as before.

In the GUI, on two grouped texts selected by one click: Space on master
leaves both where they were, turned; here it gives exactly what
Shift+Space gives on both (both texts swung around the group's centre).
tst_itemgroups: 4 new checks; without the whole-group condition, a
picked member counts as a group and fails. ctest 24/24.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:39:03 +13:00
ispyisail 6ed41bbfd0 Click again on a member of a selected group to pick it on its own
Clicking an item of a group selects the whole group (#1070). Clicking
again on one of its items is meant to select just that item, to edit it
on its own -- what discussion #1070 proposed -- but the second click
selected the whole group again: Qt left only the clicked item selected on
release, and the group completion pulled the others back in.

A press on a member of a group that is selected whole now notes that
member (ItemGroups::memberToPick(), which also finds the member when the
click lands on a symbol's own text). If the click ends without a drag and
Qt has left only that member selected, the selection stays so. A drag
still moves the whole group; Ctrl+click keeps its meaning; a group of one
is not picked from.

In the GUI, on two grouped texts: one click then Delete removes both
(master and this); click, click again, Delete removes only the clicked
text here, both on master; dragging after one click moves both texts by
the same amount on both. tst_itemgroups: 5 new checks; removing the
whole-group or the group-of-one condition fails one each. ctest 24/24.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:33:28 +13:00
ispyisail 75450d7102 Make saving a just-saved project change nothing
Saving a project that had just been saved changed it again in 18 of the
24 example projects, so a project kept in version control showed changes
nobody made. Both causes were cleanup done on save but not on load:

- Symbol information whose values were all empty was written as an empty
  <elementInformations/> block (DiagramContext::toXml() skips empty
  values, Element::toXml() wrote the block anyway). The next load read it
  as no information and the next save dropped it. The block is now written
  only when something went into it.
- Information values were trimmed on save but not on load, so a label with
  stray spaces (" PRISE") kept them in memory and in its displayed copy
  until the project was opened again. The same rule, kept in one place,
  now applies when reading: stray whitespace around real content trimmed,
  a value that is only whitespace kept (#973).

All 24 examples now save identically a second time (master: 6), and each
one's first save is byte-for-byte what master wrote only on its second.
A title-block property set to a single space keeps it through two saves.

tst_resaveunchanged runs --resave twice on Projet_vierge.qet and
m_000.qet; both fail without this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 21:59:02 +13:00
ispyisail e12410e439 Never derive a uuid the file already carries
A symbol keeps its derived uuid once saved, even when it is moved. A
symbol saved without a uuid that later turns up on the spot it left -- a
hand edit, an older version, another tool writing the file -- derived the
same uuid, and the project had two symbols with one identity.

readDiagramsXml() now collects every symbol and wire uuid the file
carries, on any folio, before a folio loads; derivedItemUuid() moves to
the next counter value while a candidate is among them. The result still
depends on the file alone. Renamed from derivedUuid(), which QETProject
already has for the project's own uuid.

tst_derivedsymboluuid: newcomerOnAMovedSymbolsSpotGetsAnotherUuid fails
with the check switched off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 21:13:02 +13:00
ispyisail 0721b42e21 Give symbols saved without a uuid the same one on every load
A symbol saved without a uuid got a random one from Element::fromXml()
on every load, and the next save wrote it out: two loads of the same file
gave the same symbol two identities, and anything pointing at it by uuid
(a script, a comparison of two versions, a wire's identity) could not
follow it from one session to the next.

When a folio is loaded, such a symbol now gets a UUID v5 derived from
what it is and where it sits: its type, its position on the folio and its
orientation. Never the folio's index, so inserting or moving a folio does
not change it. Identical symbols stacked on one spot, or a copied folio,
are told apart by a counter kept per project (QETProject::derivedUuid()),
in load order among those symbols alone. A paste still renews uuids.

Symbols that have a uuid in the file keep it. All 24 example projects
already have one for every symbol, so they are unchanged; with the
symbols' uuids stripped, each saves byte-for-byte the same twice (master:
different every time).

tst_derivedsymboluuid runs --resave on a fixture with its uuids stripped:
same uuids on every load, same after a folio is inserted in front, saved
uuids kept, stacked copies differ. The first two fail without this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 21:13:02 +13:00
Laurent Trinques 5ca047a05d Merge pull request #1104 from Alfmat01/master
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 2m29s
Complete Spanish translations
2026-09-28 09:40:57 +02:00
Alfmat01 db3ca9eb6d Merge pull request #1 from Alfmat01/Alfmat01-Complete-Spanish-translations
Complete Spanish translations

Complete 1,383 pending entries in lang/qet_es.ts, using terminology appropriate for electrical schematics, wiring and control panels.

Translate missing entries and review unfinished drafts.

Preserve placeholders, plural forms, links and markup.

Leave previously completed entries unchanged.

Validation: XML structure checked and no pending translations remain in the submitted file. Not tested in the running application.
2026-09-28 09:33:08 +02:00
Alfmat01 257ec77136 Complete Spanish translations
Complete Spanish translations
2026-09-28 09:27:43 +02:00
ispyisail 1586aaec75 Merge pull request #1097 from ispyisail/fix/qet-mcp-free-texts-only
Fix qet_diff counting a symbol's own texts as free texts
2026-09-28 16:48:53 +13:00
ispyisail 4ad352126b Merge master into fix/qet-mcp-free-texts-only
Conflict in _extras(): keep master's tables and folio uuids and _angle(),
and read only the folio's own inputs/shapes/images as this branch does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:26:31 +13:00
ispyisail 2e56001c9d Merge pull request #1102 from ispyisail/fix/legacy-symbol-input-texts
Fix schema_indus.qet example showing no device labels
2026-09-28 16:21:27 +13:00
ispyisail 65f10f6c86 Merge pull request #1092 from ispyisail/fix/search-list-dark-icons
Fix element icons that are dark on a dark theme in search results
2026-09-28 16:20:42 +13:00
ispyisail 5585d87ea5 Merge pull request #1089 from ispyisail/fix/1069-align-lone-shape
Fix Align centre commands when a group is left with only a shape
2026-09-28 16:20:11 +13:00
ispyisail 83da997c83 Merge pull request #1100 from ispyisail/feat/qet-mcp-items
Add qet_items: list drawn items with their uuids
2026-09-28 16:15:03 +13:00
ispyisail 049a4ba673 Merge pull request #1101 from ispyisail/feat/qet-mcp-edit-table-text-uuid
Let qet_edit address tables and symbol text fields by uuid
2026-09-28 16:14:41 +13:00
ispyisail f14bc0de28 Merge pull request #1098 from ispyisail/feat/script-table-text-uuid-index
Add scripting lookups for tables and symbol text fields by uuid
2026-09-28 16:14:09 +13:00
ispyisail 6d62140445 Merge pull request #1096 from ispyisail/fix/qet-mcp-diff-rotation
Fix qet_diff not reporting a rotated symbol
2026-09-28 16:13:34 +13:00
ispyisail d04eee756a Merge pull request #1095 from ispyisail/test/qet-mcp-read-tool-contracts
Add exact-answer tests for the MCP read tools and qet_diff
2026-09-28 16:13:02 +13:00
ispyisail d8c309afce Merge pull request #1094 from ispyisail/fix/qet-mcp-autonum-not-a-wire
Fix the MCP server counting a wire-numbering rule as a wire
2026-09-28 16:12:37 +13:00
ispyisail b1689fb760 Merge pull request #1093 from ispyisail/fix/qet-mcp-continuity-folio
Fix qet_continuity reporting a missing folio as clean
2026-09-28 16:12:13 +13:00
ispyisail 2529b11d69 Convert examples/schema_indus.qet to the current format; its labels show again
schema_indus.qet was saved by QElectroTech 0.3, where each placed symbol
kept its own values for its definition's old text fields, as
<element><inputs><input text="T1" .../>. Loading those was deliberately
dropped in 1f53c3929 ("Remove retro compatibility of element text item
prior to qet 0.7"), so since 2021 this shipped example has shown none of
its per-symbol texts: 122 texts on 44 of its 48 symbols, including every
device label (Q1, KM1, KM2, T1, M1, S1-S5, H1, H2, the X terminals) and
ratings such as "24VAC" and "F0 am 0,5A".

This commit changes the example file only, not the loader. It was
converted by loading it once with a local build that had 1f53c3929
reverted (and the converted label kept when the symbol's own label was
empty, as that code intended), and saving it. That build is not proposed.

Checked with a build of current master: every one of the 44 symbols shows
exactly the texts its <inputs> held; the 48 symbols (type, position,
rotation), the 69 wires (ends and numbers) and the folio are unchanged; the
two free texts only gain their font written out, as any save does. Like any
save, the file also gains uuids and the current version number.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:01:31 +13:00
ispyisail 503c8c69b5 qet-mcp: qet_edit addresses tables and symbol text fields by uuid
set_table_position and delete_table took a table's index, and
set_element_text / delete_element_text a text field's index. An index
shifts when an earlier item is deleted, so a run that deletes table 0 and
then moves "table 1" moved the wrong table.

Each now also takes the item's uuid, resolved at run time through
qet.tableIndex() and qet.elementTextIndex() (the previous commit), as
texts, shapes and pictures already are through textIndex() and friends. A
text field's lookup is scoped to the op's element, since copies of a symbol
share their fields' uuids. An index still works, and a build without the
lookups is refused with the usual missing-methods hint only when a uuid is
actually used.

Tests: the generated script (no binary), and end to end: delete one table
then move the other, both by uuid; and edit one copy's shared field by uuid,
leaving the other copy's untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:49:36 +13:00
ispyisail 9232775a25 Merge branch 'feat/qet-mcp-diff-uuids' into feat/qet-mcp-items 2026-09-28 13:31:54 +13:00
ispyisail d92a0781b3 qet-mcp: test the text-field uuid guard on each side of a diff
A field with a uuid on a symbol with none cannot be keyed by
(symbol, field); the mutation audit showed either side's guard could be
dropped unnoticed. Now tested with the symbol uuid missing on one side,
then the other.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:31:54 +13:00
ispyisail fd962619b5 Scripting: look up a table or a symbol text field by its uuid
qet.textIndex(), shapeIndex() and imageIndex() turn a free text's, shape's
or picture's uuid into the index the other calls take. Tables and symbol
text fields had no such lookup, so a script could only name them by index,
and an index shifts when an earlier item is deleted: a script that deletes
table 0 and then moves "table 1" moves the wrong table.

- qet.tableIndex(folio, uuid): the table's current index in tables(folio),
  or -1.
- qet.elementTextIndex(folio, elementUuid, textUuid): the field's current
  index in elementTexts(folio, elementUuid), or -1. The element is part of
  the address because a field's uuid is unique only within its element:
  copying an element keeps its fields' uuids (2612_ats_singlephase.qet has
  one field uuid on 20 copies).

Tests in misc/qet-mcp's integration suite, which drives these through
--run: a table followed across a deletion of the one before it, and the
same field uuid resolving on two copies to each copy's own field. Both
fail against a build without this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:29:24 +13:00
ispyisail fd8b816475 qet-mcp: qet_items test fixture wraps its table in <tables>, as saved files do
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:20:23 +13:00
ispyisail bbe1903ec8 qet-mcp: read a folio's tables from its <tables> block only (as #1097 does for texts)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:20:22 +13:00
ispyisail 56a23d85dc Merge branch 'feat/qet-mcp-diff-uuids' into feat/qet-mcp-items 2026-09-28 13:20:22 +13:00
ispyisail a89a57bb10 qet-mcp: a symbol's own texts are not free texts on the folio
qet_diff's texts, shapes and pictures sections collected every <input>,
<shape> and <image> under a folio with iter(). Symbols in older files carry
their own <inputs><input> texts, so those were counted as the folio's free
texts: schema_indus.qet folio 1 showed 124 where QElectroTech has 2, and
editing one of those symbol texts would have been reported as a free text
changing.

Only the folio's direct <inputs>, <shapes> and <images> children are read
now. Shapes and pictures have no nested copies in the shipped examples;
they are changed too so the three stay alike. Found by comparing the
tools' answers with QElectroTech's own lists over the shipped examples.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:19:54 +13:00
ispyisail 1de307c13c Merge branch 'feat/qet-mcp-diff-uuids' into feat/qet-mcp-items 2026-09-28 13:15:21 +13:00
ispyisail 279c98f10a qet-mcp: add qet_items, listing drawn items with their uuids
qet_edit addresses free texts, shapes, pictures and tables by uuid, and
qet_diff reports them by uuid, but no tool listed them: the only way to
learn an item's uuid was to read the .qet. qet_items lists every free
text, shape, picture, table and symbol text field per folio (counted from
1, as qet_elements), with its uuid and main fields; filter by folio and
kind; default limit 500.

Also a test that every tool argument holding a data path is in the
workspace policy (_DATA_PATHS). Nothing checked that direction: a new tool
left out of the policy would have read or written anywhere with every test
passing, as removing qet_items' entry showed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:15:21 +13:00
ispyisail 8fd692783c qet-mcp: key symbol text fields by symbol and field uuid; refuse shared uuids
A symbol text field's uuid is unique only within its symbol: copying a
symbol keeps them, so 7 of the 24 shipped examples repeat one, up to 20
times (2612_ats_singlephase.qet). Keyed on the field uuid alone, those
fields merged and an edit to one copy could be reported on another. They
are now keyed on (symbol uuid, field uuid).

More generally, uuids are used as keys only when present on every item and
unique on each side; otherwise the old position/ends matching is kept, for
texts, shapes, pictures, tables, conductors and folios alike. Found by the
seeded-edit invariants (an untouched symbol's field reported changed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:15:12 +13:00
ispyisail f5888e8b56 qet-mcp: qet_diff matches conductors, folios, text fields and tables by uuid
QElectroTech now saves a uuid on conductors (those created since, or
loaded with one), folios, symbol text fields and tables, but qet_diff still
matched them by position or by ends:

- conductors: a rewire read as one wire removed and another added; by
  uuid it is that conductor with changed "ends".
- folios: a reorder read as every later folio changing its fields; by uuid
  it is one "reordered" entry, plus "added"/"removed" folios.
- symbol text fields: deleting the first of two read as the second
  changing; by uuid it is that field removed.
- tables (<graphics_table>) were not compared at all; now a section of
  their own.

Each is matched by uuid only when every item of that kind on both sides
has one; otherwise the old matching is kept, and each section says which
in "keyed_by", since older files and their first re-save mix the two.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:03:06 +13:00
ispyisail e64ce0a6ba qet-mcp: qet_diff reports rotated symbols; equal angles compare equal
Rotating a symbol changes only its orientation attribute (quarter turns),
which qet_diff did not read, so a pure rotation reported no change in any
section. The elements section now has "rotated": each symbol whose
orientation changed, with the value before and after.

Rotating and undoing leaves QElectroTech writing text-field rotations as
"-270" where they were "90" (or "-90" for "270"); compared as strings that
read as a change. Rotations of texts, shapes, pictures and element text
fields are now compared reduced to [0, 360).

Found by a seeded-edit invariant run over the shipped examples: 51
rotations unreported across 20 projects, and 7 undo sequences reported as
changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 12:55:46 +13:00
ispyisail c1b93a8892 qet-mcp: pin the read tools' and qet_diff's exact answers in tests
A mutation audit -- planting one small bug at a time in the server's read
and diff code (a flipped comparison, a skipped branch, a dropped output
field, a list cap off by one) and running this suite -- caught 78 of 241
planted bugs with the tests that need no QElectroTech binary. qet_elements,
qet_conductors and their row builders caught none: a filter that never
applied, a missing field or a wrong default limit all passed.

Two new classes pin exact output on small hand-made projects:
ReadToolContracts (qet_project_info, qet_elements, qet_conductors: every
field, every filter, limit and truncation, legacy and current conductor
keys) and DiffContracts (every qet_diff section: moves, relabels, info,
conductors and the unstable-key warning, texts/shapes/pictures by uuid and
by position, element text fields, folio and project fields, terminal
strips, and every list cap). The same audit now catches 240 of 241; the
one left is equivalent (rsplit("/", 1) vs rsplit("/", 2) then [-1]).

Tests only; no change to the server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 12:42:01 +13:00
ispyisail 18e5c30bed qet-mcp: a folio's wire-numbering rule is not a wire
A folio's conductor auto-numbering rule is saved as
<autonum><conductor><part .../></conductor></autonum>. qet_project_info
and qet_conductors counted every <conductor> tag under the folio, so the
rule appeared as a wire with no ends and no number: schema_indus.qet
folio 1 reported 70 conductors where QElectroTech holds 69.

Only children of <conductors> are wires now. A new corpus test compares
each folio's element and conductor counts with QElectroTech's own
elementCount()/conductorCount() after loading the file, over every shipped
example; it failed only on schema_indus.qet before this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 12:09:58 +13:00
ispyisail a73e5c462d qet-mcp: qet_continuity no longer reports a folio that does not exist as clean
qet.checkContinuity() answers a folio index it has no folio for with an
empty list, so qet_continuity returned "0 findings" for it -- the same
answer as a clean folio. The index counts from 0 while qet_elements numbers
folios from 1, so passing the last folio's number checked nothing and said
so cleanly; any other folio's number checked the next folio instead.

An index with no folio is now refused before QElectroTech is launched, with
the valid range and the counting rule. Each finding also carries
folio_number (counted from 1) beside the existing folio index. The
qet_continuity and qet_conductors descriptions and the README say how each
tool counts. Nothing changes for a valid index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:58:05 +13:00
ispyisail f5b644918b Merge pull request #1090 from ispyisail/fix/qet-mcp-diff-empty-info
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 2m12s
Fix qet_diff reporting untouched symbols as changed after a re-save
2026-09-28 11:54:13 +13:00
ispyisail 228e696b65 Merge pull request #1091 from ispyisail/fix/qet-mcp-edit-folio-hint
Fix qet_edit giving no clue when a folio number is off by one
2026-09-28 11:53:42 +13:00
ispyisail 9b09469a53 Adapt only element icons in the shortcut bar editor, not its commands
The editor's bar row and command list mix element previews with command
icons. The command icons already follow the palette through the qet-dark
icon theme, and running them through ElementPreviewDelegate again
flattened them to one lightness (up to 16/255 per pixel off master; a
two-tone icon would have had its tones swapped).

Adapt the element icons once, where the items are built, instead of
installing the delegate on those lists. This also covers an element
dragged from the element list onto the bar, which copies the item's icon
and so previously kept the dark one until the editor was reopened.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:53:13 +13:00