Reported on #1178. Two things kept --common-elements-dir from working for
--run and the command-line export:
- main() runs both before QETApp exists, and only QETApp::parseArguments()
applied the folder options, so they were ignored. The application of
those options is now QETApp::applyDirectoryArguments(), static, and the
two headless branches call it too. They also drop the options from the
arguments they pass on: the export reads its project and output by
position, so "--export-pdf --common-elements-dir=X p.qet out.pdf" took
the option for the project ("Project not found").
- commonElementsDir() read the saved collection path before the
command-line one, so a user's own setting beat a folder given for this
run. The command line now comes first, in the GUI too.
Tested with a --run script adding a symbol that exists only in a given
folder: master fails with or without a saved collection path; this
resolves it, before or after --run, and over a saved path to another
folder. Without the option it still fails, and a saved path to the
folder still works.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELKpbGxqJd7EFiTUypBtVe
lupdate had added them, but nobody filled them in, so the English
interface showed these strings in French: align, group and ungroup,
arc and fillet, centre on cursor, paste at the original position, wire
hops, the collection prefixes, the materials list, saving and loading
settings, the terminal-name check in the symbol editor, recovery files,
the macro recorder's messages and a few more.
About 30 of them were pre-filled by lupdate from an identical string
elsewhere and left unfinished; some of those guesses were wrong (a
dialog's "Annuler" offered as "Undo"). All are reviewed here. Plural
messages get both English forms. lrelease now reports 3487 finished,
0 unfinished. Other languages are untouched.
Terms follow the existing English: sheet (folio), terminal (borne),
conductor (conducteur), parts list (nomenclature); "liste de matériaux"
is "materials list" so it is not confused with the parts list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELKpbGxqJd7EFiTUypBtVe
Reported on #1178 after a first try of live mode on macOS:
- The English interface showed the live-mode setting in French. The
strings were in lang/qet_en.ts but untranslated. This fills in English
for live mode, the script buttons, the macro recorder and the Connect
an AI assistant dialog (101 entries); other features' untranslated
English entries are left for their own change.
- The setting is on Configurer QElectroTech > Général > Projets, but the
start-up warning, the MCP server's error and its README all said
"Configurer > Général". All three now name the Projects tab, and the
README and the server name the English menus and buttons.
- qet_live_screenshot grabbed the whole viewport, so a tall window around
a landscape folio came back mostly empty. It now crops to the folio's
frame and title block where they are on screen (the whole viewport if
the folio is scrolled out of sight), and says so in cropped_to_folio.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Rename the `near` lambda in Conductor::setPathPoints() to `isNear`:
`near` and `far` are empty macros in the Windows headers (minwindef.h).
- Reword the log when setPathPoints() refuses a route. "does not join the
two terminals" named only one of its reasons; the router always returns
the two terminals and at least one exit point, so a refusal means the
route is not a run of horizontal and vertical segments between them.
- README: running along another wire is penalised, not forbidden, so two
wires can end up drawn on top of each other.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The note the op returns reads "...; frame 1223.25 x 791.25 pt"; the README
quoted it as "PDF page".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On macOS, Qt names the settings subfolder after the organization domain
when one is set, so QElectroTech read <folder>/qelectrotech.org/ while
main.cpp, tst_settingsdir and qet_mcp.py all use <folder>/QElectroTech/.
Clear the domain when QET_SETTINGS_DIR is set, on macOS only. Nothing in
QElectroTech reads it; SingleApplication hashes it into its key, so these
runs no longer hand off to an open QElectroTech.
test_qet_mcp.py: find true on PATH (/usr/bin/true on macOS), and compare
the found binary with samefile() on case-insensitive file systems.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
injectCrossRefLinks() drew each link as it found it: the contacts of a
cross-reference come from a QMultiMap keyed by Element pointer, and the
scene's items in stacking order. Both change from run to run, so the same
project exported twice gave two different PDFs whose only difference was
the order of the link annotations.
The links are now collected and drawn sorted by their rectangle on the
page (top, left, bottom, right), with the target as the tie-break. The
links themselves are unchanged. The print window's PDF export goes
through the same function.
tst_pdflinkorder exports examples/industrial.qet five times in separate
processes and requires the same links in the same order. It fails on
the old code (lists differ at index 2).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The preset code uses QPageSize and std::floor whether or not QtPdf was
found; it compiled only because other headers bring them in.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The router costed every grid edge against every symbol and every wire
segment on the folio. On the 191-symbol, 366-wire Polonez example that
is about 145 million tests and 700 ms per route, so a qet_edit routing
some 40 wires there ran into the scripting run's 30-second limit.
Each symbol and wire now visits only the grid edges it can touch, found
by binary search, and applies the same exact test to them as before, in
the same order. Rerouting every conductor of the 23 example projects
gives the same 11,018 saved segments as before, now 22 ms per route on
Polonez instead of 700.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
setFolioBorder(folio, "preset", "tabloid-landscape") picks the column and
row counts and whole-number sizes that fill the sheet best without going
over it, as one ChangeBorderCommand. The title block and headers are
measured from the folio, so it holds for any template on either edge.
Sizes stay whole because the folio properties panel edits them in whole
pixels. Among fills within 1.5 pt of the sheet, the size nearest the
current one wins: from a new folio tabloid landscape is 23 x 70 by 12 x 82,
not 9 x 179 by 5 x 197. A0-A5, letter, legal, tabloid/ledger, each
-portrait or -landscape; folioPresets() lists them, and folioBorder() reads
"width" and "height" of the frame and title block.
Every preset exports on its sheet; landscape ones rely on the previous
commit, which turns a wide page before matching it to a sheet.
The MCP server checks the name up front, probes for folioPresets(), and
the op's note says what was chosen and the frame's size in points.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The PDF export sizes each page to its folio, and QPageSize rounds a size
within 3 pt of a standard sheet to the sheet. QPageSize knows the sheets
upright only (bar Ledger), so only portrait folios snapped: an A3 landscape
folio came out as a 1190 x 841 pt page, while an A3 portrait one was
842 x 1191, the sheet. A wide folio is now matched upright and turned.
tst_pdfpagesize exports a fixture with an A3 folio each way and checks both
pages are A3; it fails without the change (1190 x 841).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Most shipped symbols leave their terminals unnamed, so --export-wiring
wrote both terminal columns empty and a reader had to work each end out
from the .qet: terminal uuid, then its place in the definition, then the
index. The terminal table now keeps the index the scripting API's
addConductor() takes, from both fills of the database (the document and
the folios give the same value: Element::parseTerminal()'s sort, top to
bottom then left to right), and wiring_list_view and the CSV gain
from/to_terminal_index and from/to_terminal_uuid as their last columns.
The index is left empty for two terminals at one point, where the sort
is not stable and the index is not defined; the uuid tells them apart.
The wiring list dialog reads named columns and is unaffected.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A new conductor gets the default two or three straight segments, which
run through whatever symbol or wire lies between its terminals; scripts
then fix it segment by segment with moveConductorSegment().
ConductorRouter finds an orthogonal path on the folio grid that leaves
and enters each terminal in its own direction, keeps clear of every
element's rectangle, stays inside the border, and charges for bends and
for running along or crossing other wires. qet.routeConductor() and
qet.routeConductorBetween() apply it through Conductor::setPathPoints(),
which pushes the same ChangeConductorCommand a handle drag does, so the
path is saved, survives a reload, and one undo restores the default.
Where no route exists the wire keeps its path and the call says so.
qet-mcp: add_conductor takes "route": "avoid", and a route_conductor op
reroutes an existing conductor (by terminal or by uuid). The router
methods are required only by an edit that routes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With LongPathsEnabled set in the registry, Windows lets a program open
paths of 260 characters or more only if its manifest says longPathAware.
QElectroTech.exe so far carried the toolchain's default manifest. This one
repeats what the default declares (supported Windows versions, asInvoker)
and adds longPathAware. MSVC takes the .manifest as a source; MinGW gets it
through a small resource file.
Follow-up to #1178 (symbols in deep folders).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A symbol file that exists but cannot be read has a null uuid, which the
check against the copy already embedded reported as "would collide with a
different element". Say that the file could not be read, with its full
path and length, since a long path is the usual cause on Windows.
tst_unreadableelement makes the file unreadable through its permissions
and fails on master with the old message.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ElementsLocation::pugiXml() and the qet_directory name lookup opened files
with pugixml's load_file(), which on Windows fails once the full path
reaches MAX_PATH. QFile handles long paths, so exist() and the import
succeeded while uuid(), the name, the informations and the thumbnail of
the same symbol came back empty. Read the bytes with QFile and hand them
to load_buffer().
Reported on #1178 as a false collision from qet.addElement.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With "one text per potential" on, the number is drawn on the
potential's longest conductor. When two conductors tie for longest,
longestConductorInPotential() kept whichever it met first, iterating a
QSet<Conductor *> in pointer order -- which changes from run to run. The
same file, exported twice, could put the number on either conductor:
same numbers, different PDF.
A tie is now broken by where the conductors' ends are on the folio, then
by uuid. Not by uuid first: a file with no conductor uuids gets fresh
ones on every load, and a project generated again by a script gets new
ones each time, while the drawing is the same.
tst_potentialtextcarrier exports a fixture with such a tie eight times;
before the fix it failed in each of three tries.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
XmlElementCollection::importCategory() returned the project collection's
first category, taken to be "import". A project can hold other
top-level categories before it: examples/lmdg.qet has "k_elem" first.
Imported symbols were then filed under k_elem/, while every lookup of an
imported symbol is under import/ (QETProject::importElement(),
XmlElementCollection::addElement()). The first placement worked; the
second could not find the copy, tried to import it again and failed.
In the editor the drop silently placed nothing; a script's
qet.addElement() returned "".
Look the category up by name, falling back to the first one as before
when a project has none called "import".
Test: tst_importcategory places the same symbol twice in a copy of
lmdg.qet through --run. It fails without the fix (the second placement
returns "") and passes with it. ctest 45/45.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Selecting several parts by clicking them in the Parts list, then dragging
one on the drawing, moved only the part under the cursor; the others
stayed where they were. No selection handles were shown either.
updateSelectionFromPartsList() blocks the scene's signals while it copies
the list selection to the drawing, so selectionChanged never reached
ElementScene::managePrimitivesGroups(), which sets up the decorator that
moves a multi-part selection. Call it once the signals are unblocked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>