Commit Graph

8797 Commits

Author SHA1 Message Date
Laurent Trinques e5e2f2efbd Merge pull request #673 from Kellermorph/show-terminal-names-export
show terminalnames in export
2026-08-08 10:04:55 +02:00
Laurent Trinques 5c39518921 drop one dead member 2026-08-08 08:14:06 +02:00
Kellermorph 6d2995ad68 set to false 2026-08-08 08:09:45 +02:00
Laurent Trinques 1198cf73a9 Update translations files 2026-08-08 07:23:17 +02:00
Laurent Trinques e8f80697f3 Reapply "Auto-break conductor"
This reverts commit 905afc1bbc.
2026-08-08 07:03:50 +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
ispyisail 41e83bbc09 Keep group rotation on-grid when X and Y grid sizes differ
Raised by plc-user in discussion #618: the diagram editor allows moving
elements by as little as 1px, and asked that rotation not undershoot
that. Checking the actual constraint (Settings -> DiagramEditor_xGrid_sb
/ _yGrid_sb, both independently configurable, minimum 1, maximum 30)
turned up a real, verified gap this PR's existing fractional-pivot fix
doesn't cover: an ASYMMETRIC grid (xGrid != yGrid).

Swapping X/Y deltas for a 90-degree turn -- the exact-arithmetic path
already in this file -- only stays on the configured grid if
xGrid == yGrid. With an asymmetric grid, a delta that was a clean
multiple of xGrid lands on the Y axis after the swap, where the grid
unit is yGrid, and one is not generally a multiple of the other.

Verified on a real build (not just derived): two elements at (100,210)
and (150,420), both on-grid under xGrid=10/yGrid=7, selected and
group-rotated 90 degrees via a temporary local CLI harness.

  before this change: (242,292) and (32,342) -- off-grid on both axes
  after this change:  (240,294) and ( 30,343) -- exactly on-grid

Confirmed the same drift is present without this change too (i.e. not
something introduced elsewhere) and that xGrid==yGrid, the common case,
is unaffected: snapping an already-on-grid point is a no-op.

Fix: re-snap the final computed position to Diagram::snapToGrid(), not
just the shared pivot, for the exact-90-degree path. Left the
arbitrary-angle trig fallback alone -- it has no caller today (the
diagram editor only ever passes multiples of 90) and "on-grid" doesn't
have a clean meaning for an arbitrary angle regardless of grid shape.

Does not attempt to fix a separate, pre-existing property surfaced
while testing this: four consecutive 90-degree turns do not reliably
return a selection to its exact starting position, even on a symmetric
grid, because each RotateSelectionCommand recomputes the pivot fresh
from the selection's current sceneBoundingRect(), and an item whose
bounding box isn't rotationally symmetric reports a different box (and
therefore a different centre) at 0 and 90 degrees. Verified this drift
is identical with and without this change, so it is not a regression --
just a different, harder guarantee this change does not attempt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:40:25 +12:00
plc-user b6e43f78d0 fix whitespace 2026-08-07 22:09:06 +02:00
Laurent Trinques 016bb3636e Merge pull request #685 from qelectrotech/revert-639-Replace-automatic-conductors
Revert "Auto-break conductor"
2026-08-07 16:25:22 +02:00
Laurent Trinques 905afc1bbc Revert "Auto-break conductor" 2026-08-07 16:25:06 +02:00
Laurent Trinques d0fcc9ed78 Merge pull request #639 from Kellermorph/Replace-automatic-conductors
Auto-break conductor
2026-08-07 16:15:29 +02:00
Laurent Trinques cb7b45e281 Merge branch 'master' into Replace-automatic-conductors 2026-08-07 14:58:42 +02:00
Laurent Trinques 725678a866 Merge pull request #684 from arummler/master-remove-richtext-uic
Remove pre-compiled richtext widget
2026-08-07 14:52:34 +02:00
Andre Rummler cc8be46c6e For the richtext widget the compiled uic was check in since QT4(?) times although it was declared to the AUTOUIC. Removed the pre-compiled version and it still works. 2026-08-07 14:16:20 +02:00
Kellermorph 57d0b4b5a3 fix whitespace 2026-08-07 13:48:40 +02:00
Laurent Trinques e3964fb24a Update en and fr translations files 2026-08-07 13:12:28 +02:00
Laurent Trinques 5df9a987a2 Merge pull request #678 from ispyisail/fix/menu-orphaned-actions
Add two orphaned actions to the menus, drop two dead members
2026-08-07 12:42:30 +02:00
Laurent Trinques 55fdae918b Merge pull request #642 from ispyisail/feature-custom-element-properties
Add user-defined custom properties on elements (discussion #611)
2026-08-07 12:12:31 +02:00
Laurent Trinques 14ef848c4a Merge pull request #681 from IBSYSLevi/fix/save-new-project-cross-references
Fix: Save the default cross-reference properties to QSettings
2026-08-07 11:55:45 +02:00
Laurent Trinques dcc64462f6 Merge pull request #680 from ispyisail/feature/optional-pch
Add optional precompiled headers behind QET_ENABLE_PCH (default OFF)
2026-08-07 11:51:57 +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 99151b9c04 Report "no constraint" (still needs the translations; to be added in the next translation round I guess) from minimumWidth() for a title template instead of an arbitrary value.
Following up on the earlier division-by-zero fix: return -1 from the bad denominator branch of minimumWidth(), matching the "no
constraint" convention maximumWidth() already uses, instead of std::numeric_limits<int>::max() or 0. Update
TitleBlockTemplateView::updateDisplayedMinMaxWidth() to skip the "Longueur minimale" line when minimumWidth() reports -1, mirroring
its existing handling of maximumWidth() == -1.
2026-08-07 11:39:08 +02:00
ispyisail 4caefc048f Add optional precompiled headers behind QET_ENABLE_PCH (default OFF)
Building QET is dominated by re-parsing Qt's headers. A 214-line source
file expands to roughly 198,000 preprocessed lines, and compiling one
translation unit costs ~4.1 s, of which only ~0.35 s is optimisation --
switching -O3 to -O0 saves just 8%, so the usual "build Debug for faster
compiles" advice does not help here. A precompiled header caches the
parsed header state, which is the part that actually costs.

Measured on a 24-thread Xeon E5-2650 v4 with Qt 5.15.18 and GCC 15.2,
same build tree, only the option differing:

  compile one translation unit   4.12 s -> 1.21 s
  edit one .cpp -> linked binary 5.22 s -> 1.65 s

Deliberately OFF by default. A PCH satisfies includes that a source file
neglected to make for itself, so code written with it enabled can fail to
compile for everyone else. Leaving the default off keeps CI and
contributors on the strict behaviour; only developers who opt in trade
that away for the speed.

Two details in the implementation are load-bearing:

- The generator expressions are not decoration. This target also compiles
  the 18 C files of the bundled LZMA decoder, and an unguarded header list
  applies to every language in the target, so the Qt headers would be fed
  to the C compiler and fail with "unknown type name 'namespace'".
  $<ANGLE-R> is needed because a literal '>' would end the generator
  expression.
- target_precompile_headers() requires CMake 3.16 while the project still
  declares a 3.5 minimum, so the block warns and skips rather than raising
  the project-wide requirement for an opt-in developer feature.

Verified both ways: with the option off no PCH artefacts are generated and
the build is byte-for-byte the previous behaviour; with it on, all 18 C
files still compile, the generated PCH is C++-only (cmake_pch.hxx, with no
cmake_pch.h), the C compile commands carry no PCH, and the resulting
binary runs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 21:10:46 +12:00
ispyisail 9891eef916 Add two orphaned actions to the menus, drop two dead members
Both actions already exist and work; they were simply only reachable from
a toolbar, and those toolbars are user-hideable via Configuration >
Afficher, so hiding one made the feature unreachable entirely.

- "Afficher les guides" (m_draw_guides) goes into the Affichage menu next
  to "Afficher la grille". The two are adjacent lines in the view toolbar
  and do the same kind of thing, but only the grid had a menu entry.

- "Creation automatique de conducteur(s)" (m_auto_conductor) goes into the
  Projet menu. It writes a project setting via
  QETProject::setAutoConductor(), so the Projet menu is where a user would
  look for it; it is placed with the project properties, above a separator
  that keeps the folio operations grouped as before.

Also removes conductor_default and m_project_folio_list from the header.
Both are declared but never allocated and never referenced anywhere in the
tree -- that the build still links is the proof they were dead.

No new strings: both actions already carry translated text.

Found while auditing every QAction against every menu, discussion #677.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 19:07:52 +12:00
Andre Rummler 43e4603c55 Fixing minimum width calculation for title block which could lead to division by zero and subsequent program crash if opening a template with only relative sized columns. 2026-08-07 07:59:39 +02:00
ispyisail 3cf5cb945e Keep group rotation on the grid
Rotating a selection around the raw bounding-box centre moved
grid-aligned elements off the grid permanently. sceneBoundingRect()
comes from font metrics and pen widths, so the centre is almost never
a round number: with a pivot of (132.67, 101.11), an element sitting
at x=100 landed at x=133.78, and no further rotation brought it back.
Positions are written with QString::number() (%.6g), which hides the
floating-point noise but keeps the offset, so the diagram ends up
subtly misaligned with no way to repair it from the UI.

Snap the pivot with Diagram::snapToGrid(), which also follows the
user's configured X/Y grid rather than assuming the 10 px default.

Also compute the rotated offset exactly for multiples of 90 degrees
instead of going through qCos()/qSin(). The rotate actions only ever
pass right angles, and qCos(90 deg) is 6.12e-17 rather than 0, so the
trig path added error for no benefit -- four 90 degree steps did not
return a point to where it started. A quadrant is an axis swap, which
is exact; trig is kept as the fallback for any other angle.

With both, four 90 degree rotations of a grid-aligned element return
it exactly to its original position and every intermediate step stays
on the grid.

Reported by plc-user, who hit the same problem rotating graphical
primitives in the Element Editor -- discussion #618.
2026-08-07 12:18:17 +12:00
Kellermorph b3a4a41ad9 new Checkbox 2026-08-06 21:37:40 +02:00
Laurent Trinques 4ff2be3f43 Update translations files 2026-08-06 13:36:38 +02:00
Laurent Trinques 894287111c Merge pull request #665 from ispyisail/feature-eventloop-watchdog
Add an event-loop responsiveness watchdog (discussion #644 follow-up)
2026-08-06 13:30:43 +02:00
Laurent Trinques 3603feb5d8 Merge pull request #647 from ispyisail/feature-diagnostic-logging-crash
Add crash-time ring flush and diagnostics export UI (discussion #644, steps 4-5)
2026-08-06 13:26:14 +02:00
ispyisail da3a976b60 Add an event-loop responsiveness watchdog (discussion #644 follow-up)
QetLogger (discussion #644, steps 1-3) captures whatever an explicit
qDebug()/qInfo()/qWarning() call already decided to report. Most of a
session -- painting, dragging, a slow synchronous operation -- produces
no log output at all, so a silent multi-second gap in the log is
indistinguishable from the user simply not doing anything. That gap
came up directly: investigating a user-reported "the program lagged"
required inferring stalls from timestamp gaps between unrelated log
lines, which can't tell a real freeze apart from normal idle time.

EventLoopWatchdog closes that gap directly instead of inferring it. A
QTimer::PreciseTimer repeating tick (every 50ms) measures the *actual*
elapsed time since the previous tick via QElapsedTimer (monotonic,
unaffected by system clock/NTP adjustments). Qt does not queue up
missed fires for a normal repeating timer, so if the main thread is
blocked for 600ms, the timer fires once as soon as the loop frees up,
with ~600ms measured since the last tick -- that gap is the stall,
measured at its source. Only logs (via the existing qWarning() path,
so it reuses QetLogger's file/ring/rotation with no new plumbing) when
a tick is late by more than 200ms, so a healthy session produces zero
output from this class, in keeping with QetLogger's bounded-log design.

Same QET_WATCHDOG_DISABLE=1 escape-hatch convention as QetLogger's own
QET_LOG_DISABLE=1.

Deliberately not included: attributing a stall to what caused it. This
tells you a stall happened and how long -- pairing that timestamp with
gdb attached to a running session (as used for the CLI hang, PR #661)
is still how you get from "it stalled" to a root cause.

Stacked on #647 (feature-diagnostic-logging-crash) for QetLogger/
qWarning() plumbing this depends on -- diff includes its commits until
that merges.

Verified against the compiled binary, not just read: temporarily
injected a QThread::msleep(600) via a one-shot QTimer 2s after
startup, confirmed the exact expected warning
("EventLoopWatchdog: main thread stalled for 620 ms") at the right
severity through the real qWarning()/QetLogger path, then removed the
test hook and reconfirmed a normal run produces no output from this
class at all.
2026-08-06 23:05:05 +12:00
ispyisail 5dec36cb29 Add crash-time ring flush and a diagnostics export UI (discussion #644, steps 4-5)
Stacked on the steps 1-3 branch (feature-diagnostic-logging, PR #646).
Kept as its own PR rather than folded into that one, matching the
discussion's own framing: step 4 is explicitly "the highest-risk piece
... lands last, behind its own switch."

## Step 4 -- crash-time ring flush (CrashHandler)

Installs a handler for SIGSEGV/SIGABRT/SIGBUS/SIGFPE/SIGILL (POSIX) /
SetUnhandledExceptionFilter (Windows) that flushes the in-memory ring to
a fixed crash_dump.log before the process dies.

This required reworking LogRing (step 3) to be genuinely lock-free, not
just mutex-protected: a signal handler that blocks on a lock the
crashing thread (or another thread) already holds turns a clean crash
into a hang -- no ring dump *and* no core dump, worse than doing
nothing. append() now claims a slot with a single atomic fetch-add;
dumpToFd() reads the preallocated entries directly and writes them with
write(2) only, looping on EINTR/short writes. Accepted tradeoff: at most
one entry can be read torn if a crash lands mid-append into that exact
slot -- documented in logring.h, and the alternative (a seqlock to
detect and retry) wasn't judged worth the complexity for that window.

Other invariants implemented per the discussion:
- sigaltstack with a static 64 KiB buffer, SA_ONSTACK -- a stack-
  overflow SIGSEGV has no usable stack for a handler without one.
- Nothing under the actual handler touches Qt, QString or the
  allocator: the dump path and a small header (version/git/OS/Qt) are
  precomputed into fixed char buffers by install(), which runs once at
  startup in normal context.
- Atomic test-and-set so only the first crash writes a dump; a second
  concurrent/nested fault goes straight to restore-and-re-raise.
- After writing, the handler restores SIG_DFL and re-raises (POSIX) /
  returns EXCEPTION_CONTINUE_SEARCH (Windows) so the OS's own crash
  path -- core dump, Windows Error Reporting -- still runs. A handler
  that "fixed" the crash by swallowing the signal would destroy exactly
  the post-mortem evidence this whole design exists to preserve.

Tested in this environment: POSIX/Linux only, all five signals. Sent
each directly to a running process and confirmed (a) crash_dump.log is
written with the correct header and ring contents, mode 0600, and (b)
the process still terminates via the signal with the kernel's own
"core dumped" flag set (exit code 128+signal, confirmed for all five).
The Windows path is implemented per the discussion's guidance but is
untested -- no Windows build available in this sandbox.

## Step 5 -- getting the data back out

- QETApp::checkCrashDump(), called from checkBackupFiles() only when
  there's no stale project file to recover this run (so the two
  prompts never both show, per the discussion), offers an unretrieved
  crash dump via DiagnosticsReportDialog and then deletes it regardless
  of the user's choice -- offered exactly once.
- A new "Aide > Enregistrer un rapport de diagnostic..." action
  (QETMainWindow) builds the same kind of report from the *current*
  session (QetLogger::buildDiagnosticsReport(): header + this session's
  log file) for a manual "attach this to a bug report" flow, not tied
  to a crash.
- Both go through QetLogger::redact() before ever reaching the user:
  the one redaction implemented is a literal replace of the home
  directory with "~", since an absolute path under it leaks the
  account name. The discussion's fancier "optionally redact project
  filenames too" isn't attempted -- reliably telling a project path
  apart from arbitrary log text is a much fuzzier problem than a
  literal prefix match.
- DiagnosticsReportDialog shows the full (already-redacted) content
  before saving, per the discussion: "the user is about to attach this
  to a public tracker."

Verified in a real GUI session (Xvfb): triggered a SIGSEGV, relaunched,
confirmed the crash-report dialog appears with the right header/content,
confirmed it does not reappear on a second relaunch, and confirmed the
manual "Save report" action produces a correctly-formatted report and
saves it to a chosen path.

Built clean, no new warnings.

## Build systems

Registered in both: cmake/qet_compilation_vars.cmake, and
qelectrotech.pro. The .pro needed explicit globs for the new
sources/logging/ui/ subfolder -- sources/logging/*.{h,cpp} was already
globbed, but unlike the other ui/ subfolders that one had no entry of
its own, so diagnosticsreportdialog.{h,cpp} would not have been built
under qmake.
2026-08-06 23:04:27 +12:00
Kellermorph 979df376d1 show terminalnames in export 2026-08-06 12:48:11 +02:00
Laurent Trinques ea5117b148 ci(doxygen): remove stale branch guard blocking the tag-triggered job
The job trigger was narrowed to push:tags a while back, but the
job-level 'if: github.ref == refs/heads/master' was left in place.
A tag push never has github.ref == refs/heads/master, so the two
conditions are mutually exclusive: the job trigger fires only on
tag pushes, while the guard only allows master-branch refs, meaning
the job has been silently skipped on every run since the trigger
was narrowed.

This is also the likely source of the recent Git LFS bandwidth/
storage overage: while the trigger was still push-to-master (pre-
narrowing), this job ran on nearly every commit and committed a
new QElectroTech.qch (LFS-tracked) each time, via the auto-generated
update-qch PR -- accumulating one LFS object version per run."
2026-08-06 11:45:53 +02:00
Laurent Trinques 34078c6459 Merge pull request #670 from IBSYSLevi/fix/projectview_corner_Layout_crash
Fix crash when opening a project: invalid index in corner layout
2026-08-06 11:30:37 +02:00
Laurent Trinques f46b37dd3e Update .pro files 2026-08-06 10:50:27 +02:00
IBSYSLevi e3a488e2a8 Merge branch 'qelectrotech:master' into fix/projectview_corner_Layout_crash 2026-08-06 10:47:54 +02:00
Laurent Trinques a7d504aaa8 Merge pull request #646 from ispyisail/feature-diagnostic-logging
Rework diagnostic logging: fix file writer, add rotation and a ring buffer (discussion #644, steps 1-3)
2026-08-06 10:42:20 +02:00
Levi Jetzer a911d72756 Fix crash when opening a project: invalid index in corner layout
ProjectView::initWidgets() called insertSpacing(1, 10) on a QHBoxLayout
that was still empty, inserting past the end of the item list. The
corrupted layout crashed later in QWidget::setLayout() via
QLayoutPrivate::reparentChildWidgets() and QBoxLayout::itemAt().

Use addSpacing(10) instead, which is equivalent for an empty layout.
2026-08-06 10:40:03 +02:00
Laurent Trinques f95ff29c21 Rename ChangeLog.md -> ChangeLog_full.md
Because: Windows is not case-sensitive, thanks Achim
2026-08-06 10:24:37 +02:00
Laurent Trinques 06b20c483e Merge pull request #645 from ispyisail/feature-autonum-undo
Cover auto-numbering counter changes with undo/redo (discussion #608)
2026-08-06 08:49:33 +02:00
Laurent Trinques 1318dc4b8e Merge pull request #658 from ispyisail/feature-insert-folio-position
Add "Insert folio above/below" to the elements panel's folio menu
2026-08-06 08:13:41 +02:00
Laurent Trinques 9c3ba45822 Merge pull request #669 from Kellermorph/fix-plc-editor
PLC Fix scroll sync, data persistence, font defaults, copy/paste, and layout fixes
2026-08-06 08:03:22 +02:00
Laurent Trinques 37e18efc6c Merge pull request #667 from Kellermorph/text-settings-terminals
Use global font as default for terminal label font
2026-08-06 08:03:03 +02:00
Kellermorph 4f9474a05f PLC Fix scroll sync, data persistence, font defaults, copy/paste, and layout fixes 2026-08-06 07:33:39 +02:00
Kellermorph 150b1796a4 Use global font as default for terminal label font 2026-08-05 21:11:02 +02:00
Andre Rummler 484ab9ed87 Fix folio auto-num context not applying to the active diagram
BorderTitleBlock::slot_setAutoPageNum was removed in 471f876 ("Remove unused signal", 2023-10-17) without noticing autonumberingdockwidget.cpp still referenced it via old-style
SIGNAL()/SLOT() macros, which fail silently at runtime instead of producing a compile error. This has remained broken on master ever since; a fix (57572a2, "Fix two broken signal
connections") exists on the unmerged qt6-cmake-elevatormind-merged branch but that one only commented the lines out without solving the underlying issue.

Removed the dead code entirely and call BorderTitleBlock::importTitleBlock() directly on the active diagram in on_m_folio_cb_activated() instead. The same mechanism is already
used elsewhere in the codebase (undo command, new-diagram creation, XML loading) to push TitleBlockProperties into a diagram and trigger a folio numbering recompute via needFolioData().
2026-08-05 12:17:49 +02:00
Andre Rummler ee36073428 Fix DiagramView window title not updating (missed rename from 27dcd5e)
27dcd5e renamed BorderTitleBlock::diagramTitleChanged to informationChanged and updated diagram.cpp accordingly, but diagramview.cpp still used the old string-based SIGNAL()/SLOT()
macro referencing the removed signal name, which failed silently at runtime instead of at compile time. The diagram/window title never refreshed after title block changes.

Observed when going through the warnings.

Updated the connect to use informationChanged with modern pointer-to-member syntax, matching diagram.cpp's own connect to the same signal.
2026-08-05 10:54:31 +02:00
Andre Rummler ab61e00cdf Fix QSignalMapper connects silently broken under Qt6 (using the old string based system). QSignalMapper::mapped(int/QWidget*) was deprecated in Qt 5.15 and
removed in Qt6, replaced by mappedInt/mappedWidget/mappedString.

What was broken:
* the logo-conflict rename dialog
* the system tray show/hide toggle
* the Window menu
* export dialog's per-diagram preview controls

Switched to the modern mappedInt/mappedWidget signals with pointer-to-member connect(), guarded for Qt < 5.15 until Qt5 can be dropped.
2026-08-05 10:29:31 +02:00
Andre Rummler fb51489436 Fix of previous commit: use textActivated signal instead. 2026-08-05 09:39:15 +02:00