Hold the right button on a folio and drag: a ring appears around the
point where the button went down, showing up to eight commands, and the
one in the mouse's direction is highlighted with its name underneath.
Releasing runs it; releasing near the centre cancels. These are
SolidWorks' mouse gestures.
The commands are the shortcut bar's for the selection, placed clockwise
from the top, so customising the bar customises the ring. As with the
context menu, a right press selects what is under the mouse first.
A plain right click still opens the context menu, now on release on
every platform. The view tracks the right button and opens the menu
itself; the platform's own right-click event (sent on press on X11, on
release on Windows) is ignored while gestures are on, so the menu is
never opened twice. The keyboard menu is unchanged.
The view keeps out of the way while a tool or a placement is running,
since a right click cancels or finishes those, and while a text is
being edited. The new General option "Gestes de la souris avec le
bouton droit" (diagrameditor/mouse_gestures, on by default) turns it
off and restores the previous right button exactly.
Discussion #1033.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
(cherry picked from commit a625142f36e619bb4d4f3551b6086f2dd8eabc86)
After a click that selects something on a folio, a small row of commands
appears just above and to the right of the cursor, as the SolidWorks
context toolbar does. It fades as the mouse moves away and is gone past
200 pixels; a click elsewhere, the wheel, a key press or an emptied
selection hide it too. Clicking a command leaves it up, so rotate can be
clicked again.
The commands are the shortcut bar's for that selection -- elements or
conductors -- at most eight, so customising the bar customises this as
well. It is a child of the view's viewport, never a window, and never
takes the focus.
It is not shown after a drag (moving items, a rubber band), while
placing or drawing (Diagram::eventInterfaceIsRunning()), on
a read-only folio, or when switched off with the new General option
"Afficher les commandes près de la sélection" (diagrameditor/
context_toolbar, on by default).
ShortcutBarSettings::contextFor() now decides the context for both the
bar and this toolbar.
Discussion #1033.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
(cherry picked from commit 78f3c1a2bc5c3cf1f7beae4bd60aefec20847d6d)
Right-click the shortcut bar, or click the "…" button at its end, and it
turns into a small window holding two lists: the bar's commands, left
to right, and every other command. Drag a command onto the bar, off
it, or to another place on it; a double click moves it to the other
list. Terminé saves and shows the bar again where it was, with the
result; Annuler, Esc or closing the window leaves it as it was.
"Valeurs par défaut" puts back the defaults for this context.
A Qt::Popup closes on a press outside it and holds the mouse grab, so
the bar is re-shown as a Qt::Tool window for the time of the edit.
Only QSettings is written, through ShortcutBarSettings, never the
project's undo stack.
The popup now looks the commands up itself (popUpShortcutBar(pos,
context)) instead of being handed actions, since it has to rebuild
them after an edit. ShortcutBarSettings::availableIds() lists what can
go on the bar, shared with the configuration page. An empty bar still
shows its "…" button, so it can be filled again.
Discussion #1033.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
(cherry picked from commit 4145d4bec1d403126b2a6587105aea47092db8a2)
Pressing S on a folio opens the element picker at the cursor with a row
of commands above it, chosen by what is selected, like the SolidWorks
shortcut bar:
nothing selected insert last element, element picker, text, line,
rectangle, terminal strip plan, paste, folio
properties
elements selected rotate, rotate texts, edit, copy, cut, delete
only conductors reset path, edit, delete
Each row is a list of ShortcutManager ids, so any registered command can
go on it and the bar carries no command list of its own. The lists are
in QSettings (diagrameditor/shortcut_bar/<context>); a context the user
has not changed follows the defaults. A disabled command keeps its
place, greyed, so a row looks the same each time. Clicking a button
closes the bar and triggers the action.
A new configuration page, "Barre de raccourcis", edits the three lists:
add, remove and reorder any diagram editor command.
To make that possible:
- ShortcutManager::action(id, owner) returns the action a given window
registered under an id, since each editor window registers its own.
- The add-item actions (text, image, shapes, terminal strip plan) are
registered as diagrameditor.add_<kind>, with no default key. They also
appear in the Shortcuts page and can now be bound.
Discussion #1033.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
(cherry picked from commit 53c1e213282b9f7226ca1c09e81703113236bb88)
DiagramEventAddElement is already a good placement mode: the element
follows the cursor on the grid, a left click drops it, Space rotates it,
and it stays loaded for a run of the same symbol. Its only caller was
DiagramView::handleElementDrop(), so it could be reached only by
finishing a drag. Double-clicking a symbol in the Collections dock
opened the element editor instead.
- DiagramView::startElementPlacement() is split out of
handleElementDrop(). defaultPlacementPos() uses the cursor when it is
over the view and the centre of the visible area otherwise.
- ElementsCollectionWidget emits insertElementRequested() on double
click, or Enter on the highlighted item. The host decides which view
receives it, so the widget can later be reused outside the editor.
- When there is nowhere to place it (no folio open, read-only project),
the element editor opens, as a double click did before.
- "Insérer le dernier élément" (Édition menu, default key A) places the
last element again. DiagramView reports every placement it starts, so
an element dropped by drag counts too. Macros are not remembered.
A rather than Space: Space rotates the pending element inside placement
mode and is bound three more times in this editor. The key is a
ShortcutManager default and can be changed in the Shortcuts page.
Double click placing is a behaviour change, so it has a preference,
"elementscollection/double-click-inserts" (default true), shown in
Configuration as an opt-out: "Double-cliquer dans la collection ouvre
l'éditeur d'élément au lieu de l'insérer".
Discussions #676 and #1033.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
Drop 1:2.5: on a grid of 10 it steps by 4, which misses 10, so texts on
two elements 10 apart could never line up. Every remaining divisor is a
whole number, which tst_textgrid checks.
The divisor list and the snapping arithmetic move to the header-only
textgrid.h so the toolbar menu, the preferences page and the test share
them. The preferences page gets the same choice under Grille + Clavier;
QETApp::textGridChanged keeps every editor's toolbar button in step.
While element texts are dragged, the status bar names the text grid and
says to release Shift and hold Ctrl for free placement -- Ctrl+Shift
together is the pan shortcut.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
The popup now also takes a folio position before the cell, the way
cross references and folio reports write it by default (%f-%l%c): 3-B13
offers "Folio 3 (<title>), case B13", and Enter shows that folio and
zooms onto the cell. Case and a leading "p" are ignored, and the
separator is optional: 3-b13, 3b13, P3B13 and p3-b13 all work. A plain
B13 still means the current folio, and P3 alone is still row P,
column 3: a folio always needs a cell after it. A folio position past
the last folio, or a cell outside that folio's border, offers nothing.
The zoom on another folio is queued, so it runs once the tab switch
has been handled. Checked on the ATS example: jumping to 1-C5 from
folio 3 gives the same view, pixel for pixel, as C5 typed on folio 1.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
Typing a cell reference into the "Atteindre un élément" popup now offers
"Case B13"; Enter zooms the view onto that cell with one cell of margin.
The cell is read the way the border labels it: row letter(s) then column
number, honouring the "columns start at 0" setting and multi-letter rows
(AA, AB...). Cells outside the folio are not offered.
BorderTitleBlock::cellRect() is the reverse of convertPosition(); a
round trip over every cell of a 30x23 folio, under both column-numbering
settings, returned the same cell for all 1380.
When an element is labelled exactly like the cell (K1), the element stays
first so Enter keeps its old meaning; the cell is listed after it.
DiagramView::zoomToRect() re-centres from a queued call: zooming in makes
the scroll bars appear, and the viewport resize that follows is anchored
under the mouse (setResizeAnchor(AnchorUnderMouse)), which otherwise
scrolls the view away from the cell straight after the zoom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
The 3D mouse's pan and zoom speeds were fixed guesses, and each sample
was applied as it came, so the speed on screen depended on how often
the driver sends samples -- different for every platform and device.
Motion now goes through SpaceMouseMotion::map(), which scales each
sample by the time since the previous one, and applies the user's
settings from a new "Mouvement" section of Configuration > Souris 3D:
pan and zoom speed, a dead zone, inverting each axis, and zooming by
push/pull (as before) or by twisting the cap. The defaults keep the
previous behaviour. Zoom is now exponential in the deflection, so the
factor stays positive however hard the cap is pulled (1 + z/1000 went
negative past z = -1000) and an equal push and pull cancel out. Sub-
pixel pan is carried over between samples instead of being rounded
away. The backend now reports all six axes.
tst_spacemousemotion covers the mapping without a device and is built
whether or not QET_ENABLE_SPACEMOUSE is on. The new behaviour was also
checked end to end with tools/spnav-shim (qelectrotech-docker): twist
with a dead zone of 10 ignores push/pull and small drift, and a twist of
60 gives the same frame as a push of 50 with the defaults.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each information field of the element properties now offers, as a
drop-down while typing, the values the other elements of the project
already carry for it: supplier, manufacturer, reference...
The values come from the project database's element_info table, not
from a walk over the folios. Spellings that differ only by case are
offered once, as most elements spell them, and the edited element's
own value is left out. The field key is checked against
elementInfoKeys() before it becomes part of the SQL text.
Browsing the list with live edit on applies each highlighted value,
as typing applies each keystroke; ChangeElementInformationCommand
merges them, so this still leaves one undo entry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5b0785fcc routed most reads of auto_num_locked, potential_isolating and
exclude_from_bom through QET::infoFlagIsTrue(), which accepts the same
spellings as the parts-list query (true/1/yes/on, trimmed, any case).
Four checkbox reads still compared against a literal lowercase "true":
the "exclude from parts list" box in the folio properties panel, and all
three flags in the element editor.
A value such as "True" or "1" from a hand-written .elmt/.qet was
therefore left out of the parts list, but shown unticked in both panels,
and pressing Apply there wrote back "false" and flipped the flag.
Revives the unconverted part of PR #785.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Picking a sheet (folio) background colour in the diagram editor was lost
on every restart. Diagram::background_color is a static initialised to
white and PaletteGraphicsView's s_custom_bg a static bool, and neither
was ever written anywhere -- Diagram::toXml() carries no colour attribute
either -- so closing and reopening a project always came back on the
default and the choice had to be made again.
Store it in QSettings under diagrameditor/sheet_background_* as a pair of
values rather than one: the colour, and whether it was picked explicitly.
Both halves are needed. "#ffffff, follow the system" and "#ffffff, always
white" are the same colour and two behaviours -- the first is what the
views invert on a dark palette -- so keeping only the colour would
silently turn one into the other on the next start, which is the reported
problem one step removed.
The colour is written as HexRgb on purpose. The SVG export gives
Diagram::background_color an alpha of 0 to render a transparent
background and never puts it back, and that transient value must not be
persisted as a permanently transparent sheet.
Applied from main() after the headless export and scripting branch -- those
return before reaching it and must keep rendering on plain white, the rule
ProjectPrintWindow already enforces for printing -- and before QETApp is
constructed, since that constructor already loads the projects given on
the command line. The GUI export dialog is left alone: it renders through
drawBackground(), so what you see is what you export, as it already was
within a session.
Saved at the moment the colour is applied rather than at shutdown, so
neither the print window's temporary white nor the SVG export's alpha can
reach it. The button's constructor now mirrors the stored state instead of
always claiming "system colour", and the "recently used" list is stored
alongside it.
Covered by tst_sheetbackgroundsetting, which pins the custom flag and the
dropped alpha -- the two rules a single stored colour would lose.
Add a new cross reference setting per xref type (coil, protection,
commutator, PLC), labelled "Afficher tous les esclaves definis par le
maitre" and persisted as showallconfiguredslaves. When it is enabled,
the contacts display is selected and the master declares contact
groups, the contact comb draws every contact group of the master in the
master's own order, even when no slave is linked to it yet. Masters
without declared contact groups and the option turned off keep the
previous behaviour exactly: linked slaves only, sorted by position.
- XRefProperties: new property stored in the settings and in the
project XML (attribute showallconfiguredslaves, absent means false so
old files are unaffected), included in operator==.
- XRefPropertiesWidget: new checkbox placed after the terminal names
one, enabled only while "Afficher en contacts" is selected; its
enabled state is now also set explicitly when a type is loaded (a
radio button that does not change emits no toggled()).
- For the PLC type the contacts/cross radios, the two display
checkboxes and the cross options group are hidden: a PLC master is
always drawn as its IO table, those settings have no effect there.
Positioning and label settings, which the table really uses, stay.
- CrossRefItem: free slots draw the symbol of the group plus the
terminal names the master defines (pairs swapped for a single pole
NO/NC contact, labels of a changeover contact rotated one step
counter-clockwise), without position text and without hover/click.
Linked slaves keep drawing from their own data at their assigned
group position; links without a group are appended at the end in
position order.
- Xref lifecycle: the item is created and kept without linked slaves
for snap-to-bottom (MasterElement::mustShowXrefWithoutSlave) and for
snap-to-label (DynamicElementTextItem::updateXref and
ElementTextItemGroup::updateXref, which now also run when the element
lands on the scene and re-establish their project connection), so a
freshly placed master shows its comb immediately instead of only
after the next settings change. updateLabel() resets its geometry
when the option is turned off again, so no stale ghost stays.
ConductorColorToolButton::applyColor() only recoloured the selected
Conductor objects. A wire drawn across a junction is several separate
Conductor segments sharing one electrical potential, so selecting one
segment and picking a colour left the rest of the wire its old colour
-- visible only by falling back to F2/double-click, which already
expands to relatedPotentialConductors() for exactly this reason
(ConductorPropertiesDialog, and QetScriptApi::setConductorProperty).
Expand each selected conductor to its potential before building the
undo command, matching that existing pattern.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The IO table of MasterPropertiesWidget -- the panel that opens when a
PLC master placed on a schematic is edited -- set every section to
QHeaderView::Stretch. Stretch spreads the sections evenly over the
widget and disables section dragging altogether, so the column
boundaries were permanently fixed: they could be neither widened nor
narrowed, and only the width of the whole panel had any effect.
Use QHeaderView::Interactive instead, with movable sections and a set
of default widths, so the columns can be dragged as they are everywhere
else in the application. The layout reached this way is written to
QSettings under masterpropertieswidget/plc-table-header-state on every
sectionResized and sectionMoved, and restored the next time the table
is built -- the same header-state trick the free and linked element
trees of this widget already use, except saved automatically rather
than only from the context menu.
Reviving the still-relevant part of #785, closed 2026-09-10 purely to
clear a review backlog, not on merit. Investigated fresh against
current master -- one of the original PR's three targets turned out
to already be fixed independently: element_nomenclature_view's SQL
predicate for exclude_from_bom already does
"COALESCE(LOWER(TRIM(ei.exclude_from_bom)), '') NOT IN ('true', '1',
'yes', 'on')" (projectDataBase::createElementNomenclatureView()).
auto_num_locked and potential_isolating had no equivalent: five call
sites across terminal.cpp, terminalnumberingdialog.cpp and
elementinfowidget.cpp compared the raw stored string against the
literal "true" with QString::operator==, silently treating "True",
"TRUE", a trailing space, or any value written by something other
than this app's own checkbox as off -- with no error and no visible
difference from the checkbox being genuinely unticked.
Added QET::infoFlagIsTrue(), matching the same accepted spellings
("true"/"1"/"yes"/"on", case-insensitive, trimmed) the SQL predicate
already uses, and switched all five call sites to it.
Verified the exact comparison logic in isolation, outside any QET
build: 15 cases including "True", "TRUE", padded whitespace, "1",
"yes", "on", and their false counterparts -- all correctly
discriminated. Qt 6.10.2, ctest 13/13.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Split from #913's second suggestion. There was no shortcut for the common
"duplicate with offset" convention; the nearest existing feature,
"Collage multiple", is a different workflow (a dialog for repeating a
paste in a grid pattern, not a one-shot duplicate).
Ctrl+D copies the selection and places it immediately, offset by a
configured spacing and direction -- no interactive follow-the-cursor
step, unlike Ctrl+V. The first press (or after the setting is explicitly
reopened) shows DuplicateOffsetDialog: spacing in grid steps, direction
up/down/left/right. Every later press reuses whatever was confirmed then,
silently, so a row of copies is one key held down and tapped, not a
dialog every time -- unattended, repeatable stamping is the actual point
of a duplicate shortcut, which a dialog or an interactive placement step
on every press would defeat. A separate "Configurer la duplication..."
entry reopens the dialog on demand to change the setting later. Cancel
leaves the diagram untouched -- verified, not assumed: qet_diff against
the saved file shows 0 added.
Chaining ("keep tapping to lay out a row") needs no special handling:
QET already reselects whatever a paste just added
(PasteDiagramCommand::redo()), so the next Ctrl+D naturally continues
from the copy just placed rather than the original.
The offset is applied by hand rather than by asking paste()/fromXml() to
place the copy at a target position. Both of those feed the position
through Diagram::snapToGrid(), which reads
QApplication::keyboardModifiers() and rounds to the nearest PIXEL instead
of the grid whenever Ctrl is held -- and Ctrl is always held here, this
action's own shortcut being Ctrl+D. Measured before settling on this:
routing the offset through paste() first produced copies off-grid on both
axes, by an amount that tracked the selection's own bounding-box geometry
rather than being a fixed error -- caught by qet-mcp's qet_elements
against the saved file, not by eye. fromXml() is instead called with no
position argument at all (leaves every item at its source coordinates,
landing the copy on top of the originals -- (0,0) is not a position, this
is "keep the source coordinates"), and the offset is added directly with
setPos(). A plain addition cannot be off by a rounding rule that never
runs.
Conductors are not in the hand-translated set: fromXml() itself does not
reposition them either -- they load after elements are already in their
final place and take their geometry from their terminals, which have
already moved with the elements that own them. Verified this holds: drew
a conductor by hand between two elements (drag, not click-click),
selected both, Ctrl+D, and the new conductor correctly joins the two new
elements via qet_conductors -- not the originals, not a mix.
Verified end-to-end on a built binary via qet-mcp, not by eye:
before L2 (303,207) L9 (512,196) -- deliberately off-grid
spacing=2, down (303,227) (512,216) -- +0,+20 exactly
same again, 2nd (303,247) (512,236) -- +0,+20 again, chained
Both elements land exactly the configured offset from their immediate
source regardless of the selection's own alignment. Qt 6.10.2, ctest
11/11.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A script reaches the whole project and, through the export calls, the
filesystem. That is a capability most people installing an electrical CAD
program never asked for, and leaving it on by default hands it to them
anyway. So QET_HAS_SCRIPTING builds now ship with it switched off.
QetSettings::scriptingEnabled() is the single answer, read by all three
places that need it, with QET_ENABLE_SCRIPTING=1 overriding the stored
value. The override is not decoration: a CI job or a batch run has no
dialog to tick, and a machine whose HOME is created fresh for each run has
nowhere to keep the setting either. It beats a stored "false" on purpose,
so a box unticked once cannot lock a build server out of --run for good.
Only the exact value "1" counts.
--run refuses with exit 3 and a message naming both ways in.
Projet > Exécuter un script... asks once, and turns the setting on if
the answer is yes. Asking beats grey: a disabled menu
entry says something exists and nothing about how to have
it, and this is the pattern people already know from
macro security in office software.
Configurer QElectroTech > Général > Projets has the checkbox, for
turning it back off. While the environment forces
scripting on, the box is disabled and says why, and
applyConf() then leaves the stored value alone rather
than quietly overwriting it.
runOnProject() checks as well, after both callers have. It is the one
function that actually evaluates JavaScript, so it is the one place a
future caller cannot forget to ask; the callers check first only to give a
better answer than it can.
Verified on the built binary, all four states, with an isolated HOME:
stored env result
absent - refused, exit 3
true - script runs, exit 0
false - refused, exit 3
false 1 script runs, exit 0
tst_scriptingsetting covers the same matrix hermetically, in its own
QSettings scope, and was mutation-checked: flipping the default to true
turns defaultsToOff() red.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add a KColorButton next to the 'Utiliser les couleurs du système'
checkbox in the Apparence settings tab:
- When the checkbox is active (default), system colors are used
and the color button is disabled
- When the checkbox is inactive, the color button becomes active
and lets the user pick any color for the entire application
- useCustomPalette() builds a full QPalette from the chosen color
with proper light/dark text contrast, button shading, and icon
theme switching
- The chosen color is persisted in QSettings as
'customapplicationcolor' and restored on next startup
Files changed:
- sources/ui/configpage/generalconfigurationpage.ui: HBoxLayout
with checkbox + KColorButton, customwidget declaration
- sources/ui/configpage/generalconfigurationpage.h: new slot
- sources/ui/configpage/generalconfigurationpage.cpp: load/save
custom color, enable/disable logic, toggled slot
- sources/qetapp.h: useCustomPalette() declaration
- sources/qetapp.cpp: useCustomPalette() implementation,
startup restore of custom color
Replace the white/grey toggle (m_grey_background) with a full color
picker widget (DiagramBgColorToolButton) in the Affichage toolbar,
matching the existing ConductorColorToolButton UX:
- Preset colors: White, Off-white, Light grey, Grey, Dark grey, Black
- Recently used colors section
- "Autre couleur..." opens QColorDialog for any custom color
- "Couleur système" restores the default dark-mode inverted background
New features:
- Diagram::m_custom_background_color flag: when the user picks a
custom color, PaletteGraphicsView skips lightness inversion so the
chosen color is displayed as-is
- Border and titleblock text/lines automatically switch between black
and white based on Diagram::background_color.lightness(), so a dark
background always shows a visible light border and titleblock content
- "Couleur système" restores Qt::white + re-enables inversion
Files changed:
- New: sources/ui/diagrambgcolorbutton.h/.cpp
- sources/diagram.h/.cpp: added static m_custom_background_color flag
- sources/palettegraphicsview.cpp: skip inversion when custom bg active
- sources/bordertitleblock.cpp: adaptive border pen color
- sources/titleblocktemplate.cpp: adaptive ink color for cell borders/text
- sources/qetdiagrameditor.h/.cpp: replace toggle with new widget
- cmake/qet_compilation_vars.cmake: register new source files
The settings and project dialogs list their pages with 64 or 128 pixel
icons, and two pages had theirs at 22 pixels only: the terminal-strip
page and the shortcuts page, which borrowed configure-toolbars. Both get
a 128 pixel icon drawn in the style of the other page icons, the
shortcuts page under its own name, configure-shortcuts. The SVG sources
sit beside the PNGs.
On a dark palette the Printing and Export pages were small too: their
128 pixel icons exist in the light theme only, and Qt inherits by name,
not by size, so the dark theme's small copies were scaled up instead.
make_icon_themes.py now aliases the light files of the sizes a dark name
lacks, when they read on the dark window at 3:1.
A test asks the theme for every page icon at 128 pixels, on both
palettes.
Fixes#960
Add a 'Numérotation auto' tab to the global settings page (Settings >
Nouveau projet) where users can define default auto-numbering rules
for Conducteurs, Eléments, and Folios. These rules are automatically
transferred to every new project created.
Changes:
- Add NumerotationContext::saveToSettings()/loadFromSettings() static
helpers for persisting named numerotation contexts via QSettings
- Add 'Numérotation auto' tab to NewDiagramPage with three sub-tabs
using SelectAutonumW widgets (same UI as project properties)
- Add save/remove/persist slots for conductor, element, and folio
contexts with immediate QSettings persistence on every change
- NewDiagramPage::applyConf() saves autonum settings when editing
global defaults (no project)
- QETProject constructor loads global autonum settings from QSettings
for new empty projects
An electrician on the forum draws 400 V and 24 V circuits in the same
folio and wants the colour to be one click away
(qelectrotech.org/forum, viewtopic pid=23296). Today the quickest route
is F2, which opens a colour dialog and refuses to act unless exactly one
conductor is selected, so colouring a run means one conductor, one
dialog, at a time.
This adds a swatch button beside the auto-conductor actions. Picking a
colour does two things:
- recolours every conductor currently selected, as ONE undo step;
- becomes the colour of the next conductor drawn, through the
LastUsedStyle mechanism #888 already added and Conductor's
constructor already reads.
Either half is useful alone: with nothing selected it just sets the pen
for what comes next.
The menu lists the colours the trade names -- the three phases, neutral,
earth, and the ones used for control and extra-low-voltage circuits --
then any custom colours picked this session, then the full colour
dialog. A colour already in the standard list is not repeated under
"recently used".
Nothing is written to the project or to QSettings. That is deliberate:
it is the same session-scoped "what did I just use" idea as
LastUsedStyle, so it adds no persisted state and no file-format change.
Named presets stored per project -- what #461 actually asks for -- are a
larger feature that needs a maintainer decision first; the question is
still open on that issue since 21 June.
Verified on a virtual display against examples/Habitat-Schemas_developpes.qet,
reading colours back from the saved project rather than the screen:
select all on folio 1, pick Rouge
21 conductors {none:1, #ff5500:2, #ff0000:6, #00aa00:5, #0000ff:7}
-> all 21 #ff0000
one Ctrl+Z
-> back to the original five-colour mix, exactly
pick Marron with nothing selected, then draw a conductor
-> the new conductor is #7b3f00
ctest 12/12, Qt 6.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CaKympWT3owLotCpEN2CFj
QET had no icon theme: the 446 entries of the icon table and the 116
iconsets in .ui files each named a resource path, so an icon could only
ever be one file, and a variant for another palette or a vector source
had nowhere to go (GitHub #466, #690, #870). This adds the theme layout
without changing a single pixel; a dark variant comes in a follow-up.
The theme "qet" follows the freedesktop layout Qt's icon loader
understands. misc/make_icon_themes.py generates ico/icon-themes.qrc,
which aliases the existing ico/<size>/<name>.png files into
themes/qet/<size>/<name>.png, and ico/themes/qet/index.theme. No file
moves. The four table entries that paired a 16 pixel file with a 22
pixel file of another name (ConductorSettings, DiagramAdd,
DiagramDelete, DialogInformation) get the 22 pixel file aliased under
the 16 pixel name.
QETApp::initIconTheme() registers the theme before initIcons() and makes
it current on every platform, so a desktop icon theme cannot replace
QET's icons. Icons are then looked up by name: QIcon::fromTheme() in
qeticons.cpp and in the few places that built a QIcon from a resource
path directly, and theme="..." on the iconsets in .ui files, with the
resource path kept as fallback. Flags, color swatches, application and
MIME icons stay on their paths.
One entry does not go through the theme. The elements panel draws the
project root with ProjectFileGP in the 50 pixel slot it reserves for
element previews, and the name "project" also carries the 128 pixel
file the configuration dialog uses. On a 2x display Qt's loader picks
that file for a 50 pixel request and fills the slot. ProjectFileGP
loads the 16 and 22 pixel files directly, as before.
tests/qttest/tst_qeticons: every name in the theme resolves, the four
aliases resolve at 22 pixels, a Fusion tool button shows its icon at
3:1 with disabled weaker than enabled, and the project root icon stays
at 22 pixels or less when asked for 50 at 2x while the configuration
dialog still gets its 128 pixel file. The rendering helpers shared
with tst_qetpalette moved to tests/qttest/inkcontrast.h.
Refs discussion #886: a saved-report manager built on custom SQL and
nomenclature.json. Most of what was asked for already existed --
Projet > Exporter au format CSV already builds/saves/reuses named
SELECT queries via ElementQueryWidget and nomenclature.json, and
Projet > Ajouter une nomenclature already inserts one into a folio.
This fills the three real gaps.
- projectDataBase::isReadOnlySelect() rejects anything that isn't a
single SELECT/WITH statement. Checked in newQuery() itself, the one
choke point every query path already goes through -- including a
query loaded from a saved nomenclature/summary table's <query>
element on project open, not just the dialog's own custom-SQL box.
ElementQueryWidget shows the same check live as you type, and
BOMExportDialog surfaces it before running or exporting anything.
- BOMExportDialog gains a Preview button + table (QSqlQueryModel),
so a report can be checked on screen before committing to a CSV file.
- ElementQueryWidget gains Importer.../Exporter... buttons that
read/write nomenclature.json's saved reports as a JSON file, so a
report can be handed to a colleague or another install. Import asks
before overwriting a locally-saved report of the same name.
Verified: Qt 6.10.2, builds clean, ctest 8/8. Drove the real dialog
through Xvfb: typed "DROP TABLE element" into the custom-SQL box and
got the inline warning immediately, then confirmed Preview also
refuses it with a "Requête refusée" dialog rather than running it.
Preview against the real default query returned live column headers
and a row. Export opens a save dialog without crashing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Another exception: one non-converted code path in projectprintwindow.cpp to be followed-up.
One issue found in the Qt6 code path of diagramview.cpp which has been fixed.
- Add !parentGroup() guard to stale xref removal in updateXref()
to prevent deleting xref data that ElementTextItemGroup::updateXref()
just stored for a grouped text item.
- Replace QStringLiteral("xref") with QETInformation::ELMT_XREF
in linkelementcommand.cpp and masterelement.cpp.
- Fix indentation in plclinkwidget.cpp.
The Selection properties "texts" tab painted the color value's own text in
that color, so the default black was unreadable on a dark palette. Show a
swatch in the cell instead and leave the text in the palette's color.
The terminal plan preview draws black ink with a white brush, like the
printed page it previews, on whatever background the view inherits from
the palette. Give the view a white background.
Two additional bugs found after the initial xref cleanup:
1. Undo after unlink did not restore PLC variables (PLC_TYPE,
PLC_ADDRESS, etc.) because PlcLinkWidget::on_m_unlink_pb_clicked()
never stored the current group index in the LinkElementCommand.
makeLink() only populates PLC fields when group_idx >= 0, so
the slave was re-linked but appeared empty. Fixed by reading the
group index from the master via groupIndexForElement() and calling
setGroupIndex() before unlinking.
2. m_update_slave_Xref_connection was only cleared inside the
if(m_slave_Xref_item) block in the updateXref() cleanup path.
For AlignHCenter (Text field) position, no m_slave_Xref_item
is ever created — the connections are stored in
m_update_slave_Xref_connection but never cleaned up on unlink.
On re-link, the old stale entries prevented new connections from
being established. Fixed by clearing the list unconditionally
in the cleanup path.
Forum #3186 / issue #850: a user who has built up conductor and element
numbering rules in one project has no way to reuse them in the next one.
The only answer today is to open both .qet files in a text editor and copy
the XML across by hand.
Adds an "Import from another project..." button to the auto-numbering page
of the project properties dialog. It offers every numbering found in the
chosen file, per category, with names that already exist here unticked by
default and a "replace same-named numberings" option for when that is what
the user wants.
The source file is parsed as plain XML rather than opened as a QETProject.
Opening it would run the whole load path, including the modal dialog raised
for a file written by a different version of QElectroTech -- a dialog the
user has no reason to see, since nothing but the <newdiagrams> block is
being read.
Two supporting changes:
- readValuesFromProject() clears the three combo boxes before filling
them. It only ran once before; it now runs again after an import, and
without the clear every name appeared twice.
- FolioAutonumberingW::setContext() likewise replaces its list instead
of appending to it. It has a single caller, the line above.
This deliberately does not attempt the project-template feature also raised
on the forum thread. That needs decisions about where templates live and
what else they carry, and is better settled in a discussion first.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This reverts merge commit 3d5799773, restoring shortcutsconfigpage.cpp to
its state before it.
#759 and #821 fix the same issue (#757). #821 was opened on 8 September and
is the better fix; #759 was merged on 12 September without checking whether a
PR for it already existed, and its merge is what left #821 conflicting with
master. Reverting is the way to let the right change land.
#759 keys conflict detection on the row's category, which is a tr() string.
#821 keys on the shortcut ID prefix, which is stable and untranslated, and
encodes the overlaps the category cannot express: main-window actions are
live while any editor is open, and the depth.* actions are installed into
both the diagram and the element editor.
Checked against the registry rather than by reading -- 94 registered actions
plus the four depth.* ones registered through QObject::tr. On the shipped
defaults the two behave identically: all 24 shared sequences are legitimate
cross-editor duplicates and neither flags them. They diverge on shortcuts a
user assigns, where #759 misses four classes of real conflict that #821
catches: a diagram or element editor action given the main window's F1, and
a diagram or element action given a depth.* sequence.
The reason #759 looked adequate is that the scope prefix currently maps
one-to-one onto the translated category for all seven scopes, so same-scope
detection comes out the same either way. It fails only where scopes overlap,
which is the case #821 exists to handle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>