Commit Graph

480 Commits

Author SHA1 Message Date
ispyisail 8ce3b214c1 Merge pull request #1563 from DaTweety/feature/place-symbol-texts
Place symbol texts: label and notes as one block, at one of nine points
2026-10-21 21:46:42 +13:00
ispyisail de248eb20a Merge pull request #1555 from DaTweety/fix/element-text-resize-corner
Symbol text width handles: the point the text is aligned on stays in place
2026-10-21 21:41:17 +13:00
ispyisail 76a36bc5e9 Merge pull request #1573 from ispyisail/fix/autonum-increment-width
Fix the Auto numbering Increment boxes cut off on Windows
2026-10-21 21:40:13 +13:00
ispyisail 0e5d4ad990 Merge pull request #1572 from ispyisail/fix/selection-panel-collapse
Shrink the Selection properties panel again when nothing is selected
2026-10-21 21:39:55 +13:00
ispyisail 537e3c3c10 Fix the Increment boxes of the Auto numbering panel cut off on Windows
Each Increment spin box had a fixed maximum width of 55 px. The Windows
11 style puts the up and down arrows side by side, which leaves almost
nothing of 55 px for the number (#1566). The boxes now take the width
their style asks for.

Test: tst_autonumincrementwidth runs the editor under a style that
widens spin boxes the way the Windows 11 style does and checks each
Increment box is at least as wide as the style needs: 55 px against 81
px before this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:26:57 +13:00
ispyisail dc30b26dab Shrink the Selection properties panel again when nothing is selected
#1533 stopped the panel shrinking to its title bar when the selection
had nothing to edit, so that it would keep the height the user gave it
(#1439). Users want the room back for the Collections and Auto numbering
panels while nothing is selected (#1566).

The panel's layout is back to QLayout::SetMaximumSize, so the empty
panel shrinks as before. Just before its last editor goes, the panel
keeps the heights of every docked panel in its area; when an editor
comes back it gives them back with QMainWindow::resizeDocks, the call a
separator drag makes. Giving only this panel its height back fell 35 px
short in the test: the neighbours had been split again from their size
hints and gave up only part of the room.

Test: tst_selectionpanelheight now checks, twice over, that the empty
panel shrinks and that selecting an element again brings back the height
the user gave it. Without the restore the panel comes back at 470 px
instead of 560; on master the empty panel does not shrink.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:26:57 +13:00
ispyisail 04ad1c890a Write the project's numbering from the project database
QETZ-DB-FIRST stage 2b-2. The save writes the numbering schemes and
their settings from the project database's numbering rows (2b-1)
instead of the project's own lists. The project still keeps the rows
in step after each change, and QET_CHECK_NUMBERING_ROWS still says
whether the two agree, until the lists go (2b-3).

tst_elementautonumids::numberingRowsFollowEveryChange also checks the
saved file: the current symbol and wire numbering and the schemes. With
setCurrrentElementAutonum() not writing the rows, a save straight after
it now names the old scheme in use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 14:40:29 +13:00
ispyisail 025bd0a57e tst_elementautonumids: the numbering rows test on a one-folio project
A --run script is stopped after 30 s: on industrial.qet the 23 saves
took 23 s natively and did not finish under AddressSanitizer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 06:54:50 +13:00
ispyisail d05cc654ad Keep the project's numbering in the project database too
QETZ-DB-FIRST stage 2b-1. The numbering schemes of wires, symbols,
folios and cables, and the settings saved with them (the scheme in use
of each kind, freezing new labels, auto-break, the cable axis and
question), get three tables of the project database: numbering_scheme,
numbering_part (one row per part, in order) and numbering_setting.
QETProject writes them when it reads its file and after each change of
its numbering, from every setter; nothing reads them yet.

The save writes the numbering through writeNumberingXml(), the code it
had moved out of writeDefaultPropertiesXml(), so the same writer can
take the rows once the save moves to them (2b-2).
QET_CHECK_NUMBERING_ROWS=1 writes the numbering from the project and
from the rows at each save and says whether the two differ.
tst_elementautonumids::numberingRowsFollowEveryChange checks it after
each of 23 changes; with one setter not writing the rows, one save
differs and it fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 06:54:49 +13:00
DaTweety 1bf076eca8 Width handles: a text is not dragged narrower than its widest word
Narrower, a long word ran out of the text's box while the short lines
were still aligned inside the too narrow width: on a right aligned or
centred text a lone "1" stood in the middle under its words, and moved
on every step. Found on a real plan (-XS20, "Steckdosen Windfang, 1").
The probe checks that no line runs out of the text after narrowing, and
also for a text aligned bottom left.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:53:52 +02:00
DaTweety 8f2cd9f039 Width handles: read the drag in the text's coordinates as they were at the press
The text moves on every step of a drag to keep its alignment point in
place. Mapping the mouse with its current transform made the delta grow
with every step, so a right or centre aligned text ran away: 90 px of
mouse gave +1220 px of width. The probe now drags in 30 steps and checks
the width grows by exactly as much as the mouse moved.

Reported by ispyisail in the review of #1555.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:01:23 +02:00
DaTweety 214b0e3b7d Width handles keep the point the text is aligned on
Which point of a symbol's text stays in place while its width is
dragged now follows the text's alignment, the nine points of its
alignment dialog, rather than the handle: a text aligned top right
keeps its top right corner whichever handle is dragged, one centred
keeps its middle, and a text that wraps grows away from that point.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:01:23 +02:00
DaTweety 52adc8b3f8 Width handles: a text that wraps keeps its top corner in place
Narrowing a symbol's text with the left handle until it wraps onto
more lines moved it up: the point kept in place was the middle of the
opposite edge, and the middle moves down as the text grows. Keep the
top of the opposite edge instead, or the middle or the bottom for a
text aligned that way, for both handles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:01:23 +02:00
DaTweety 74f908aa1e Symbol text width handles: the opposite edge stays in place
Dragging the left width handle of a symbol's text changed its width but
kept its position, which is its left edge: the right edge moved with the
drag and the text jumped away from where the user held it. Now the edge
opposite the dragged handle stays in place, as in a drawing program and
as the corner handles of a free text already do (TextResizeHandles). It
is found along the text's own baseline, so a rotated text behaves the
same. Undo restores width and position together, with TextResizeCommand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:01:23 +02:00
DaTweety f828e464c4 Place symbol texts: undo puts a group's texts back where they lay
Placing a group switches its alignment, which lays its texts out anew.
Undoing to AlignJustify, the default of the "Label and comment" group,
lays out nothing, so the texts stayed at their placed offsets. The
positions of the group's texts are now part of the undo step. The probe
reproduces the case from a group with freely placed texts.

Reported by ispyisail in the review of #1563.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:01:17 +02:00
DaTweety 9c3c19a316 Place symbol texts: the icon shows where the texts go
Each of the nine points has its own icon: a grey square for the symbol
and two bars for the lines of text, where they will go and lined up as
they will be. The grid shows them instead of arrows, and the toolbar
button takes the icon of the point chosen last, so that one sees where
a click on it puts the texts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:01:17 +02:00
DaTweety 0a577eaabf Place symbol texts: label and notes as one block, at one of nine points
A new command, "Place symbol texts", for the selected symbols: their
label and the notes beside it go as one block next to the drawing of
the symbol, 3 px away, at one of the nine points of the text alignment
dialog, chosen in a 3 x 3 grid that opens from the toolbar button and
from the Edit menu. A click on the button places them at the point
chosen last (left of the symbol at first).

Left of the symbol the lines are aligned right, so that they hang on it
and a longer text grows away from it (their alignment is set too);
right of it aligned left; above, below and on it centred. The lines
keep their order and their spacing, a group of texts is placed as one
line with the same alignment, a turned symbol is placed in the folio's
directions, and everything is one undo step.

Element::drawingRect() gives the area the drawing covers, tighter than
the bounding rect, which is rounded up to the grid. A toolbar command
with a menu now shows as a button with an arrow beside it.
Translations for all languages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:01:17 +02:00
DaTweety 7a2b390b78 Report jump: show the target's whole folio, shade its texts too
Feedback on a real plan: zooming in on the end jumped to was too much.
A jump now shows the target's folio whole, as a page (the view's fit),
selects the target and shades it and its texts blue for three seconds,
so that the eye finds the label too. One function, Element::jumpTo(),
serves the double click on the arrow and on the text.

The mouse pointer is not moved onto the target: Wayland does not let a
program move the pointer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 22:39:06 +02:00
DaTweety 5641b031f9 Jump between folio report ends with a pointing hand and a highlight
A linked folio report now shows a pointing hand under the mouse, on its
arrow as well as on the text that shows its label (which already turned
blue), and a double click on the arrow goes to the other end, as a
double click on the text did. Before, a double click on the arrow opened
the report's properties, which stay reachable from the context menu and
the properties panel.

The end jumped to is highlighted for two seconds, with the same blue as
the hover highlight but independent of it: the hover highlight is
cleared as soon as the mouse leaves the end jumped from, so on the same
folio the user saw nothing mark where the jump landed.

The text of a linked slave that shows its master's label gets the same
blue and hand, since a double click on it already jumped to the master.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 20:12:30 +02:00
Laurent Trinques e7a168d1f9 Merge pull request #1556 from ispyisail/test/round-trip-gate
Test the round trip between .qet and .qetz in CI
2026-10-19 17:23:51 +02:00
Laurent Trinques a9715b5bf6 Merge pull request #1559 from DaTweety/feature/folio-crosshair
Folio-wide crosshair under the mouse (Display menu)
2026-10-19 17:21:08 +02:00
DaTweety 3738b3c1d8 Folio-wide crosshair under the mouse (Display menu)
A new option in the Display menu, off by default and kept in the
settings: two thin lines cross the whole folio, inside its border,
through the grid point under the mouse, so that one sees at a glance
whether symbols and terminals are in line, also while dragging them.

They are drawn by the view in drawForeground(), on screen only: no item
is added to the folio, so nothing of them is saved, exported or
printed. Only the strips the lines leave and enter are repainted, and
they go when the mouse leaves the view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 13:20:00 +02:00
ispyisail 8fa58bd609 Fix the 3D mouse search asking every device on the PC for its name
While no 3D mouse is plugged in, which for most users is always, the
hidapi backend looks for one every 3 seconds for as long as
QElectroTech runs. It asked hidapi for every HID device,
hid_enumerate(0, 0), and on Windows hidapi reads the serial number,
manufacturer and product strings of each device that matches what it
is asked for from the device itself. So every mouse, keyboard, headset
and webcam on the PC was asked for its names every 3 seconds, on the
GUI thread. Issue #1553 reports the pointer moving by itself on Windows
since the first package with this backend; nothing else in that package
talks to devices. SDL 2.0.9 polled the same way and saw freezes and a
keyboard that kept re-initialising (libsdl-org/SDL#3071).

The scan now asks for 3Dconnexion's vendor id, and for each 3D mouse
made under Logitech's id by its product id, since most other mice and
keyboards carry Logitech's id too. That is twelve hidapi calls instead
of one, but a device that does not match is no longer asked for its
names. On Linux and macOS hidapi takes the names from the system, and
one call is cheaper than twelve (on Linux, median of 200: 5 ms against
36 ms), so the scan there is unchanged.

Where the scan is filtered, a Logitech-id device that is not one of the
eleven 3D mice on the list is no longer found, even if it reports
itself as a multi-axis controller. 3Dconnexion's own mice and keyboards,
such as the CadMouse, are still asked: their vendor id is asked for as
a whole, so that new 3D mice are found without a list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 22:39:02 +13:00
ispyisail 5837bec4fe Merge remote-tracking branch 'upstream/master' into test/round-trip-gate 2026-10-19 22:34:36 +13:00
ispyisail 4fee464919 Merge pull request #1552 from ispyisail/fix/keep-unloadable-items
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m43s
Fix symbols and wires lost when their definition cannot be found
2026-10-19 20:58:13 +13:00
ispyisail 289e33d06e Merge remote-tracking branch 'upstream/master' into fix/keep-unloadable-items 2026-10-19 20:21:02 +13:00
ispyisail 46d8588113 Fix a closed project staying in the element collection panel
A project opened before the element collection panel's first load is
queued until the load starts; one opened during a load goes into the
model being loaded. ElementsCollectionWidget::removeProject() only
removed the project from the model already shown, so a project closed
in either window stayed in the collection, and the next reload read the
freed project (crash in XmlElementCollection::root()).

removeProject() now also takes the project out of the queue and out of
the model being loaded. That model waits for its loading threads, which
read the project, and drops the project's items from the list applied
when the loading ends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:28:35 +13:00
ispyisail 31730678c5 Fix a crash on clicking a sheet of a project closed without saving
Closing a modified project and choosing Discard left the project in the
project tree. The panel removed its item, but the project still emits
projectInformationsChanged() while it is torn down (setModified()), and
GenericPanel answered by adding the project back, with items for its
sheets. Once the project was freed, a click on one of those sheets
called diagram->showMe() on a freed Diagram: SIGSEGV in
QObjectPrivate::maybeSignalConnected(), the backtrace reported on the
forum (topic 3215).

The panel now stops listening to a project when it is closed, to its
sheets and to its embedded title block collection, before removing its
item.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:28:31 +13:00
ispyisail 7fcd6086a8 Test the round trip between .qet and .qetz on every example and a project using every feature
scorpio810's condition for the database as the model (#1512): a lossless
round trip between the formats, tested in CI. For every example and for
fixtures/every_feature.qet, the save stamps aside:
- a .qetz saved again is the same file;
- a .qetz saved as a .qet is the project saved straight as a .qet;
- that .qet saved as a .qetz is the project saved straight as a .qetz,
  part by part, its database row by row;
- lazy folios save what every folio built saves, in both formats.

fixtures/every_feature.qet (arummler's test file, #1512) was built through
QElectroTech's script API: four folios, numbering schemes with every kind
of part, a coil linked to three contacts, folio reports, terminals, a
cable, wires with every property, every symbol information field, symbol
texts of every source, independent texts, shapes, a curve, a picture, a
PLC master with its inputs and outputs, a terminal strip bridged, a parts
list and a folio summary, copied, rotated and mirrored symbols. What the
API cannot set was added in the form QElectroTech saves it (new-folio
defaults, a folio field of the user's, single-line wires, a wire text
moved by hand, a coloured symbol text, a text group, an old information
key, a parts list continued on a fourth folio), then QElectroTech opened
and saved it. Of every tag and attribute QElectroTech wrote saving the
341 projects of our corpus, it holds all those found in 5 or more of them
and 98.3 % of all; it also holds what none of them has (terminal strips,
PLC data, tables).

It failed before the previous commit (the .qetz saved again had its folio
parts in another order).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:14:49 +13:00
ispyisail 833ed0f523 Keep in a .qetz what QElectroTech cannot compute for a symbol it cannot build
The .qetz leaves out of its folios what QElectroTech computes when it
builds them (format 4): a symbol's terminal list and information texts,
a wire's description of its ends. A symbol whose definition the project
does not hold is never built, so none of it came back: through a .qetz,
the symbols and wires kept by the previous commit lost their terminal
lists, texts and end labels. QetContainer::dropComputed() now leaves a
symbol whose definition is not in the project's collection
(embeddedDefinitions()), and the wires ending on it, as they are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:21:45 +13:00
ispyisail 7f05b23062 tst_movedterminals: count the wires joined, a wire not joined is kept now
A wire whose terminal is not matched is saved back as it was read (F128),
so the saved file holds it: the cases count the wires joined (saved, less
those QElectroTech says it did not load), and the cases that refuse a
match check the wire is still in the file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:11:24 +13:00
ispyisail 0bcd0ca4b6 Keep the symbols and wires a folio cannot load, and save them back
Opening a project dropped, with only a debug line, a symbol whose
definition could not be found (missing from the project's collection and
from the library) and every wire on it; and a wire whose terminal could
not be matched, after the symbol's definition was redrawn. The next save
wrote the project without them. One save of a forum upload whose
collection was incomplete kept 1 of 219 symbols and none of its 299 wires.

A folio now keeps what it could not load as it was read
(Diagram::m_kept_symbols, m_kept_wires) and writes it back when it is
saved whole (Diagram::writeKept()):
- a symbol, its terminals given ids after those of the folio's symbols;
- a wire, an end found written from its terminal as Conductor::toXml()
  does, an end not found as the symbol and terminal uuids it names; for a
  wire saved by legacy ids, the terminal's uuid derived from where it was
  saved (Element::savedTerminalUuid(), out of matchMovedTerminals()), so
  it joins again once the definition has it there. A wire whose ends
  cannot be read, or an end naming no terminal of the file, is kept so
  that it joins nothing.
A symbol kept counts as waiting (projectDataBase::waitingSymbols()), so a
partner keeps its link to it. The open warning now lists the symbols not
loaded too, and says both are kept.

Once the definition is back, the symbol and its wires are built again:
741.qet with one transistor definition removed (8 symbols, 33 wires on
them) saves all 67 wires, and with the definition put back opens with
all 65 symbols and 67 wires, the same folio as before.

tst_resaveunchanged::unloadableItemsKept: fixtures/coil_contact_wired.qet
(coil, linked contact, two wires; built with the script API) with the
coil's definition removed, or the contact's terminals moved and given new
uuids: both symbols, wires and links saved, a second save the same, and
the coil's definition put back gives the undamaged project's symbols and
wires. Fails on master (1 symbol; 0 wires).

FINDINGS F127, F128.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:45:55 +13:00
ispyisail 266d1e2573 Merge pull request #1540 from hairykiwi/fix/elements-panel-crashes
Guard elements-panel auto-select against a detached project item
2026-10-19 16:00:33 +13:00
ispyisail 3abaf21302 Merge pull request #1543 from ispyisail/fix/save-keeps-qet
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m54s
Fix Save writing a .qetz beside an opened .qet: keep the .qet
2026-10-19 15:07:41 +13:00
ispyisail 171a597b12 Merge pull request #1546 from ispyisail/refactor/link-from-db-rows
Link symbols on folios opened later from the project database
2026-10-19 15:07:21 +13:00
ispyisail 4d61909fd1 Merge pull request #1545 from ispyisail/refactor/save-links-from-db
Fix removed folios leaving links to their symbols in the saved file
2026-10-19 15:07:03 +13:00
ispyisail 9a41e01187 Link a symbol built to its partners from the project database's rows
A symbol built after opening (lazy folios) links to the partners its
link rows name, and to the symbols whose rows name it; its file's list
only adds what the rows miss. The rows keep a partner on a folio not
built until it is built, in its saved place, so a symbol no longer keeps
a list of its own: the list read from the file is dropped once opening
has filled the database. pendingLinks(), waitingLinks(),
keepsSavedLinks(), linksToSave() and QETProject::waitingLinkTargets()
go; the callers that asked a symbol for its waiting partners ask the
database (waitingPartners()).

When its links change, a symbol's rows are written from its built
partners and the waiting partners its rows held, in their order
(rowsToWrite()), so a full rewrite of the table keeps them too.

A symbol built alone as a partner on a folio not built
(Diagram::buildSymbols()) counts as built: its rows are kept, it is not
waiting (waitingSymbols(), waitingPartners()), so saving does not build
its folio, and removing it does not remove the rows naming it. A row
naming a placed symbol that has not linked yet (its folio being built)
is kept too. At opening the database holds no link until its fill
(forgetLinks()); the symbols read their files' lists until then, and the
fill takes their order: a symbol's own rows, its list, then the rows
naming it. The symbols waiting on folios not built are cached and
updated as folios are built.

QET_CHECK_LINK_ROWS now checks the rows against the links: each built
partner in its order, no other row but to a folio not built.

tst_resaveunchanged::undoKeepsLinksToUnbuiltFolios: m_000 opened with
lazy folios, a contact, its folio or its coil's folio (not built)
removed, then undone: every link saved as before.

QETZ-DB-FIRST stage 2a-3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 10:47:41 +13:00
DaTweety ba64fd5164 %T last: a folio title keeps the variables it contains
The folio title was put into the formula before %c, %l, %prefix, the
wire variables, the title block and project variables and the
sequences, so a title such as "Schema %c" had them replaced inside it.
It is replaced after all of them now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 22:52:06 +02:00
ispyisail b473a07b7d Save a symbol's links from the project database
A symbol's <links_uuids> are now what the project database's link rows
list for it, in their order (projectDataBase::savedLinks()), instead of
a list the save built itself from the symbol's partners: QETZ-DB-FIRST
stage 2a, the save reads links from the database. A symbol the database
does not hold (saved outside a project) still lists them itself.

The rows leave out a partner the project no longer has, so removing a
folio with every folio built no longer saves links to its symbols, as
lazy folios already did (the links part of F113). Every other save is
unchanged.

tst_resaveunchanged removedFolioLeavesNoLinkToIt: affuteuse_250h with
every folio built, folio 3 removed: 4 links named symbols no longer in
the file, none now.

Needs #1522 (each symbol its own uuid) and #1523 (rows in saved order);
local only until both are merged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XCdg2Er2vYhB8Vwa5G7fSR
2026-10-19 01:50:35 +13:00
hairykiwi 959695ed5c Add regression test for elements-panel project-open auto-select crash
- tst_elementspanelprojectopen drives ElementsPanel::projectWasOpened() --
  the slot the application connects to a project being opened -- on a fresh
  panel whose first reload() has not run, reproducing the detached-item
  ordering deterministically offscreen.
- On master without the guard the test fails: the auto-select leaves
  currentItem() pointing at an item whose treeWidget() is not the panel (and
  on some builds crashes in QTreeModel). With the guard it passes: no item is
  selected while the project is detached.
- Registered via qet_add_app_test(), which links the application code and
  runs a real QETApp offscreen.

Generated-by: Claude Code
Signed-off-by: hairykiwi <174199+hairykiwi@users.noreply.github.com>
2026-10-18 13:44:35 +01:00
ispyisail 89c3979957 Fix Save writing a .qetz beside an opened .qet: keep the .qet
A project opened from a .qet was saved as a .qetz beside it, with a
notice, and the .qet left as it was: a project kept in git did not stay
a .qet. Save now writes the format the project was opened in; only Save
As changes it (scorpio810, discussion #1512). A .qetz is saved as a
.qetz as before.

The save-beside path goes: the flag set on opening, the .qetz path made
from the .qet's, write()'s switch to it and back on failure, and the
notice and replace question in ProjectView::doSave().

tst_resaveunchanged::qetSavedAsQetzBeside becomes qetSavedAsQet: Save of
an opened .qet writes no .qetz and the .qet holds what a plain save of
it holds (it fails without the fix: a .qetz is written beside).
tst_changedondisk makes its .qetz with Save As, as Save no longer does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 22:56:22 +13:00
DaTweety 1080188f6e Add %T, the target folio's title, to report and cross reference formulas
A folio report could name the folio it points to by position (%f), folio
number (%F), installation (%M) and location (%LM), but not by its title.
EPLAN and other tools print references like "+UV1/1 Versorgung UG/EG.0",
which with %T is the formula "+%LM/%F %T.%c".

- FormulaContext carries the folio title, filled from the title block.
- %T is replaced on its own only, not as the start of a longer word.
- A report whose formula uses %T follows the target folio's title block
  (BorderTitleBlock::informationChanged), as %F already follows its
  folio number.
- The report and cross reference property pages list %T.

tst_reportfoliotitle checks the replacement, that %total and title
block variables keep their values, and an empty title.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 11:47:48 +02:00
ispyisail 8c5a16d50e Merge pull request #1535 from ispyisail/fix/link-rows-test-skip-without-qml
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m39s
Fix the Linux CI failing since #1523
2026-10-18 22:31:50 +13:00
ispyisail a715ff25ff Merge pull request #1534 from ispyisail/feat/multi-symbol-info
Add editing the information of several symbols at once (#1439)
2026-10-18 22:31:36 +13:00
ispyisail ff1a81d85d Fix the Selection properties panel losing the height the user gave it
The panel shrank to its title bar whenever the selection had nothing it
could edit (a click on an empty spot, several elements, a mix of items):
its content layout used QLayout::SetMaximumSize, and an empty vertical
layout's maximum height is 0. The other right-hand panels took the room,
and when an element was selected again the panels were split again from
their size hints, so the height the user had dragged the panel to was
lost (reported by ankostis in #1439).

With the default size constraint the panel keeps its height and stays
blank while there is nothing to edit. The element, wire, text and shape
editors draw the same: their screenshots in a 730-860 px tall panel are
pixel-identical before and after.

Test: tst_selectionpanelheight (on master the height goes from 560 to
19 px).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 22:29:54 +13:00
ispyisail e05b511cb2 Tests: skip linkRowsAfterLazyEdit on a build without --run
linkRowsAfterLazyEdit() (#1523) edits a project through --run, which a
QElectroTech built without Qt Qml does not have. The Linux CI builds it
that way, so the test has failed there since #1523 merged, on master
(e46bb97c5) and on every pull request after it: "MOVED true" is never
printed. It now skips, as the five other --run tests of
tst_resaveunchanged do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 21:51:25 +13:00
ispyisail ce0798bb18 Add editing the information of several elements at once (#1439)
Selecting several elements left the Selection properties panel blank and
Edit (Ctrl+E) disabled, so giving many elements the same supplier or
manufacturer meant editing them one by one.

The panel now shows the Information fields for two or more elements, or
for elements selected with other items (left unchanged, and counted in
the panel's header). Ctrl+E opens the same editor as a dialog when only
elements are selected.

- A field shows the value every element shares, or "Multiple values".
  Left alone, each element keeps its own value; typed text goes to every
  element; the erase box clears the field on every element.
- A restore button per field, and Reset, bring back the values the
  elements had.
- One apply is one undo step. The keystrokes typed in the panel merge
  into one step but never into an earlier change of the same elements
  (a paste of element information, another edit).
- Numbering scheme, label freezing and the number picker are left out:
  they are about one element. Custom properties are kept as they are.

Also:
- An element's own texts, selected with it by Select all, count as part
  of the element, not as other items.
- ElementInfoWidget::reset(): Reset in the element dialog now brings back
  the Information tab's values; it did nothing.
- The panel opens an element's editor on the tab picked last, instead of
  the first tab, after the selection went to something else and back.

Test: tst_multielementinfo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 21:44:00 +13:00
ispyisail 8db65ddaeb Merge master into fix/joined-wire-uuid
The conflict was tests/qttest/CMakeLists.txt, where #1520 added
tst_savedstamps at the place this branch adds tst_joinedwireuuid. Both
are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 20:50:24 +13:00
ispyisail e46bb97c5b Merge pull request #1523 from ispyisail/refactor/link-rows-in-place
Fix the project database listing a symbol's links in another order than its file
2026-10-18 19:53:12 +13:00
ispyisail 5cba4dd7d7 Merge pull request #1522 from ispyisail/fix/shared-symbol-uuid-renew
Fix symbols sharing a uuid: give each copy its own when a project opens
2026-10-18 19:52:54 +13:00