Commit Graph

10360 Commits

Author SHA1 Message Date
ispyisail ff35d0dc69 Merge pull request #1416 from bhangart/feature/drop-images-on-folio
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m40s
Drop picture files on a folio to add them
2026-10-09 22:59:20 +13:00
ispyisail d02f6c1c12 Merge pull request #1417 from ispyisail/fix/1408-shared-symbol-uuid
Fix wires moving to another symbol after saving an older project (#1408)
2026-10-09 22:54:57 +13:00
ispyisail f5c643da06 Merge pull request #1415 from bhangart/fix/shape-pivot-flag-undo
Undo restores whether a shape's pivot was placed by hand
2026-10-09 22:50:30 +13:00
ispyisail e18bd1c182 Merge pull request #1412 from bhangart/fix/mirror-twice-keeps-position
Mirror a symbol twice and it comes back where it was
2026-10-09 22:45:58 +13:00
ispyisail b769fa1213 Merge pull request #1413 from bhangart/fix/image-rotation-saved
Save the rotation of a picture turned with Space
2026-10-09 22:44:40 +13:00
ispyisail af978ab907 Merge pull request #1411 from bhangart/fix/settings-tests-windows
Give the toolbar and gesture settings tests their own settings scope
2026-10-09 22:39:37 +13:00
ispyisail 7868dd13a4 Merge pull request #1414 from bhangart/fix/image-handle-escape
Cancel a picture's handle drag with Escape
2026-10-09 22:39:22 +13:00
ispyisail aeb97fcaaa Merge pull request #1410 from ispyisail/feature/settings-profile-parts
Add saving and loading only part of the settings
2026-10-09 22:35:37 +13:00
ispyisail 053ff0b6cf Fix wires moving to another symbol after saving an older project (#1408)
Symbols copied in old versions can share one uuid on a folio. Up to
0.100 that was harmless: a wire on a terminal without uuid was saved by
terminal number. Since #1118 every terminal has a uuid, so every wire was
saved by symbol uuid and terminal uuid, and on reopening findTerminal()
took the first symbol carrying it: all the copies' wires landed on it.

- Save: a wire end on a symbol whose uuid another symbol of the folio
  also carries is written by terminal number again.
- Load: when a wire's uuid names several symbols, pickEnds() takes the
  pair whose distance matches the wire's saved path, then the symbols
  whose labels match the ones saved with the wire, then two terminals no
  wire joins yet; otherwise the first, as before. This repairs a file a
  build with #1118 saved once.

tst_sharedsymboluuid gives the three lamps of
Habitat-Schemas_developpes.qet one uuid: master loses a net after a
save (8 of 9) and reopens the file it saved with 8; with this, 9 both
times. The 25 examples save the same as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 22:30:26 +13:00
ispyisail 50cbb3bcee Merge pull request #1404 from ispyisail/feature/1251-static-text-anchor
Add anchor="alignment" for static texts in element files (#1251)
2026-10-09 22:19:25 +13:00
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
ispyisail d159a8be94 Settings files: leave out dialog sizes, trackpad gestures with controls
- Dialog sizes and positions (dialoggeometry/...) describe this computer,
  like the other window layout keys: no longer saved to a settings file,
  and kept when one is loaded.
- The trackpad gestures setting (diagramview/gestures) is in the part
  for toolbars, shortcuts and gestures rather than all other settings.
- Loading only the defaults for new projects now says it is done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 22:09:28 +13:00
ispyisail 449ba6eff0 Comments: DXF, honours, "Uses the same width as"
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:52:10 +13:00
Beat Hangartner 2192084483 Mirror a symbol twice and it comes back where it was
Mirroring a symbol on a folio keeps its centre in place and then snapped
the new position to the grid. A symbol that was not on the grid was
pulled onto it, so a second mirror the same way left it a few pixels
away from where it started. In perceuse.qet that is 171 symbols, each
way. tst_scriptmirror::mirrorBackAndSave() picks its symbol in an order
that changes from run to run, so it failed only some of the time (three
runs in eight on macOS, and on the Windows CI).

The move is now snapped to the grid instead of the position. A symbol on
the grid stays on it, one off the grid keeps its offset, and because
qRound() rounds halves away from zero both ways, a second mirror moves
it exactly back.

The new tst_scriptmirror::mirrorTwiceComesBack mirrors every symbol of
perceuse.qet twice, horizontally and vertically, and checks that every
terminal is back in place. It fails on master (171 symbols moved).

No change to the file format.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:48:13 +02:00
Beat Hangartner 6de8d406aa Give the toolbar and gesture settings tests their own settings scope
tst_toolbarsettings, tst_diagramtoolbarsettings and tst_gesturesettings
never set an organization or application name. On macOS and Linux,
QSettings then falls back to an "Unknown Organization" file, so the
tests pass there. On Windows it has no registry key: every write is
dropped and every read gives the default, and nine checks fail.

Each test now sets its own organization and application name in
initTestCase(), as tst_scriptingsetting and tst_sheetbackgroundsetting
already do. That also keeps the tests away from the settings of the
account that runs them.

Tests only; no change to the app or its files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:48:13 +02: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
ispyisail 24e67da33c Add saving and loading part of the settings (discussion #1405)
Save settings as... now asks which parts to save: toolbars, keyboard
shortcuts and mouse gestures; defaults for new projects (folio, title
block, wires, numbering); everything else. All three ticked writes the
same complete file as before (format 1, collection folders included).

A file with only some parts is format 2 and lists its parts. Loading it
replaces those parts and keeps every other setting. The folders of the
collections are never in such a file: they rarely exist on another
computer. A QElectroTech that only knows format 1 refuses it rather than
loading it as complete and losing every other setting.

A file holding only the defaults for new projects loads without closing
QElectroTech, since QETProject reads them again for every new project.

Which part a key belongs to is ConfigProfile::partOf(); a key it does
not list falls in "everything else", so a new setting can never be left
out of every part. The test checks that what BorderProperties writes for
the folio defaults lands in the new-project part.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:16:15 +13: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
Beat Hangartner 360065036c Spread several dropped pictures over the folio in a grid
Pictures dropped together were cascaded 20 px apart from the drop
point; once scaled to fit the folio they lay almost on top of each
other. They are now laid out side by side in a roughly square grid
over the folio's drawing area, inside the same 20 % margin, each
shrunk into its cell with its proportions kept. A single dropped
picture still lands on the drop point.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:00:31 +02:00
Beat Hangartner c76bc5eb4e Fit dropped pictures into the folio frame, with a margin
A dropped picture too large for the folio is now scaled to leave 20 %
of the drawing area free on every side, instead of being fitted to
the visible part of the view, and every dropped picture is kept inside
the frame -- a scaled-down one also off that margin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:00:31 +02:00
Beat Hangartner 034013ff67 Drop picture files on a folio to add them
Picture files (png, jpg, bmp, svg) dragged from the file manager onto a
folio are added there: the first centred on the drop point, the others
cascaded from it, and one undo step removes them all. A picture larger
than the visible part of the folio is scaled down to fit it. Files that
cannot be used are listed once after the others have been placed, and a
drop holding only other files (a .qet project) still reaches the main
window, which opens it.

The checks a picture must pass before it is embedded in the project
move into ImageDrop::load and are now shared by the drop, the add image
dialog and the script API: a regular file of at most 10 MB, and at most
64 megapixels, read from the header before any pixel is allocated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:00:31 +02:00
Beat Hangartner 606a526344 Undo restores whether a shape's pivot was placed by hand
A shape remembers whether its pivot was placed by hand: a hand-placed
pivot is saved and no longer follows the shape's centre when the shape
is resized. Dragging the pivot handle marked it as hand-placed, but the
undo step restored only the pivot and the position. After Ctrl+Z the
pivot was back at the centre and still marked as hand-placed, so it
stayed behind on the next resize and was saved as a <transform>.

pivotIsCustom becomes a property and goes into the pivot drag's undo
step, the same fix as #1367 for pictures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:59:17 +02:00
Beat Hangartner be8e2372a9 Cancel a picture's handle drag with Escape
Pressing Escape while a handle of a picture was dragged went to
DiagramView, which clears the selection. That removed the handles under
the mouse, so the drag stopped where it was: the picture kept the new
size, angle or pivot, and since the release never arrived, no undo step
was recorded and the change could not be undone.

Diagram gets a keyboardItem: an item in a state Escape must end. While
a resize, rotate, skew or pivot handle is dragged, the picture is that
item; DiagramView sends each key to it first, and only keys it does not
take reach the view's own shortcuts. Escape puts the picture back as it
was when the handle was pressed (position, transform, and whether the
pivot was placed by hand) and ignores the rest of the gesture, so no
undo step is recorded. The drag also ends when the handles go away.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:59:15 +02:00
Beat Hangartner 136eb08a1b Save the rotation of a picture turned with the rotate action
The rotate action (Space) turned a picture through QGraphicsItem's
plain rotation, which DiagramImageItem::toXml() does not save -- it
saves the rotation of the picture's own transform. The picture came
back unrotated once the project was reopened. Turn it through
"rotationAngle" instead, around its own pivot, and when a selection
is turned as a group, move the picture so that its pivot, not its
top-left corner, orbits the group's centre.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:59:13 +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
ispyisail 3b0824c3ef Element editor: mirror a multi-line static text by its widest line
PartText::mirror() measured the whole text as one line, so a text of
several lines landed off by the width of the other lines (18 px for
"A\nLONGER LINE" at 20 pt).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:31:16 +13:00
ispyisail 1db025ceae Element files: anchor="alignment" puts a static text's x at its alignment (#1251)
A static text's x/y in an .elmt file are the baseline-left of the text.
A program writing an element (a pinout generator, dxf2elmt) then has to
measure the text with QET's font metrics to right-align or centre it.

With the opt-in anchor="alignment", x is the left edge, centre or right
edge selected by Halignment; y stays the baseline. The box is the text
without the document margin, as ElementPictureFactory draws it, so the
editor and the folio agree. Rotation pivots on x/y as before.

The element editor reads it and writes it back for the texts that had
it; it does not add it to other texts. Files without anchor draw and
save as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:31:16 +13: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