Commit Graph

151 Commits

Author SHA1 Message Date
ispyisail 79a8637251 Add optional hops where wires cross (#436)
Where two wires cross without being connected, a project can now draw a
small arc (a hop) on one of them, instead of the user inserting a jump
symbol and splitting the wire. It is a project setting, off by default:
Project properties > Général > "Croisements de conducteurs", with no
hops, hops on horizontal wires, or hops on vertical wires. Choosing the
orientation rather than following drawing order keeps a project
consistent.

Only the drawing changes: no element is added and no wire is split. A
wire that ends or bends on another one is a junction and never hops,
and a crossing too close to the end of a segment for the arc to fit is
drawn plainly. PDF, SVG and image export and printing show the hops,
since they paint through Conductor::paint(); DXF export, which writes
the segments itself, does not.

The setting is saved as <wire_crossings hop="..."/> under the project
root, next to <usage>, and only when hops are on, so a project that
never used them saves exactly as before.

The geometry is in wirehops.cpp, free of any graphics item, and tested
on its own. Conductor::paintedPath() feeds it the conductors of the
folio from a snapshot of their scene points, rebuilt only when a
conductor changes shape, moves, or enters or leaves a folio; each hop
path is cached the same way. Asking the scene for the conductors in a
rect instead strokes every candidate's shape, which made a 366-wire
folio render 0.6 s slower; with the snapshot the difference is within
measurement noise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:18:12 +13:00
Kellermorph 48aac69391 Merge upstream/master into prefix-button
Only one conflict: the include block of
sources/ui/configpage/generalconfigurationpage.cpp, where upstream adds
editor/terminalnamecheck.h next to the two includes of the prefix
editor. All three are kept. The file list in
cmake/qet_compilation_vars.cmake merged itself this time.
2026-09-30 11:10:02 +02:00
Kellermorph 1d339a8b27 Settings: only write the prefix rows that were edited
Review findings on the prefix editor:

- OK without touching anything changed the file: every row was written
  back, so an explicit <prefix/>, which reads as an empty field and
  cancels the inheritance, was dropped and that folder started
  inheriting its parent's prefix again. A row is now written only when
  it was edited or no longer holds what the file has, and the two kinds
  of empty field look different: hasPrefix() tells an explicit
  <prefix/> from a missing one, which is what the field hint shows.
- load() copied a broken file aside before the user had chosen
  anything, so every "Corriger le fichier" left a .bak behind while the
  message said nothing had been modified. The copy is now made by
  save(), right before the file is replaced: repairing or cancelling
  leaves nothing behind, and a copy that cannot be made stops the write
  rather than destroying the only copy.
- The button followed the combo box only for "Parcourir...": put back
  on "Par defaut" it fell back to the previously saved path instead of
  the default one. The directory of the entry being displayed is now
  used, "Par defaut" being dataDir()/elements/.
2026-09-30 11:01:31 +02:00
Kellermorph 6ea626fec7 Settings: configure the folder prefixes of the user collection
The prefixes of a collection's folders live in a qet_labels.xml that so
far could only be edited by hand, and a hand-edited file is easy to
break: one extra </category> and the file stops being well formed, which
the lookup answers with "no prefix at all". Labels then degrade
silently, without any error anywhere - the file is simply ignored. Add
a way to edit it from the settings.

- "Configurer les préfixes…" next to the user collection path opens
  PrefixConfigurationDialog, which lists every folder of that
  collection, subfolders included, one line edit each, with "Tout
  déplier"/"Tout replier" and OK/Abbrechen. It writes on OK only :
  cancelling leaves the collection exactly as it was, and an emptied
  field drops the <prefix> again so the folder goes back to inheriting
  its parent's.
- QetLabelsFile owns the reading, scanning, structure building and
  writing of the file. prefixFromLabelFile() moves there unchanged from
  assignvariables.cpp as prefixForPath(), so the lookup used by label
  assignment and the one used by the dialog are one piece of code.
- Entries whose folder no longer exists on disk are offered as
  Conserver or Supprimer and only applied on OK.
- An unparsable file is copied to qet_labels.xml.bak first, and the
  dialog then reports the line and column of the syntax error, with
  "Corriger le fichier" as the default choice - such a file may be one
  forgotten tag away from being valid - and rebuilds the whole
  structure only when "Reconstruire" is picked. A broken file that
  cannot be backed up is refused rather than overwritten.

The scan reads non-hidden directories recursively in name order,
without following symlinks. A collection without any subfolder has
nothing to configure and says so instead of opening an empty dialog.

Tests: a standalone harness kept outside the tree (84 checks :
structure, inheritance, explicit empty prefix, orphan handling, broken
file, wrong root element, broken file whose backup cannot be written,
reload after save) and the dialog driven offscreen (29 checks,
including "reject creates no file"). ctest 27/28, the failing
tst_menubarkeyboard being the headless F10 test, unrelated to this.
The new strings are French source strings like the rest of the code;
the .ts files are left to the translation update.
2026-09-29 23:52:22 +02:00
ispyisail bd4d942d71 Add a terminal name check when saving an element
Discussion #1157. IEC 61666 §4.1 requires each terminal to be
identified unambiguously within its object, so two terminals of one
element must not share a name.

On save, the element editor now:
- refuses to save when two terminals share a name, lists the names
  ("N ×3") and selects those terminals;
- warns, and still saves, when a terminal has no name. Folio reports,
  conductor definitions and thumbnails are skipped.

Names are compared after trimming surrounding spaces, case sensitive.

Settings > General > Editor has a new checkbox, on by default, that
turns both checks off (elementeditor/check-terminal-names).

--check-elements applies the same rule: repeated names are a FAIL,
missing names a WARN. It ignores the setting, since it is an explicit
check. On the shipped collection this reports 82 FAILs, the elements
that repeat a terminal name today.

The rule lives in the header-only editor/terminalnamecheck.h, shared
by both, and is unit tested by tst_terminalnamecheck.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 07:24:55 +13:00
Laurent Trinques 40f0235c71 Merge pull request #1088 from Kellermorph/materiallist
Add a material list lookup for the element article fields
2026-09-27 21:43:52 +02:00
Kellermorph d5ae15f568 Add a material list lookup for the element article fields
The element description dialog only offered typing, so every article of
a catalogue had to be copied in by hand into the Artikelbeschreibung
row of each block, over and over.

Settings > General now keeps the path of a CSV material list (Créer...
writes a template with the header lines), and the Artikelbeschreibung
row of every block of the Bauteil dialog shows a "..." button opening a
searchable listing of that file. "Appliquer" fills the fields of that
block only -- the main block gets everything but label and formula, an
auxiliary block gets description, number, manufacturer, references,
supplier, quantity, unit and additional info -- and a cell that is
empty in the file never erases a value already in the element. The
symbol editor is left alone.

- sources/materiallist/: CSV reading and writing (UTF-8 BOM, ';', every
  field quoted, atomic write through QSaveFile), two header lines
  (translated labels then the canonical English keys), the selection
  window and the new entry form.
- The selection window reads the file again every time it opens, keeps
  its size, column widths, column order and sort between openings,
  shows every column of the file, scrolls all four directions with the
  wheel (Shift + wheel moves the columns) and puts back the order of
  the file when the corner above the row numbers or a row number is
  clicked.
- German translations for the new strings in lang/qet_de.ts.
2026-09-27 21:18:54 +02:00
ispyisail f62a44827b Add a setting to show collection search results as a tree
#1051 replaced the filtered tree with a ranked flat list. A new checkbox
in Settings > General, "Afficher les résultats de recherche sous forme de
liste triée" (elementscollection/search-flat-list, default on), keeps the
ranked list; unticked restores the pre-#1051 filtered tree search.

The Insert picker and shortcut bar call rankedSearch() directly and are
unaffected. Down/Enter from the search field only apply to the flat list.

Requested by scorpio810 on #1051.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
2026-09-28 05:43:11 +13:00
Laurent Trinques 0b4f480dfe Merge pull request #502 from ispyisail/proto-conductor-selection-properties
Add wire properties to the Selection properties panel, as an option (#500)
2026-09-27 09:11:22 +02:00
Laurent Trinques 05260de87b Merge pull request #1060 from ispyisail/feature/shortcut-bar-symbols
Add pinned elements to the shortcut bar, with a width you can set
2026-09-27 09:09:44 +02:00
ispyisail ff795258c4 Conductor dock: turn it on in the preferences, off by default (#500)
Showing a selected conductor's properties in the selection-properties
dock is now a preference, on the General page under Appearance:
"Afficher les propriétés d'un conducteur sélectionné dans le panneau
Propriétés de la sélection". It is off by default, so selecting a
conductor does what it did before unless the user turns it on.

This replaces the View-menu toggle, which defaulted to on; that entry
is dropped in the merge with master. Same setting key
(diagrameditor/conductor_properties_panel).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 09:13:55 +13:00
ispyisail 5bd55801e9 Pin elements to the shortcut bar
The bar's customising window gets an element search next to the command
list. Type part of a name, then drag a hit onto the bar, double-click
it, or press Enter to pin the best one. A pinned element shows as its
icon among the commands, and clicking it places the element, as the
picker does.

Pinned elements are saved in the same list as the commands, by
collection path (common://, custom://, company://), so the row keeps
the user's order. Elements embedded in a project are not offered: their
path names the project as loaded now. Elements are offered for the
empty-folio bar only; with something selected the bar is for acting on
it. Once any element is pinned there, the palette folder grid under the
bar is hidden, and comes back while typing a search.

Dragging a pinned element off the bar onto the commands, or a double
click, removes it. The Preferences page shows pinned elements by name
and icon, and removing one there drops it instead of listing it as a
command. The customising window is now kept on screen, since it is
taller than before.

Discussion #1033.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 03:16:56 +13:00
ispyisail b57c37ca62 Run commands with right-drag mouse gestures
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)
2026-09-26 21:40:46 +12:00
ispyisail b80d6e7cd3 Show the selection's commands beside the cursor after a click
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)
2026-09-26 21:40:32 +12:00
ispyisail 6d42556d9f Customise the shortcut bar in place
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)
2026-09-26 21:40:19 +12:00
ispyisail 2da101ca90 Add a shortcut bar that opens at the cursor with S
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)
2026-09-26 21:40:19 +12:00
ispyisail 92cbc8fd2a Merge master into stage 2 (#1042) as the base of the stage PRs 2026-09-26 21:40:18 +12:00
ispyisail e6291eb8e8 Place an element from the collection without dragging it
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
2026-09-26 11:53:34 +12:00
ispyisail 95f3ea2f6d Share the text grid choices; add a preference, a status hint and a unit test
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
2026-09-26 10:22:50 +12:00
ispyisail 17ebbffbca Add 3D mouse speed, direction, dead zone and twist-to-zoom settings
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>
2026-09-25 09:56:04 +12:00
Laurent Trinques 48883b320e Merge branch 'master' into feature-3dmouse-spnav 2026-09-24 15:45:33 +02:00
ispyisail 8334a9a27f Scripting is off until asked for, and says how to turn it on
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>
2026-09-23 04:06:33 +12:00
ispyisail bf7f49f334 Merge remote-tracking branch 'upstream/master' into review-635-fix
# Conflicts:
#	CMakeLists.txt
#	cmake/developer_options.cmake
#	cmake/qet_compilation_vars.cmake
#	sources/qetapp.cpp
2026-09-22 23:10:34 +12:00
Kellermorph 5d033bf1b2 Add custom application color picker in global settings
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
2026-09-21 22:05:31 +02:00
Laurent Trinques 1bec2700bd Merge pull request #924 from Kellermorph/auto-numbering-new-project
Add global auto-numbering rules to QElectroTech settings
2026-09-20 20:07:16 +02:00
Jeff Patterson ac78c51b70 Give every configuration page an icon at page size
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
2026-09-20 10:44:09 -05:00
Kellermorph 7fa1ff35b8 Add global auto-numbering rules to QElectroTech settings
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
2026-09-19 13:47:28 +02:00
ispyisail a6fd42ac5f Import automatic numbering rules from another project
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>
2026-09-13 09:56:25 +12:00
ispyisail f9d063bd38 Merge pull request #821 from enesgursoy6110/fix/shortcut-conflict-scope
Scope shortcut conflicts to overlapping editors
2026-09-13 00:00:54 +12:00
ispyisail a0acf878a2 Revert "Merge pull request #759 from ispyisail/fix-shortcut-conflict-scope"
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>
2026-09-12 23:51:41 +12:00
ispyisail 9c3ab588a4 Merge branch 'master' into fix-shortcut-conflict-scope 2026-09-12 23:07:27 +12:00
enesgursoy6110 cfa1e3ed27 Scope shortcut conflict detection by editor 2026-09-08 21:02:46 +03:00
ispyisail 6657b3de2b Make ProjectAutoNumConfigPage follow ProjectConfigPage's documented init() contract
ProjectConfigPage::init() is documented as "Typically, you should call this
function in your subclass constructor" -- it runs initWidgets(), initLayout(),
and (if a project is set) readValuesFromProject() and adjustReadOnly(), in
that order. ProjectMainConfigPage's constructor follows this. Until now,
ProjectAutoNumConfigPage's did not: it called initWidgets(), its own
buildConnections(), and readValuesFromProject() directly, skipping both
initLayout() and adjustReadOnly() entirely, and calling
readValuesFromProject() with no null-project guard.

In practice this was harmless today -- this subclass's initLayout() and
adjustReadOnly() overrides are both empty, and every construction site
happens to pass a real project -- but it is exactly the kind of latent
inconsistency Joshua's own refactor notes call out for this class ("remove
inconsistent virtual method usage... allow subclasses independent
implementation"). The day someone fills in adjustReadOnly() for this page
(e.g. to disable auto-numbering editing on a read-only project, which is
what the empty override's own doc comment says it is for), the constructor
path would silently never call it.

Fixed narrowly: the constructor now calls init() like its sibling does.
buildConnections() moves to the end of initWidgets(), the same relative
position it held in the constructor, so behaviour for the paths already
exercised is unchanged. This is the safe, no-redesign half of Joshua's
note; removing the init()/initWidgets()/initLayout()/readValuesFromProject()
scaffolding itself, so subclasses are free to sequence things however they
want, is a real redesign of the ConfigPage contract and needs his sign-off
on what should replace it -- not attempted here.

Verified with a GUI capture rather than by reading: opened Project
Properties on examples/industrial.qet, selected "Numérotation auto", and
confirmed the Management tab renders with its saved policy (Conductor/Element
"Both", "Apply to Entire Project") and the Conducteurs tab's combo box comes
up pre-populated with the project's saved context ("de la nouvelle
numérotation"), which on selection correctly fills the Type/Valeur/Formule
fields -- proving both readValuesFromProject() and the buildConnections()
signal wiring still work end-to-end through the new call sequence.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 23:09:12 +12:00
ispyisail dab4c6c424 Scope shortcut conflict detection to the category
checkConflicts() compared key sequences across the whole registry, but
QElectroTech deliberately registers one key per editor window: Undo,
Redo, New, Open, Save and Ctrl+Shift+S each exist three times, once for
the diagram, element and titleblock editors. Those are not collisions --
they act on different windows.

The result was that 60 of the 95 shipped default bindings displayed as
conflicts, so the indicator carried no information and the page looked
broken on first open.

Conflicts are now keyed on (category, sequence). The category is the
registry's existing per-window grouping, so no new concept and no
ShortcutManager API change is needed.

Fixes #757
2026-08-23 21:50:42 +12:00
plc-user 56464629bf FIX compile-warnings 2026-08-22 13:34:29 +02:00
Laurent Trinques f172e0740b Merge pull request #760 from ispyisail/s6-shortcut-browsing
Shortcuts page: group by category and make search actually work
2026-08-21 08:38:46 +02:00
ispyisail e9ee8b600b Make the shortcut list browsable and searchable
Replace the flat QTableWidget with a QTreeWidget that groups actions under
one collapsible top-level node per category. Fix the search box so it also
matches the current key sequence (exactly), accepts multi-keyword queries
(AND, any word order) and is accent-insensitive, auto-expands matching
groups and shows an "N actions" count. Add a quick filter (all / bound /
unbound / conflicts) that combines with the text query. Conflict detection,
per-row reset, reset-all and persistence are preserved.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-17 12:30:23 +12:00
Andre Rummler 7ba295a339 Remove all Qt version checks and branches for <5.14.0 as such versions are no longer supported. 2026-08-13 15:45:15 +02:00
Andre Rummler deba8d0e4e Merge branch 'master' into master-update-more-signal-slot 2026-08-13 13:07:53 +02:00
Andre Rummler 8d08c3fd56 Replacing depreciated qAsConst with std::as_const 2026-08-12 12:29:56 +02:00
Andre Rummler c25c400c93 Some of the newly guarded (Qt5 path) signal/slot connects could be still converted to method pointers. Mostly for completeness as they will go away anyhow soon.
Remaining: richtext editor.
2026-08-10 09:08:57 +02:00
Laurent Trinques cdf5cad1e5 Merge pull request #698 from arummler/master-modernize-signal-slot
Migrate string based signal/slot to method pointer
2026-08-09 21:08:28 +02:00
plc-user 3a3f23b7a8 swap position of ComboBox and static text in config-page 2026-08-09 20:55:01 +02:00
Andre Rummler 9f283322a2 Merge branch 'master' into master-modernize-signal-slot 2026-08-09 11:30:27 +02:00
Andre Rummler 5adf61936b Merge branch 'master' into master-modernize-signal-slot 2026-08-08 23:13:42 +02:00
Andre Rummler bfee5b1cdb Merge branch 'master' into master-fix-slot 2026-08-08 15:16:48 +02:00
Andre Rummler 62ad49a6d3 Update old fashioned SIGNAL/SLOT to point-to-member. Only simple and clear cases. 2026-08-08 00:32:31 +02:00
plc-user b6e43f78d0 fix whitespace 2026-08-07 22:09:06 +02:00
Levi Jetzer 97709bff6a Save the default cross-reference properties to QSettings
NewDiagramPage::applyConf() writes every other default to QSettings —
border, title block, conductors, folio reports and the guides — but the
cross-reference branch only fetched the properties into a local hash and
then dropped it on the floor. hash_xrp was never used.

The result: changing the cross-reference defaults under Settings > New
project has no effect. Nothing is written, no defaultxref* key ever
appears in the configuration file, and XRefProperties::defaultProperties()
keeps handing out the hardcoded fallbacks for every new project.

Write each of the four types (coil, protection, commutator, plc) with the
"diagrameditor/defaultxref" + key prefix that defaultProperties() already
reads back.
2026-08-07 11:42:20 +02:00
Andre Rummler fb51489436 Fix of previous commit: use textActivated signal instead. 2026-08-05 09:39:15 +02:00