With "snap to bottom", each master's cross was centred under it at the
bottom of the folio on its own, so two coils in one column drew their
crosses on the same spot and the references printed over each other.
The crosses of a folio are now placed together: when two would overlap,
the one of the higher element goes above the other, so a column of
coils gets its crosses in the same order. Crosses that do not overlap
stay where they were. Moving an element sideways now re-places them
too, since it can move a cross into or out of another's column.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With SOURCE_DATE_EPOCH set, the document id came from the project's uuid
and a hash of the .qet file. A project generated again from the same
data has new uuids, so its PDF differed in the id alone (XMP DocumentID
and trailer /ID, 120 bytes) although every page was the same.
Qt now writes a fixed placeholder id, and once the file is written the
placeholder is replaced with an id derived from the bytes of the PDF:
the same pages give the same id. Same length, so no offset changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lupdate (run on qet_en.ts only) found strings added since the last
update that the English interface showed in French:
- wires per terminal (#1272, #1273): the rules in Settings > General
and Project properties, the experimental-feature warning, and the
tooltip on a terminal that refuses a conductor (both plural forms);
- Display > Show (#1268): the item kinds and the "%n kinds of items
hidden" status message;
- the 3D mouse settings page.
Terms follow the existing English: conductor (conducteur), terminal
(borne), sheet (folio), sheet reference (renvoi de folio). The menu
"Affichage" is "Display", so its "Afficher" submenu is "Show" rather
than lupdate's guess "Display". lrelease reports 3573 finished,
0 unfinished. Other languages are untouched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
qet_element_search matched every word as a substring of the names and
the path, so "NC contact" offered a remanence coil ("nc" in "remanence",
"contact" in its folder) and an outlet before any auxiliary contact,
"normally closed contact" found nothing, and "emergency stop" ranked a
maker's part and two assembly-plan drawings above the push button.
- A word of one or two letters must now be a whole word. Longer words,
and short ones with a digit, still match inside a word, so German
compounds ("schutz", "klemme") and "3p" find what they found before;
on 37 sample queries no result is lost.
- NO, N/O, "normally open", "normalement ouvert" are one word, and NC,
N/C, NF, "normally closed", "normalement fermé" another.
- Symbols under *_graphics, *_manufacturers_articles and
*_miscellaneous_unsorted come after every other match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Circle and ellipse had a blue square handle and the arc none; all three
now have hollow 3 px handles and 1 px lines, like draw-line. Breeze draws
transform-crop as a crop mark at 16 px and a dashed box at 22, so a 16 px
dashed box in ico/breeze-added/16/ replaces Breeze's 16 and 32 px art.
The test set the elements folder in a settings file under XDG_CONFIG_HOME,
which macOS does not read, so common:// paths did not resolve and the
first placement failed. It now passes --common-elements-dir.
The icon section was one Breeze (LGPL) paragraph that listed GPL and
CC BY-ND icons in its text, so a DEP-5 reader saw every icon as LGPL.
Each license now has its own Files paragraph and License text.
The search panel's close button and the five KDE Breeze icons that sat in
ico/breeze-icons/ now come from ico/breeze/, with 16, 22 and 32 px art.
André's three SVGs moved from ico/generated/ to ico/scalable/. Removed the
18 .svgz files and the QETIcon and QETOxygenLogo icons nothing used.
Review of #1272: scorpio810 asked for a warning box for users; the inline
box added in 9a55939bb is replaced by a pop-up, as the element editor
gives for unnamed terminals. When OK in Settings > General or in Project
properties turns a rule on (a limit where there was none, or one wire
per folio report), a warning says the rules are experimental, may change
with settings to redo if wires and conductors become separate objects,
and can all be turned off in Settings > General. OK goes on. Changing a
limit that was already on, or turning rules off, shows nothing.
tst_wiringrules: when the warning is due (WiringRules::turnsRuleOn()).
Checked in the GUI from both pages, and that a plain OK shows nothing.
English for translators: "Warning"; "The wires-per-terminal rules are an
experimental feature. They count wires as QElectroTech represents them
today. They could change, and your settings need redoing, if wires and
conductors become separate objects in a future version. All these rules
can be turned off in Configure QElectroTech > General."
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
The DXF export warns that pictures become outlines. Pictures hidden by
View > Show are left out of the export, so they no longer trigger it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1272 (scorpio810, after arummler's note that the rules would
need redesigning if wires and conductors become separate objects): a
warning box, with the standard warning icon, at the top of the rules in
Settings > General and in Project properties. It says the feature is
experimental, counts wires as QElectroTech models them today, and that
the rules, and the user's settings, could change if wires and conductors
are separated.
English for translators: "Experimental feature. These rules count wires
as QElectroTech represents them today; they could change, and your
settings need redoing, if wires and conductors become separate objects
in a future version."
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
The project's CI builds QElectroTech without scripting, where --run is
not an option and the process never exits, so the test waited out its
timeout and failed (#1272's stack, CI run 37118548599). The --run checks
now skip there, as the script tests are left out of such builds; the
rules and the save round trip still run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
With "one text per potential", deleting the wire that carries the text
moves it to another wire of the potential, but only if the text
isVisible(). While wire texts were hidden by View > Show none was, so the
text was never moved and the potential came back with no text.
ShownKinds::wantsVisible() also counts a text hidden only by its kind.
Also drop the always-true/false enum range checks clang warned about.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The limits were per project only. They are now set once in Settings >
General, under the master switch: the most wires per terminal and "one
wire per folio report". Every project follows them.
Project properties > "Conducteurs par borne" gets "Utiliser les réglages
de l'application", ticked by default, showing the application's values
greyed. Unticked, the project keeps its own values, saved in the project
as before, so a drawing that needs its own limit carries it to any
computer. A project's own "no limit" overrides an application limit too.
QETProject::wiringRules() now returns the rules in force (the project's
own, or the application's), so the code that checks a wire is unchanged;
projectWiringRules() returns what the project itself stores. A project
that follows the application still writes nothing, so it saves exactly as
before.
tst_wiringrules: which rules win, the save round trip of a project's own
"no rule", and through the real binary that the application's limit
refuses a wire unless the project overrides it or the master switch is
off (checked to fail with the application's rules ignored). ctest 55/55.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
qet.deleteElement() built its DeleteQGraphicsItemCommand from the element
alone. The Delete key's selection also carries the wires on the element's
terminals (DiagramContent's conductors to update), and the command removes
those with it. From a script they stayed: still listed by
qet.conductorUuids(), still saved, attached to an element that was gone.
The wires on the element's terminals now go into the command, as for the
Delete key. tst_scriptconductoruuid checks that no wire is left with an
end on the deleted element (it failed before this change: 1 left).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
Discussion #1158 asks for a limit on the wires per terminal. Before any
limit, two read-only checks let anyone count their own projects:
- crowded_terminals: terminals with more than four wires (two double
ferrules, one either side of the screw), folio reports left out;
- reports_with_several_wires: folio report arrows with more than one
wire, the rule plc-user proposes for reports.
Both are info, not errors: cable, busbar and single-line symbols carry
more wires on purpose, and a third of the shipped examples' reports have
several. Over the 24 examples: 23 crowded terminals in 10 projects
(the worst, 19, is a cable symbol in affuteuse_250h.qet) and 130 reports
in 4 projects. Plain SQL over the conductor table, so they work on any
current QElectroTech.
test_qet_mcp: the checks are read-only, the exact-answer test lists them,
and a coil wired to five others is reported once with its count (checked
to fail with the threshold raised). 398 tests pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
Discussion #1158, stacked on the limit itself. Two of QElectroTech's own
tools wire several terminals as a star, every terminal to one of them:
"create wires in a drawn polygon" (also qet.addConductor()), and deleting
a symbol, which rewires the far ends to keep the potential. Six terminals
give the hub five wires, which the project's limit forbids.
When the project sets a limit (and the master switch is on), both now
wire the terminals one after another instead, from the top left one to
the nearest not yet wired (WiringRules::chainOrder()). The polygon tool
also skips a wire whose terminal is already full. Without a limit, both
build the star as before.
tst_wiringrules: the chain order, and through the real binary that
deleting a symbol wired to four others leaves them with at most two
wires each under a limit, three on one of them without (checked to fail
with chaining turned off).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
qet.deleteElement() built its DeleteQGraphicsItemCommand from the element
alone. The Delete key's selection also carries the wires on the element's
terminals (DiagramContent's conductors to update), and the command removes
those with it. From a script they stayed: still listed by
qet.conductorUuids(), still saved, attached to an element that was gone.
The wires on the element's terminals now go into the command, as for the
Delete key. tst_scriptconductoruuid checks that no wire is left with an
end on the deleted element (it failed before this change: 1 left).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
Discussion #1158: a real terminal takes one or two wires (four with
double ferrules), a folio report is a virtual point that carries one, but
QElectroTech lets any number of wires be connected anywhere.
Project properties > General gets a "Conducteurs par borne" group:
- the most wires a terminal may take (0, the default, is no limit);
- "one wire per folio report".
When set, a new wire that would go past the limit is refused: the
terminal shows red while dragging, as for any refused link, and a tooltip
says why on release. Auto-connect on placing, moving or multi-pasting a
symbol skips full terminals, and qet.addConductor() refuses with its own
message. Wires already drawn, opening, pasting and undo are never
refused.
Settings > General gets a master switch, on by default, that turns every
rule of this feature off in every project; the project group is greyed
with a note while it is off.
The rules live in wiringrules.cpp. The project setting is saved as
<wiring_rules .../> only when a rule is on, so a project that never used
it saves exactly as before; an older version drops it, which means "off".
tst_wiringrules covers the rules, the save round trip and, through the
real binary, that a wire past the limit is refused unless the master
switch is off (checked to fail with the limit check removed).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
A submenu under View (Affichage > Afficher) with one tick per kind:
symbol texts, wire texts, free texts, shapes, pictures, tables and
cross-references. Unticking one hides it on every folio of every open
project; the status bar says how many kinds are hidden. Session only,
not saved in the project.
Each item is tagged with its kind by its constructor (ShownKinds::tag),
so an item created while its kind is hidden starts hidden whatever
created it. A tag rather than type(): the cross-reference under a
contact's label is a plain QGraphicsTextItem.
Items that already hide themselves (wire text switched off, one text per
potential, a cross-reference snapped elsewhere) now go through
ShownKinds::setVisible(): the kind state can only veto a show, and
apply() re-shows only what it hid itself (hidden_key), never what an
item hid. Conductor::updateTextVisibility() replaces the same visibility
rule pasted in diagrampropertiesdialog.cpp and qetscriptapi.cpp.
Hidden items are not selectable (Qt), so Select All, copy and delete
skip them. Print, PDF and image export render the scene and leave them
out; the DXF export walks items itself and skips them through
ShownKinds::isHidden(). CrossRefItem::linkedChanged() keeps following
its slaves while hidden only by kind, so its label is right when shown.
Known limitation: QGraphicsScene::itemsBoundingRect() still counts hidden
items, so Zoom to fit and borderless image export keep their space.
Verified: tst_shownkinds (12 cases, two mutation checks) and ctest 55/55;
GUI hide/show of symbol texts, wire texts (incl. one text per potential),
free texts, shapes and cross-references returns pixel-identical folios;
with nothing hidden, --export-dxf of the 24 examples is byte-identical to
master (133 files) and --export-pdf renders identically (133 pages).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
A wire is straight only when its two terminals are exactly in line, and
qet_edit placed a symbol only by its origin, so an assistant had to guess
where the terminals would land -- and usually missed by a few pixels.
- place_element adds a symbol with one of its terminals in line with
another symbol's, gap pixels away on the side that terminal faces, and
notes when the new symbol's terminal faces the wrong way to meet it.
- align_terminal moves a placed symbol across so two terminals line up.
- align_elements lines up symbols' boxes (left/center/right/top/middle/
bottom), as Edit > Align does.
- distribute_elements spaces symbols' origins evenly, or a pitch apart,
keeping symbols on the grid on it.
place_element and align_terminal need qet.terminalPosition(); on a build
without it the edit stops before running, naming the missing call, and
every other op still works there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
A script laying out a drawing could read where a symbol is
(elementGeometry) but not where its terminals are, so it could not place
one symbol with a terminal exactly in line with another's -- the one
thing that makes the wire between them straight. And it could read a
wire's drawn path only through conductorSegments(), which names the wire
by a terminal and so refuses any terminal carrying two wires: 282 of the
3120 wires in the shipped examples.
terminalPosition(folio, element, terminal) returns where a wire docks on
the terminal, in folio coordinates, and which way it leaves (n/e/s/w,
the element's rotation included). conductorPath(folio, uuid) returns any
wire's drawn path as points, by its uuid.
tst_scriptlayoutreads checks the two against each other on every wire of
a fixture: each path starts and ends where its terminals' positions say,
and leaves each the way it faces; where conductorSegments() can name a
wire, both give the same points; and a quarter turn turns the facing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD