stderr got it in the previous commit; stdout (the JSON of --info, the
messages of the exports) still ended its lines in "\r\n" on Windows
while stderr ended them in "\n". Both streams now give the same bytes
on every system.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
itemChange() removed the corner handles when the text left the scene
but did not bring them back when it returned still selected, which Qt
allows (undoing a scripted delete does it) without a selection change.
Refresh on every scene change. The two handle helpers are protected:
nothing outside the class calls them.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The box showed Auto for -1 while IndependentTextItem::setTextWidth()
treats every value up to 0 as automatic. Typing 0, or the first click
on the up arrow from Auto, then put a width change in the undo history
that changed nothing, left the box on 0, and in live edit every later
change of X, Y, angle, text or size was replaced by that empty command.
Auto is 0 in the box now, like in the setter: no number is the automatic
width, and the first step up is 1 px.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A dynamic text field is never edited inline in the element editor (it
never sets text interaction flags), so the handles never had to hide
for it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The slot was auto-connected by name to QComboBox::activated(QString),
which Qt 6 removed, so connectSlotsByName() found no signal and picking
a variable in the "Texte composé" dialog did nothing. activated(int)
exists in Qt 5 and Qt 6; the slot never used its argument and reads
currentData() as before.
Same fix as the CompositeTextEditDialog half of #674, which was closed
unmerged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With symbols as blocks, every non-empty information field of a symbol
(label, manufacturer, reference, supplier, quantity...) is written as a
hidden attribute of its INSERT, tagged with the field's name. A CAD
program can then list the parts from the drawing, as with AutoCAD's
attribute extraction; hidden, they change nothing on screen, in any
reader. A field already written as a visible attribute (with
--dxf-attributes) is not repeated. The label formula is left out: it is
how the label is made, not part data.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With symbols as blocks and the new "DXF : textes des symboles en
attributs" option (--dxf-attributes on the command line), a symbol's own
texts (its label, function, comment...) are attributes of its INSERT
instead of loose texts, so a CAD program moves them with the symbol and
can list or edit them. Each is written exactly where, and as, the text
was. A text showing an information field is tagged with its name
(LABEL, FUNCTION...), a typed text TEXT1, TEXT2...; a text's second line
gets _2. The block defines each tag once, placed where the first symbol
has it.
Its own option, off by default, because LibreCAD (2.2.1.5) does not
show attributes: the labels would vanish there. AutoCAD shows them.
The text geometry moves into textLines(), shared with the text loop,
so the attributes and the plain texts cannot drift apart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With "DXF : symboles en blocs" ticked in the export dialog, or
--dxf-blocks on the command line, each symbol definition a folio uses is
written once as a DXF block and every placed symbol as an INSERT of it.
A CAD program then selects, counts and replaces a symbol as one object.
The block is drawn by the same code as a symbol drawn in full, as if
the symbol sat unturned at the folio's origin, which is DXF (0,
sheetHeight): that is the block's base point, so the INSERT carries the
symbol's position and quarter-turn and nothing else. Blocks have to come
before any entity, so the export collects them first and dxfBegin()
writes them in its BLOCKS section. Entities in a block keep their own
layers. Off by default; with it off the file is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Circles and arcs: drawArcEllipse() split each curve into quarters and
fitted a circular ARC through a point half way between the curve and its
chord, so every circle came out as a flattened diamond. A circle or an
arc of one is now one exact CIRCLE or ARC. R10 has no ELLIPSE entity, so
a true ellipse is a polyline through the curve, one point every 5
degrees. The export also used a different scale on each axis, which made
round things oval; it now uses one, the largest that fits the sheet.
Title block at the right: the DXF laid the horizontal template out in
the narrow vertical strip, squashing it into the top corner. It is now
laid out horizontally and turned a quarter, as draw() turns the painter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Edit menu had grown to 42 entries and no longer fitted on a laptop
screen; Qt wrapped it into a second column. Select, Group, Rows and
columns and Depth now sit one level down, as Add and Align already did.
Search for a command, Shortcut bar and Repeat move to the end, beside
Search/Replace. No action, label or shortcut changes: Ctrl+A was checked
to still select everything from inside the new Select submenu.
Lignes et colonnes was already built as a submenu for the folio's
context menu; the Edit menu now uses the same one instead of a flat copy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
parseText() creates a QGraphicsSimpleTextItem for every <text> of an
element (the DXF export reads them later) and appends it to the
primitives it is given. build() draws every primitive twice, once for
the normal and once for the low-zoom picture, and gave the second pass
a fresh `primitives fake_prim` per primitive that went out of scope
with its text items still allocated. dropCache() also removed a
location's primitives without deleting their text items.
LeakSanitizer on the Linux CI sanitizer build (separate PR) stops
every --resave or export of a project whose elements have texts:
ERROR: LeakSanitizer: detected memory leaks
Indirect leak of 37200 byte(s) in 75 object(s) allocated from:
#1 QGraphicsSimpleTextItem::QGraphicsSimpleTextItem(QGraphicsItem*)
#2 ElementPictureFactory::parseText(...) elementpicturefactory.cpp:658
#4 ElementPictureFactory::build(...) elementpicturefactory.cpp:324
Give the low-zoom pass one set of primitives for the whole element and
delete its text items when both pictures are drawn; in dropCache(),
take the primitives out of the hash and delete their text items. The
items kept in m_primitives_H for the DXF export are unchanged, and the
destructor deletes those as before.
No file-format change, nothing drawn differently. Tests: the
sanitizer job on Linux fails on master with the report above and
passes with this change; ctest on macOS 64/64. The leak is invisible
to a plain test and to ASan on macOS, which has no LeakSanitizer.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Beat Hangartner <beat@hangartners.ch>
The exports, --info, --resave and --run return before QetLogger's
handler is installed, so they keep Qt's default handler. On Linux and
macOS it writes to stderr. On Windows it writes to the debugger when the
program has no console, and a GUI program started by another program
never has one: everything a headless run reported -- a wire that could
not be reconnected, which way the project database was filled, an
element that could not be read -- was lost to the caller, a test or a
tool like qet-mcp. Install a handler for these runs that writes to
stderr in the form the default handler uses on the other systems (the
default message pattern, UTF-8).
On macOS the stderr of --info, --resave, --run and --export-svg is
byte-identical before and after (memory addresses aside). On Windows,
tst_databasefromdocument and tst_terminaluuids, which read these
messages, fail before and pass after.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
In the element editor, a dynamic text field that is the only selected
item gets the corner handles of TextResizeHandles: dragging one sets
the width the field wraps to, keeping the opposite corner in place;
double-clicking a handle goes back to the automatic width. One undo
step per drag, on the editor's undo stack.
ElementScene keeps the handles of the same text when the undo stack
changes: pushing the resize itself calls managePrimitivesGroups()
again, and deleting the handles there would delete them while they are
still emitting.
No file format change (text_width on <dynamic_text> already exists).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A selected free text gets the corner handles of the previous commit:
dragging one sets the width the text wraps to, keeping the opposite
corner in place; double-clicking a handle goes back to the automatic
width. One undo step per drag, on the folio's undo stack. The handles
are hidden while the text is typed in.
DiagramTextItem shows the handles for a subclass that asks for them
(textResizeHandlesWanted()); only free texts do for now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A "Width" spin box in the properties of a free text sets the width it
wraps to (previous commit). "Auto" (-1) is the automatic width. It also
works with several selected texts, like the size and the angle; texts
with different widths show "Auto" and are left alone unless the box is
changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MSVC stopped with C2280 at liveserver.cpp:861 (issue #1333): it copies
the result of the ?: expression, and QPrinter cannot be copied. GCC and
Clang build the object in place under C++17, so Linux and CI built.
QPrinter(info) gives the same printer in both cases: info is only set
when printing to a printer, and QPrinter() itself constructs with an
empty QPrinterInfo (qprinter.cpp, QPrinter::QPrinter(PrinterMode)).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In the terminal strip manager, after a free terminal was moved into a
strip, clicking another free terminal showed nothing on the right. It
only came back after selecting a strip first.
The tree remembers whether a free terminal is selected and only tells
the window to update when that, or the selected strip, changes. Moving
a terminal rebuilds the tree, which clears the selection and shows the
empty page, but the "free terminal selected" flag stayed true. The next
click on a free terminal then looked like no change and was ignored.
The flag is now reset when the selection is cleared, and updated on
every change it takes part in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
layoutLines() trimmed the trailing spaces of every line and kept
non-breaking spaces, which the DXF export used to write as plain spaces
(toPlainText()). A DXF R10 file has no code page, so a reader assuming
Windows-1252 could show them as 'Â '. Now a paragraph that does not
wrap gives exactly its line of toPlainText().split('\n') again
(trailing spaces and tabs kept, non-breaking spaces as plain spaces,
<br> as a line break). In a paragraph that wraps, every line loses its
trailing spaces, so spaces that wrap to a line of their own give an
empty line: not written, but it keeps the spacing of the next lines.
Test: tst_textlines compares with toPlainText() for trailing spaces,
tabs, non-breaking spaces, <br> and paragraphs, and checks wrapped
lines (fails before).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- setTextWidth() returns without a signal when the width does not
change, which is also the case on every load of a text without one.
- Say why IndependentTextItem::textWidth()/setTextWidth() hide the
non-virtual QGraphicsTextItem functions, and what that means for code
holding a base pointer.
- The fixture no longer carries a /tmp path from the machine it was
made on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The independent_text table had the text and rotation of a free text but
not its width, and a width change did not update the row at all -- not
even its width and height, which follow the wrapped text. Add a
text_width column (NULL for the automatic width) and update the row on
textWidthChanged.
Test: tst_scriptfreetextwidth reads the table with qet.query() (fails
before).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A scene drops the event filters of an item that leaves it. The handles
installed theirs once, in the constructor, and only if the text was in
a scene: after the text was removed and added back (undoing a delete
does that), or when the handles were made before the text was in a
scene, they no longer reacted, and a drag moved the text instead.
TextResizeHandles now installs the filters again whenever it enters a
scene.
Tests: dragAfterLeavingTheScene, dragWhenCreatedOutsideAScene (both
fail before).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In the element editor, changing the width of a right-aligned or centred
dynamic text field (with the width box) moved its aligned edge: the
field always grew or shrank from its left edge, so a right-aligned
field's right edge moved. PartDynamicTextField::setTextWidth() now
keeps the point chosen by the alignment in place, with the
prepareAlignment()/finishAlignment() pair setPlainText() and setFont()
already use. Not while the field is read from the .elmt, whose saved
position is the one to keep.
No file format change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
updateForm() filled the box with setValue(textWidth()), and setValue(int)
truncates: a field 61.7 px wide showed 61 while editingFinished compared
with qRound() = 62, so leaving the box still set the width to 61 and
pushed an undo step. Round in both places.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Shortcuts page now shows:
- each command's icon, and a Menu column saying where it is
("Projet › Scripts"); the text filter matches that too
- a category filter beside the existing Bound/Unbound/Conflicts one
- "Rechercher par touche": press a key combination and only the
commands using it are listed, however the key is spelled in the
user's language. A key also finds the two-key shortcuts it starts.
- "Copier la liste": the shown rows go on the clipboard as
tab-separated text with a header, so they paste into a spreadsheet
one cell per column
ShortcutManager::ShortcutInfo carries the icon and the first live
QAction so the page needs no second registry.
New test tst_shortcutspage covers the menu path, the columns, both
filters and the copied text.
Second stage of discussion #1319.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A symbol text with a width (text_width) is drawn wrapped on the folio,
but the DXF export split texts on '\n' only, so it was written as one
long line, running past its box and over the drawing. The export now
writes the lines of folio texts as they are laid out
(TextLines::layoutLines(), new header-only textlines.h). Texts without
a width give the same lines as before.
Tests: tst_textlines, tst_dxfwrappedtext (fails without this change).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>