Compare commits

...

442 Commits

Author SHA1 Message Date
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
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
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
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
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
plc-user c1551c7477 add English and German translations 2026-08-04 23:36:30 +02:00
plc-user 6183518b2f Merge pull request #633 from ispyisail/fix/blank-folder-names
Never leave a collection folder without a name (replaces #622)
Mark elements-folder with an exclamation-mark, when "qet_directory" is missing or faulty.
2026-08-04 23:01:40 +02:00
ispyisail f83aa3f1bc Fix stack-overflow crash in FileElementCollectionItem::setUpIcon()
The unconditional early return was narrowed to non-directories only, so
that the just-added warning badge could be picked up once setUpData()
resolved m_qet_directory_unreadable asynchronously. But every directory
then called setIcon() on every single data(Qt::DecorationRole) query --
not just once -- and QStandardItem::setIcon() -> setData() emits
dataChanged() unconditionally (QIcon has no equality check to suppress
it). QTreeView handles dataChanged() by recomputing the row's size hint,
which re-enters data() for the same index, calling setIcon() again:
unbounded mutual recursion, confirmed by an isolated reproduction to
overflow the stack in a single frame (100k+ frames) well before the
first paint completes. Matches plc-user's report of a segfault right as
the elements tree begins drawing.

The race the guard was widened for doesn't actually occur:
ElementsCollectionModel only attaches itself to the tree view (the only
way data() becomes reachable) from loadingFinished(), which fires after
the QtConcurrent::map over every item -- this one included -- has
already finished. m_qet_directory_unreadable is therefore always final
before setUpIcon() can run for the first time, so the plain, always-only-
once guard is sufficient and the badge still works correctly.
2026-08-05 08:29:16 +12:00
Kellermorph 06d1d70142 bug 2 2026-08-04 16:48:52 +02:00
ispyisail c0896c7ba7 Add "Insert folio above/below" to the elements panel's folio menu
"Add folio" always appends to the end of the project, ignoring
whatever folio is currently selected in the left panel -- even though
the panel already tracks the selected diagram's position for its
existing move up/down/top actions, and QETProject::addNewDiagram(pos)
already accepts an arbitrary insertion index, pushed as an undoable
AddDiagramCommand (QetGraphicsTableFactory::create() already relies on
this exact mechanism to insert a folio right after a specific one).

Add two new context-menu actions that compute the target position from
the selected diagram's folioIndex() and pass it straight through the
existing machinery -- no changes needed to QETProject or
AddDiagramCommand. New requestForNewDiagramAt/addDiagramToProjectAt
signal/slot pair added alongside the existing
requestForNewDiagram/addDiagramToProject rather than changing it, so
the plain "Add folio" action's append-at-end behavior is untouched.
2026-08-04 16:36:23 +12:00
Kellermorph 5ec49eedda fix bug 2 2026-08-03 21:48:34 +02:00
ispyisail f6b448daa7 Badge unreadable-qet_directory folders with a warning icon in the tree
@plc-user asked (review on #633) for a way to see a broken folder
directly in the tree instead of only on tooltip hover, originally
suggesting a "FixMe: " text prefix on the displayed name. That name is
reused verbatim in dialog titles and status-bar messages elsewhere
(elementscollectionwidget.cpp), so baking a prefix into it would leak
into those too. An icon badge gets the same visibility without
touching the name value.

setUpIcon() overlays a small warning glyph on the folder icon when
m_qet_directory_unreadable is set. Also drop the "already has an icon,
skip" guard for directories specifically: that flag is only known once
the async setUpData()/localName() job completes (QtConcurrent::map), so
without this a directory painted before that finished would have its
plain folder icon cached forever and never pick up the badge.
2026-08-04 06:57:16 +12:00
Kellermorph d850241f91 fix 2026-08-03 19:17:38 +02:00
Laurent Trinques 7307a59c10 Merge pull request #637 from ispyisail/feature-elmt-export-svg
Add "Export to SVG" to the element editor (discussion #605)
2026-08-03 16:34:54 +02:00
ispyisail ff812f221a Rework diagnostic logging: fix the file writer, add rotation and a ring buffer
Implements steps 1-3 of discussion #644 (deliberately not steps 4/5 --
no signal handler / crash flush, no diagnostics UI; see below).

## Step 1 -- fix the existing logger (bugs, no new behavior)

- One QFile handle held open for the whole session under a mutex,
  instead of opening and closing the log file on every single message.
- The log directory and the session's date-stamped filename are
  resolved exactly once, in the new QetLogger::init() called explicitly
  from main() immediately before qInstallMessageHandler() -- not
  recomputed per message, so a session that runs past midnight now
  stays in one file instead of silently splitting.
- Age-based retention now uses lastModified() instead of lastRead():
  opening a log to attach it to a bug report no longer resets its
  retention clock.
- stderr and file output both encode UTF-8 explicitly (toUtf8()),
  replacing stderr's toLocal8Bit() and the file stream's previously
  Qt5/Qt6-inconsistent default encoding.

## Step 2 -- size-capped rotation + hardening

- The previously-unbounded daily file is now capped at 2 MiB and
  rotated (kMaxFileBytes/kRotationKeep in QetLogger), keeping
  <date>.log plus <date>.1.log .. <date>.4.log; oldest is dropped.
- Each message is truncated to 4 KB with a "...[truncated N bytes]"
  marker before it reaches the ring or the file.
- Control characters (newlines, tabs, other non-printables) in message
  content are escaped, since much of what QET logs is externally
  controlled (file paths, element names, font strings out of a .qet
  file) -- left unescaped, an embedded '\n' could forge log lines.
- The log file is refused if a symlink already exists at that path,
  and is created/rotated owner-read/write only.

## Step 3 -- in-memory ring buffer

- LogRing (sources/logging/logring.h) is a fixed-capacity, always-on
  ring of the last 4096 log lines, preallocated once at construction
  (4096 * 512 B = 2 MiB) so append() never allocates. Entries are
  stored as plain pre-formatted bytes in fixed-size slots -- the shape
  discussion #644 specifies so a *future* crash handler could dump it
  with nothing but write(2), even though no such handler exists yet.
  Thread-safe via a plain QMutex (the lock-free requirement in the
  discussion applies specifically to a signal-handler read path, which
  this step doesn't add).

## Escape hatch

QET_LOG_DISABLE=1 in the environment at startup bypasses all of the
above -- no ring, no file, no rotation -- falling back to a minimal,
self-contained stderr passthrough that doesn't share any code with the
new formatting/sanitization path, so it stays usable even if that path
is what's misbehaving.

## Deliberately not included (per the discussion's own phasing)

- No signal handler / crash-time ring flush (step 4) -- the discussion
  flags this as the highest-risk piece, explicitly meant to land last
  and behind its own switch once the rest is proven.
- No diagnostics export UI (step 5).
- No log categories, session header, repeat collapsing or rate
  limiting -- listed under "best practices worth building in", not
  part of steps 1-3.

## Testing

Built clean, no new warnings.

Verified with real runs (QT_QPA_PLATFORM=offscreen, isolated HOME):
- Log file created at the expected dataDir()/YYYYMMDD.log path, mode
  0600.
- A full startup's worth of real messages (translations, MachineInfo's
  system dump, collection loading) written correctly; every one of the
  231 lines in one run starts with a proper timestamp -- confirmed the
  sanitizer correctly escapes the raw embedded newlines/tabs in
  MachineInfo's multi-line CPU/GPU description fields into visible
  \n/\t sequences rather than letting them fragment the log.
- QET_LOG_DISABLE=1: zero log files created, stderr still worked via
  the independent legacy path.
- Rotation: pre-filled a log to just under the 2 MiB cap, ran a normal
  session, confirmed it rotated to <date>.1.log (still 0600) with a
  byte-clean split (no truncated/duplicated line at the boundary) and
  a fresh <date>.log picked up from the next line.
2026-08-03 14:25:42 +12:00
ispyisail 2e0fe44174 Cover auto-numbering counter changes with undo/redo
Placing an auto-numbered element or conductor advances a shared
NumerotationContext counter (QETProject::addConductorAutoNum/
addElementAutoNum) as a side effect that sat entirely outside the undo
stack. Undoing the placement removed the visible number but left the
counter advanced, so every undo of an auto-numbered placement silently
burned a number, with no way to get it back short of a manual reset.

Adds SetAutoNumContextCommand, a small QUndoCommand storing the old/new
NumerotationContext and calling the matching add*AutoNum() setter on
undo()/redo() -- the same shape QPropertyUndoCommand already uses next
to it in ConductorAutoNumerotation::applyText().

Wires it into the two conductor call sites (the static newProperties(),
and numerateNewConductor(), both in ConductorAutoNumerotation) and the
element call site (Element::setUpFormula(), called from
DiagramEventAddElement::addElement() when a new element is dropped onto
a diagram). setUpFormula() now takes an optional parent QUndoCommand;
addElement() calls it before pushing its own undo_object so the counter
change lands in the same undo macro as the element's placement -- one
Ctrl+Z reverts both together, instead of leaving the counter adrift.

The project-properties config dialog's own add*AutoNum() calls (editing
the numbering rule itself, not a side effect of placing something) are
deliberately left untouched, as are the load-time folio-sequential
bookkeeping calls in Diagram::loadElmtFolioSeq()/loadCndFolioSeq() and
the bulk folio-renumbering passes in QETProject -- none of those run as
part of an undoable user gesture.

Implements the scope proposed in discussion #608.
2026-08-03 13:19:51 +12:00
ispyisail 6b9cb0a220 Add user-defined custom properties on elements
ElementInfoWidget's fixed ~40 predefined ELMT_* keys had no way for a
user to add a genuinely new element-info key, even though DiagramContext
already stores/round-trips arbitrary keys generically via toXml()/fromXml().

Adds an "Ajouter une propriété personnalisée" button that appends a
CustomElementInfoPartWidget row (both key and value user-editable,
unlike the fixed ElementInfoPartWidget rows bound to one predefined
key). The typed key is validated live against the existing
DiagramContext::isKeyAcceptable() and flagged with a red border when
it doesn't match, instead of silently dropping it. Any key already
present on the element that isn't one of the predefined/special keys
is re-displayed as a custom row on next selection.

Implements the scope proposed in discussion #611.
2026-08-03 10:28:26 +12:00
ispyisail eb520ac3f8 Pre-fill SVG export filename with the element's own name
Suggests the element's filename (without its .elmt extension) as the
default save name when exporting to SVG, instead of only defaulting to
the customElementsDir with no filename. Addresses plc-user's review
suggestion on PR #637.
2026-08-03 08:31:17 +12:00
Kellermorph ca42a2b7ff Auto-break conductor 2026-08-02 15:18:57 +02:00
ispyisail 8a72230916 Add "Export to SVG" to the element editor
Implements discussion #605. The diagram editor can already export a folio
to SVG; the element editor, where a single .elmt symbol is drawn, had no
export capability at all -- confirmed by grepping its header for "export"
before starting: nothing.

## Renders the live scene, not ElementPictureFactory's cache

The discussion proposed sourcing this from ElementPictureFactory's cached
per-element QPicture (m_pictures_H), the one used for the elements-panel
preview icons. Checked that cache's actual invalidation before building on
it: nothing in the editor ever tells it to drop an entry on edit, and it is
keyed by the element's on-disk uuid. So for any element already previewed
once in the panel, exporting from the cache would silently produce stale
content after any edit; for a brand-new, never-saved element, no entry
would exist at all. Neither is acceptable for a File > Export action a
user expects to reflect what's on screen right now.

Renders ElementScene directly instead, the same way
ExportDialog::generateSvg() already renders the live Diagram for the
diagram editor's own SVG export: no new drawing logic, only a new playback
target (QSvgGenerator instead of the screen), sized to the element's own
content bounds via the existing elementSceneGeometricRect() helper.

## Hotspot cross excluded from the export

ElementScene::drawForeground() draws the red origin/hotspot cross on every
render() call, unconditionally -- it's an editing aid, not part of the
element being drawn, and diagram editor's SVG export has no equivalent
problem since Diagram doesn't draw one. Added a settable
hotspotVisible flag, defaulting to true (the existing editing view is
completely unaffected) and turned off only for the duration of the export
render() call.

## Verified end-to-end via a real Xvfb session, not just a build

Opened a real shipped element (en_60617_05_06_04.elmt, "Phototransistor"),
exported it, and rendered the resulting SVG back to a bitmap with a small
QSvgRenderer-based harness -- pixel-identical in shape to the element as
shown in the editor. Confirmed the file is valid XML and contains no
red/#ff0000 stroke (the hotspot cross did not leak in).

Then the case the whole "render live, not cached" decision was about:
opened the same element, drew a new line with the line tool, and exported
again *without saving*. The new line is present in the exported SVG.
git status on the source .elmt file after both exports shows it completely
untouched -- the export is read-only and reflects live, unsaved editor
state, exactly the property a cache-based implementation would have gotten
wrong.

Built clean, no new warnings.
2026-08-02 23:06:12 +12:00
Laurent Trinques 834495387b Merge pull request #626 from ispyisail/feature-autonum-reset-buttons
Add quick reset buttons to the auto-numbering dock
2026-08-02 12:02:10 +02:00
Laurent Trinques 04b12ac719 Merge pull request #634 from ispyisail/feature-autonum-number-format
Numbering parts: user-settable display format (zero mask)
2026-08-02 11:50:59 +02:00
ispyisail ee4ba82d28 Refresh the dock's counter field when the numbering actually advances
Reported by @scorpio810 on #626: "The field does not update automatically;
you need to list the other rules for it to update."

Two reasons, both mine:

The refresh was wired to the combo boxes' activated() signal, which Qt
emits only for user interaction. Nothing that changed a context
programmatically -- which is to say, numbering an element -- ever reached
it. Re-picking a rule from the combo was not a workaround so much as the
only code path that refreshed at all.

And there was no signal to hang it on for two of the three categories:
addElementAutoNum() emitted elementAutoNumAdded(), but addConductorAutoNum()
and addFolioAutoNum() emitted nothing, so even a listener would not have
heard a conductor counter advance.

Add QETProject::autoNumContextUpdated(), emitted by all three setters, and
have the dock re-read its three fields on it. Kept deliberately separate
from the existing *AutoNumAdded/*Removed signals: those make listeners
rebuild their rule lists, which is both heavier than needed here and would
disturb the user's current selection every time an element is numbered.
This one only says "re-read me".

The automatic refresh skips a field that has keyboard focus, so numbering
an element cannot overwrite a value half-typed under the cursor. Explicit
refreshes after a reset or an edit still write unconditionally, so the
field always ends up showing the canonical stored value.

Measured, advancing a counter the way numbering advances it and without
touching the combo box:

  field before advance   "5"
  context after advance   6
  field after advance    "6"      (was still "5")
2026-08-02 20:41:17 +12:00
ispyisail d192d609ec Add a display format to numbering parts: a spreadsheet-style zero mask
A cyclic part could only ever be rendered at its natural width, which is
fine for one of @scorpio810's two real layouts and wrong for the other:

  April 5000/2000, 32-point cards   %IX0.0 .. %IX0.31, then %IX1.0
  Schneider M340, 64-point cards    I1.00 .. I1.63,    then I2.00

The first wants no padding, the second wants two digits. Since the two
conflict, the width cannot be derived from the modulus or from the part
type -- it has to be the user's to set.

Add a format field holding a run of zeros, the same convention a
spreadsheet uses for integer padding: "00" renders 7 as 07, "000" as 007.
The field's length is the minimum number of digits. It applies to every
numeric part type, not only cyclic ones, so "Chiffre 01" can be widened
past two digits without inventing another type for it.

An empty mask means the part type's own natural width, so it reproduces
exactly what every existing context does today -- Chiffre 1 stays 7,
Chiffre 01 stays 07, Chiffre 001 stays 007. That is what makes this safe
for existing projects: absent is the default, and absent changes nothing.

Stored as a sixth field on the context part and as an XML attribute
written only when set, following how modulus was added: readers guard on
size() and treat a short item as "no format". All seven places that
rebuild a part while incrementing it now carry the format through --
missing one would have silently dropped the padding on the second element
numbered.

The editor field is restricted to zeros by a validator, and is enabled
only for types that render as a number.

Measured:

  April, mask empty     %IX0.29 %IX0.30 %IX0.31 %IX1.0 %IX1.1
  M340,  mask "00"      I1.00 I1.01 ... I1.62 I1.63 I2.00 I2.01
  no mask               unit 7,8,9   ten 07,08,09   hundred 007,008,009
  ten with mask "0000"  0007 0008 0009
2026-08-02 20:29:02 +12:00
ispyisail 6f7e537db3 Let a Cyclique (modulo) part actually be displayed: %seqw_N
Reported by @scorpio810 on #632 with a screenshot: a "Chiffre 1" followed by
a "Cyclique (modulo) 8" numbers elements 0..7 and then jumps straight to 9,
never showing 8, and never producing the 0-7 / 10-17 / 20-27 pattern the
feature exists for.

The cause is that the wrap-and-carry feature shipped without its rendering
half. Commit 68c2603 added the arithmetic and the editor UI across seven
files, none of them assignvariables.*, so there is no %seqw_ variable, no
wrap list in sequentialNumbers, no branch in setSequential(), and no branch
in numerotationContextToFormula(). A cyclic part therefore contributes
nothing to the generated formula and cannot be referenced from one -- it is
invisible.

Invisible but not inert: it still advances and still carries. So the digit
in front of it receives +1 from the carry on top of its own increment, and
the only digit the label does show jumps by two. That is the missing 8.

Add the missing half:
  - sequentialNumbers gains a wrap list, handled in the copy constructor,
    assignment, comparison, clear(), toXml() and fromXml();
  - setSequential() collects wrap parts when the label uses %seqw_;
  - assignSequence() substitutes %seqw_N and counts wrap in its bound, so a
    context whose only counter is cyclic still resolves;
  - numerotationContextToFormula() emits %seqw_N, so adding a Cyclique part
    in the editor now puts a token in the formula instead of nothing.

Old projects are unaffected: <wrap> is simply absent from files written
before this, which fromXml() reads as an empty list, and such files have no
cyclic parts to reference in the first place. An older QET reading a newer
file ignores the unknown child.

Measured on the exact configuration from the report, unit + wrap(mod 8):

  formula generated   %sequ_1%seqw_1   (was %sequ_1 -- wrap contributed none)

  carry digit increment 1   00 11 22 33 44 55 66 77 90 101 112 ...
  carry digit increment 0   00 01 02 03 04 05 06 07 10 11 ... 17 20 21

The second line is the requested pattern. The first shows what is left once
the rendering is fixed but the carry digit still increments itself as well
as receiving the carry -- worth a UI decision, noted on the PR.
2026-08-02 18:46:28 +12:00
ispyisail 52c8ef6b49 Editable counter value in the dock, and fix two bugs in the reset button
Follows @scorpio810's review on PR #626 and three defects found by finally
running the thing rather than only building it.

Replace the "?" button with an editable value field, as asked for. It shows
the current value of the context's counter -- the last part that actually
progresses, i.e. the least significant digit -- and typing a new value and
committing it writes that value back. This is strictly more useful than the
button it replaces: "?" is still reachable by typing it, and any other value
is now reachable too, which was the point of the request.

It also removes a destructive edge the button had: "reset to ?" rewrote
*every* part, so a scheme built as "K" + counter became "?????" and the
configured prefix was gone for good. There is no undo command for
numbering contexts.

Two bugs fixed in the reset path itself:

- The project was never marked modified. addConductorAutoNum() and friends
  are a plain insert into a QMap; they emit nothing and set no dirty flag,
  and the properties dialog that this code was modelled on calls
  setModified(true) separately afterwards. Without it the user resets a
  counter, closes the project, is not asked to save, and the reset is lost.
  Verified before the fix: projectWasModified() stayed false across a click.

- A wrap part was reset to "1". A modulo part cycles over [0, modulus) --
  the PLC addressing that motivated the feature runs %IX0.0..%IX0.31 -- so
  its starting value is 0, not 1.

An empty value field is treated as "no change" rather than as an empty
value, so clearing the box by accident cannot wipe a counter, and the field
is refreshed from the context after every write and whenever the selected
context changes.
2026-08-02 18:33:14 +12:00
ispyisail 87a010eb75 Never leave a collection folder without a name, and say when it is broken
Fixes https://qelectrotech.org/bugtracker/view.php?id=332

localName() set a non-root folder's label only inside the success path of
loading its qet_directory file. If that load failed -- file missing,
malformed, or unopenable because of the Windows path-encoding problem with
accented characters that plc-user diagnosed on the tracker -- nothing was
set at all, and since a fresh item's text() is null the folder rendered
with a completely blank label. That is the reported symptom.

Resolve the name into a local and always fall back to the folder's own
directory name, so the label is never empty whatever went wrong.

The fallback is applied *after* NamesList::name() rather than passed into
it. This matters: name() returns a caller-supplied fallback before it
reaches its "first available translation" step, so passing m_path in would
replace a perfectly good name in some other language with the raw
directory name. A folder named only in French, viewed under an English
locale, previously showed "Accentué" and must keep doing so.

Falling back on its own would then hide the broken file -- the user sees a
plausible name and never learns there is anything to repair. So a folder
whose qet_directory could not be read now says so in its tooltip, naming
the file, above the collection path that tooltip already carried.
Suggested by plc-user on PR #622. The flag is recorded in localName() and
consumed in setUpData(), because setUpData() assigns the tooltip after
localName() runs and would otherwise discard it.

Only a file-level failure is flagged. A readable qet-directory with no
entry for the current language is not an error; NamesList::name() resolves
that itself and no warning is shown.

Verified on a fixture collection of four folders -- valid, malformed,
missing, and one named only in French:

                  master        this patch
  fr-only         Accentué      Accentué        (no warning)
  malformed       <blank>       malformed       (warning)
  no qet_directory <blank>      no_file         (warning)
  valid           Valid Folder  Valid Folder    (no warning)
2026-08-02 18:13:38 +12:00
ispyisail 66eaf13022 Default the modulus when a part becomes a Cyclique (modulo) part
Turning the default "Chiffre 1" part into a "Cyclique (modulo)" one left
the modulus spin box at 0, and a modulus of 0 means "no cycle" -- so the
part counted upward forever instead of wrapping, which is the whole point
of the type. Reported on #593 against a modulus-7 test and, more usefully,
against a real April 5000 PLC layout addressed %IX0.0..%IX0.31 per card.

setType() defaulted the modulus to 8 inside the block that installs numeric
behaviour, and that block runs only when the *previous* type was
non-numeric. Switching from one numeric type to another skips it. Since a
fresh part starts out as "Chiffre 1", the ordinary way to reach this
feature -- change the type of the part in front of you -- was exactly the
path that skipped the default. Going the long way round, via "Texte", set
the modulus to 8 and worked, which is why the feature tests fine when you
build the context some other way.

Moved the default out of that block so it applies whatever the part was
before, and made it fire only when the current modulus is unusable, so a
value the user picked on purpose survives switching type away and back.

The wrap/carry arithmetic itself was already correct: with a carry target
in front of it, a modulus-32 part yields %IX0.0..%IX0.31, %IX1.0 as asked.
Saved configurations are untouched -- a stored modulus, including a 0 left
behind by this bug, still loads and round-trips exactly as it was.
2026-08-02 17:49:29 +12:00
Laurent Trinques ec70bdc0a3 Merge pull request #623 from Kellermorph/plc-quickfix
Fix tiny bug in PLC manager
2026-08-02 07:44:21 +02:00
ispyisail 031710b5fc Add quick reset buttons to the auto-numbering dock
Resetting an active numbering counter back to a starting value, or
marking it as needing manual numbering, currently requires the full
round trip through the project properties dialog: open it from the
dock's Configure button, locate the right numbering context, select
the specific part row, clear and retype the value, confirm.

Add two small buttons next to each of the three combo boxes
(Conductor/Element/Folio) on AutoNumberingDockWidget itself:

- Reset to start: calls NumerotationContext::replaceValue() on every
  part that represents a progressing counter, using a sensible
  per-type value -- the part's own stored initialvalue for
  folio-anchored types (unitfolio/tenfolio/hundredfolio), "1" for
  plain numeric types and wrap, "a" for alpha. Non-incrementing types
  (string, plant, locmach, idfolio, folio, elementline,
  elementcolumn, elementprefix) are left untouched, since there's no
  meaningful "start" distinct from whatever the user configured for a
  fixed/contextual value.
- Reset to "?": sets every part's value to the literal placeholder
  "?" unconditionally, for marking a context as needing manual
  numbering.

Both write the updated context back via the same
addConductorAutoNum/addElementAutoNum/addFolioAutoNum calls the
project properties dialog itself already uses, so the dock's existing
refresh signals fire exactly as they do today.

Verified with a full build (Qt6) after the change -- clean compile
and link, including the .ui-generated Ui class correctly picking up
the six new button object names. Wasn't able to get a reliable live
GUI run in this environment to click-test the buttons themselves (ran
into unrelated session/display instability before any interaction
with the new buttons occurred), so this is verified by code review
and successful build rather than a runtime screenshot.

Implements https://github.com/qelectrotech/qelectrotech-source-mirror/discussions/597
2026-08-02 08:24:01 +12:00
Kellermorph fed32be90e Fix tiny bug in PLC manager 2026-08-01 20:20:51 +02:00
Laurent Trinques c1694f2b4f Merge pull request #620 from ispyisail/fix/element-panel-dark-theme
Fix element library icons invisible on dark OS themes
2026-08-01 15:47:18 +02:00
Laurent Trinques 2d99ba67f1 Merge pull request #588 from Kellermorph/Fix-PLC-Manager
Fix-PLC-Manager
2026-08-01 15:30:10 +02:00
ispyisail bb61dde811 Apply the elements-panel light palette to the viewport too
ElementsPanel and ElementsTreeView already force a fixed light palette
(white base, black text) on themselves, specifically because element
icons are rendered with colors read directly from each .elmt file --
almost always black linework, matching printed-schematic convention --
onto a transparent background. That only stays legible if the row
background is reliably light, regardless of the OS/desktop theme.

But QAbstractItemView paints row backgrounds using its viewport's
palette, not the view widget's own palette. setPalette() on the view
itself doesn't propagate to viewport() in the general case, so under
styles that actually respect the viewport's (unset, therefore
theme-inherited) palette -- e.g. KDE Plasma's Breeze Dark -- the row
background falls through to the app's dark palette while the element
linework is still literal black, making library icons and terminal
symbols invisible.

Apply the same QPalette to viewport() right after setPalette() in both
constructors, so the fix these two classes already clearly intended
actually takes effect under every style.

Fixes https://qelectrotech.org/bugtracker/view.php?id=335
2026-08-02 01:23:23 +12:00
Kellermorph e91bab14fe Update 2026-08-01 14:26:39 +02:00
Laurent Trinques eb140dac60 Merge pull request #594 from ispyisail/feature-alpha-autonum
Add alphabetical auto-numbering (a, b, ... z, aa, ab, ...)
2026-08-01 12:55:07 +02:00
ispyisail f7a79e75af Add alphabetical auto-numbering (a, b, ... z, aa, ab, ...) (#579)
Adds a real base-26 incrementing part type to the autonumbering engine,
alongside the 14 existing NumStrategy leaves. Unlike StringNum (a fixed,
non-incrementing text segment), AlphaNum::next()/previous() carry/borrow
entirely within the part's own value -- the composition loop in
NumerotationContextCommands doesn't need to change, since (unlike #578's
wrap-and-carry) nothing here needs to signal an adjacent part.

- incrementAlpha()/decrementAlpha() implement the spreadsheet-column-name
  algorithm: increment carries right-to-left on 'z'/'Z' overflow,
  prepending a new leading letter if the whole value overflows (z -> aa,
  az -> ba). decrement is the exact inverse, including the symmetric
  shrink case (aa -> z) once every position has borrowed. A single letter
  already at "a"/"A" has no representable predecessor and is clamped
  rather than turned into "z" -- caught via manual testing, since the
  initial implementation mutated the string in the borrow loop before
  checking whether to clamp, silently discarding the original value.
- Registered in NumerotationContext::validRegExpNum() but deliberately
  not in validRegExpNumber(), so addValue() doesn't force alphabetic
  values through int conversion.
- New "Cyclique"-adjacent "Alphabétique" entry in numparteditorw's type
  dropdown, with its own letters-only QRegularExpressionValidator; the
  increase spinbox is disabled since the step is always exactly one
  letter, not a configurable amount.

Also wires the new part type through to actual element/conductor labels,
which turned out to be required for the feature to do anything visible
beyond folio numbering (which applies a NumerotationContext's
represented string directly). Element and conductor numbering instead
go through a separate formula-substitution layer
(autonum::sequentialNumbers + %sequ_/%seqt_/%seqh_-style placeholders in
AssignVariables::assignSequence()) that numerotationContextToFormula()
auto-populates. Without a matching placeholder, an "alpha" part would
silently vanish from the generated formula and never reach the label,
even though the underlying counter was advancing correctly:
- sequentialNumbers gained an `alpha` QStringList member (copy ctor,
  operator=, operator==, toXml/fromXml, clear()).
- numerotationContextToFormula() emits a new %seqa_N placeholder for
  alpha parts, the same way %sequ_N is emitted for unit parts.
- setSequential()/setSequentialToList() populate seqStruct.alpha,
  passing the raw string through as-is rather than the .toInt()-based
  formatting used for the numeric part types.
- AssignVariables::assignSequence() substitutes %seqa_N from
  seqStruct.alpha, mirroring the existing %sequ_N/%seqt_N/%seqh_N
  substitutions.
No "alphafolio" variant was added, matching the discussion's scope (only
unit/ten/hundred have folio-anchored variants).

Verified against production code via the numbering config dialog's own
Suivant/Précédent buttons: from "a", 25 clicks reached "z"; one more
produced "aa"; 25 more reached "az"; one more produced "ba" (carry).
Reversed: "ba"->"az"->(25 clicks)->"aa"->"z" (shrink)->(25 clicks)->"a".
One more "previous" at "a" correctly stayed at "a" after the clamp fix.
Also confirmed the Formule field auto-updates to "%seqa_1" the instant
the type is switched to "Alphabétique", confirming the formula-generation
wiring works live in the UI, not just at the engine level.
2026-08-01 22:45:49 +12:00
Laurent Trinques 25effbd137 Merge pull request #592 from DieterMayerOSS/pr/font-report
Report salvaged and unreadable font descriptions after opening a project
2026-08-01 11:41:55 +02:00
Dieter Mayer 3bc2e8e712 Make the font restoration counters safe against nested project loads
DialogWaiting pumps the event loop while the folios of a project are
built, so a second openAndAddProject() can run to completion nested
inside the first one (drop on another editor window, queued open) and
the plain reset/read counters would then report the wrong numbers.

Replace them with a RAII counting window (FontRestorationScope): the
constructor keeps the enclosing counts aside, the destructor restores
them. The nesting is strictly LIFO - the nested load completes inside
the pump of the outer one - so each load reports exactly its own
numbers, and the early-return paths of openAndAddProject() restore the
outer window automatically.

Suggested by ispyisail in the review of the reporting change.
2026-08-01 11:14:04 +02:00
Laurent Trinques b446074731 Merge pull request #593 from ispyisail/feature-wrap-and-carry-autonum
Add wrap-and-carry (cyclic/modulo) auto-numbering
2026-08-01 11:09:58 +02:00
Laurent Trinques a4d75c4d8c Merge pull request #586 from ispyisail/feature-jump-to-element
Add Ctrl+G "jump to element" quick-open popup
2026-08-01 10:58:33 +02:00
ispyisail 68c260342e Add wrap-and-carry (cyclic/modulo) auto-numbering (#578)
Adds a configurable wrap-at-N counter type to the autonumbering engine
(NumerotationContext + NumerotationContextCommands), covering PLC/rack-style
addressing conventions like "e0.0...e0.7, e1.0...e1.7" (8 channels per
card) generally, rather than hardcoding octal specifically.

- New "wrap" part type (WrapNum, alongside the existing UnitNum/TenNum/
  HundredNum strategies) stores a modulus in addition to the existing
  value/increase/initialvalue fields. Its own next()/previous() only wraps
  its own value modulo the configured modulus -- carrying into (or
  borrowing from) the adjacent part requires visibility across parts,
  which only the composition loop has.
- NumerotationContextCommands::next()/previous() gained carry()/borrow()
  helpers: when a wrap part's own next() would reach/exceed its modulus
  (or go below 0 on previous()), the nearest preceding numeric part is
  bumped by exactly one unit, skipping non-numeric parts (e.g. a "."
  string separator). Wrap parts chain correctly if adjacent (e.g. seconds
  wrapping into minutes wrapping into hours).
- For the leading part of a wrap-and-carry pair to stay fixed except when
  carried into (i.e. actually produce "e0.0...e0.7, e1.0..." rather than
  advancing on every step under its own strategy), its own increase must
  be 0. The increase spinbox's minimum was 1, which made this
  configuration impossible through the UI -- lowered to 0 and documented
  with a tooltip, since this wasn't obvious from the UI alone.
- NumerotationContext gained a 5th pipe-separated field (modulus) in its
  serialized string form, defaulting to 0 (non-wrapping) for every
  existing part type; toXml()/fromXml() persist it as a "modulus" XML
  attribute the same way "initialvalue" is already persisted for
  unitfolio/tenfolio/hundredfolio.
- New "Cyclique (modulo)" entry in the part-type dropdown (numparteditorw),
  available for element, conductor, and folio autonumbering alike, since
  all three already go through NumerotationContextCommands.

Verified in the running app via the numbering config dialog's own
Suivant/Précédent buttons (which call the production
NumerotationContextCommands::next()/previous() directly): a two-part
context (unit, increase=0 + wrap mod 8) produced exactly
e0.0→...→e0.7→e1.0→...→e1.7 on repeated "next", and the exact reverse
(with correct borrowing) on repeated "previous".
2026-08-01 19:33:39 +12:00
Dieter Mayer 0fa2591c4f Report salvaged and unreadable font descriptions after opening a project
Until now a font description that could not be parsed only produced
console warnings most users never see, so nobody learned that their
texts silently lost their formatting (see the reports in issue #553).

Count in QETUtils::fontFromString() how many descriptions were salvaged
from a foreign or corrupt format and how many stayed unreadable, and
show a message box after opening a project when either happened:
salvaged descriptions are rewritten in the stable format on the next
save, unreadable ones fall back to the default font. Projects without
font issues open exactly as before, and non-interactive opens only log
the counters.

Verified with a Qt 5.15 build on a project carrying 52 19-field and
one 21-field description: the dialog reports 53 restored descriptions;
the same file on a Qt 6.11 build (which parses those formats natively)
shows no dialog.

See issue #553.
2026-08-01 08:49:55 +02:00
Laurent Trinques acf2320f68 Update translations files 2026-08-01 07:34:38 +02:00
Laurent Trinques 6cdf60e2e7 Merge pull request #587 from ispyisail/feature-configurable-shortcuts
Add configurable shortcuts: Shortcuts preferences page + app-wide registry
2026-08-01 07:23:56 +02:00
Laurent Trinques 98c56fcc7b Merge pull request #590 from ispyisail/feature-folio-undo-redo
Add undo/redo support for folio add, delete, and reorder
2026-08-01 07:09:57 +02:00
ispyisail 55886e5e8a Add undo/redo support for folio add, delete, and reorder (#575)
Three new QUndoCommand subclasses (AddDiagramCommand, RemoveDiagramCommand,
MoveDiagramCommand) pushed onto the project's existing (already
project-scoped) undo stack, so folio structure edits are undoable
alongside every item-level edit already on that stack.

- QETProject::addDiagram()/detachDiagram() are the shared attach/detach
  primitives: they mutate the diagram list, connect/disconnect the two
  per-diagram signals set up at add time, and emit diagramAdded/
  diagramRemoved. AddDiagramCommand and RemoveDiagramCommand call these
  (via friend access) for both redo and undo, so a removed diagram is
  parked rather than destroyed -- it's only actually deleted if the
  command itself falls out of undo history while still detached.
- ProjectView reacts to diagramRemoved the same way it already reacted to
  diagramAdded (tearing down/rebuilding the tab), so both directions of
  both commands go through the same reactive path every other diagram
  listener (project database, cross-references, generic panel) already
  relies on.
- MoveDiagramCommand wraps a new ProjectView::setDiagramPosition(), which
  performs the tab move and the project's diagramOrderChanged() list
  reorder synchronously in one step, instead of relying on the queued
  tabMoved connection (needed for interactive drag-and-drop) to catch up
  later -- avoiding a second, redundant reorder from that queued call.
- Multi-folio delete and multi-folio move (QETDiagramEditor::removeDiagrams()
  and the moveDiagram*(QList<Diagram*>) batch slots) wrap their per-diagram
  loop in QUndoStack::beginMacro()/endMacro(), so a multi-select action is
  one undo step, matching current UX.
- Softened the delete confirmation's "this change is irreversible" wording
  now that it no longer is.

Verified headlessly (Xvfb + xdotool + scrot): add/undo/redo, delete/undo/
redo (single and multi-select, single undo step for the batch), and
move/undo/redo all behave correctly against a 7-folio project.
2026-08-01 17:03:39 +12:00
Laurent Trinques c772e1d3ea Merge pull request #589 from ispyisail/feature-element-editor-coord-readout
Add live cursor-coordinate readout to the element editor status bar
2026-08-01 05:59:23 +02:00
ispyisail 9637f0ff9e Add live cursor-coordinate readout to the element editor status bar (#580)
Displays the cursor's scene position (same grid units as the parts'
X/Y property spinboxes) in a permanent status bar label, updated on
every mouse move. Addresses the overlapping-node mis-click case from
the originating forum report: with a live readout, precise pointing
no longer requires guessing against nearby z-ordered points.

ElementScene::mouseMoveEvent already computed the (optionally
grid-snapped) scene position on every move; it now also emits it via
a new mouseMoved(QPointF) signal, which QETElementEditor's status bar
label subscribes to.
2026-08-01 15:12:44 +12:00
Kellermorph 4c1f82f814 Fix-PLC-Manager 2026-07-31 21:02:07 +02:00
ispyisail 5275fb44fe Add configurable shortcuts: ShortcutManager registry + Shortcuts config page (#574)
Implements the first pillar of #574: a "Shortcuts" preferences page letting
users rebind, search and reset every keyboard shortcut in the app.

What it does
- New ShortcutManager singleton: every one of the ~95 setShortcut()/
  setShortcuts() call sites across qet.cpp, qetmainwindow.cpp,
  elementspanelwidget.cpp, autonumberingdockwidget.cpp, richtexteditor.cpp,
  qetdiagrameditor.cpp, qettemplateeditor.cpp and qetelementeditor.cpp now
  calls registerAction(target, id, category, default_sequence) instead,
  which applies the user's saved override (or the default) and remembers
  the target for later editing.
- New ShortcutsConfigPage, added to the existing "Configurer QElectroTech"
  dialog: a filterable table of every registered shortcut, grouped by
  category, each with a QKeySequenceEdit and a per-row reset button, plus a
  "reset all" button. Bindings are only persisted (via
  ShortcutManager::setSequence()) when the dialog is accepted.
- Conflict detection: rows whose currently-edited sequence collides with
  another row are highlighted with a tooltip naming the conflicting action.
- Overrides are stored under a "shortcuts/" QSettings group, one key per
  id, keyed to match the id (not persisted at all when equal to the
  hardcoded default), so a future QET version can safely raise a default
  for anyone who never customized it.

Design notes
- Targets are handled generically via QObject rather than QAction, since one
  call site (autonumberingdockwidget's "Configurer" button) is a
  QPushButton, not a QAction. Both declare an identical "shortcut"
  QKeySequence Q_PROPERTY, so registerAction() reads/writes it through the
  property system instead of needing a separate code path.
- Several live targets can share one id at once -- QET allows multiple
  windows of the same kind (diagram editor, element editor...) open
  simultaneously, each constructing its own QAction with the same id.
  setSequence() updates every live target for that id in one call, so a
  rebind takes effect in all open windows immediately, without restart.
- A shortcut's description is captured from its target's text() the first
  time that id is registered, then cached -- so the config page stays
  correct even after the owning window is closed. One consequence: a
  shortcut belonging to an on-demand window (element editor, title block
  editor, rich text editor) only appears in the list once that window has
  been opened at least once in the current session, since nothing has
  registered its id yet otherwise.

Testing
Full CMake build (qmake CONFIG+=no_kf5, Qt 5.15) compiles clean with zero
errors and zero new warnings. Verified end-to-end in a real running session
(Xvfb + xdotool):
- The Shortcuts page appears in Configure QElectroTech with the right icon,
  lists every always-registered shortcut with correct category/action name/
  current binding.
- The filter box correctly narrows the list, and correctly returns nothing
  for an action whose owning window hasn't been constructed yet this
  session (confirming the on-demand-registration behavior above is working
  as designed, not silently broken).
- Conflict detection correctly flagged a real pre-existing same-key overlap
  between "Supprimer" (delete selection, Del) and "Supprimer ce folio"
  (delete diagram from panel, Del) -- both highlighted with explanatory
  tooltips.
- Rebound "Manuel en ligne" to Ctrl+Shift+M, clicked OK: persisted under
  [shortcuts] in QElectroTech.conf, and the Aide menu's entry showed the new
  binding immediately, no restart needed.
- Reopened the dialog: the rebind was still shown. Clicked its per-row
  reset button, then OK: the settings key was removed entirely (not stored
  as "F1"), correctly falling back to the hardcoded default.

Retrofitting the Tab/Shift+Tab, select-all (#585) and Ctrl+G jump-to-element
(#586) shortcuts through this registry is left for a follow-up once those
PRs land, to avoid re-merging still-open branches into this one.

Developed with assistance from Claude (Anthropic).
2026-08-01 01:35:51 +12:00
ispyisail 80fcd4283d Add Ctrl+G "jump to element" type-ahead popup (#574)
Implements the third pillar of #574: a lightweight quick-open popup
for jumping straight to an element on the current diagram, rather
than scrolling/scanning visually.

New JumpToElementDialog (sources/ui/): a small QDialog with a filter
QLineEdit and a live-filtered QListWidget beneath it. Built from
every Element on the diagram, searchable against its label
(elementInformations().value("label")), type name (Element::name()),
and every other element information value, joined into one
lowercased search string per candidate. Up/Down move through the
filtered list, Enter selects the highlighted element on the diagram
(clearing the rest of the selection) and scrolls it into view via
ensureVisible(), Escape cancels without changing the current
selection. All three are handled via an event filter on the line
edit, so the user never has to leave the text field to navigate or
confirm.

Triggered by a new Ctrl+G action in QETDiagramEditor, added next to
the existing Ctrl+F "search and replace" action and to the Edit
menu. Confirmed free: not used anywhere in qetdiagrameditor.cpp or
qetmainwindow.cpp today.

Explicitly not a duplicate of the existing SearchAndReplace module
(also on this menu, via Ctrl+F): that's a bulk property search/replace
tool across whole diagrams; this is a single-item navigational
popup with no editing capability.

Verified end-to-end in a real running session (Xvfb + xdotool)
against a multi-transistor schematic: Ctrl+G opens the popup listing
every element; typing "Q16" live-filters down to the one match;
arrow keys move the highlighted row through the filtered list;
Enter selects the highlighted element (confirmed via the properties
panel showing its label) and closes the popup; Escape closes it
without changing the selection.

See discussion #574.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-01 01:05:43 +12:00
Laurent Trinques 031441884a Merge pull request #584 from ispyisail/fix-potential-selector-cancel
Fix: potential-selector dialog can't actually be cancelled (#581)
2026-07-31 10:49:54 +02:00
ispyisail 68e75b4c55 Fix: potential-selector dialog can't actually be cancelled (#581)
PotentialSelectorDialog::chosenProperties() built an OK-only dialog
and discarded exec()'s return value entirely:

    dialog.exec();
    for (QRadioButton *b : H.keys()) {
        if (b->isChecked()) return H.value(b);
    }
    return ConductorProperties();

Escape and the window close button already trigger QDialog::reject()
on a plain QDialog, but since the result was never checked, dismissing
the dialog without picking anything just silently returned blank
ConductorProperties() -- the same value returned when a real potential
was chosen but happened to produce empty properties. There was no way
to distinguish "the user cancelled" from "the user chose an empty
potential", so the caller always proceeded as if a choice had been
made.

Add a real Cancel button, check dialog.exec() == QDialog::Accepted,
and report cancellation through a new optional `bool *cancelled`
out-parameter. Also pre-select the first entry, closing a related gap
where clicking OK without ever touching a radio button hit the exact
same "silently returns blank properties" failure mode.

Thread the result through ConductorCreator::setUpPropertieToUse()
(now returning bool) so the calling constructor aborts and creates no
conductors at all when the user cancels, instead of proceeding with
blank properties.

The sibling constructor-based PotentialSelectorDialog (used for
conductor/report potential linking, a separate flow) already gates its
side effects behind on_buttonBox_accepted(), so cancelling it was
already safe -- gave it a visible Cancel button too for consistency
while touching this file, no behavior change there.

Verified with real Qt event simulation (QTest::mouseClick/keyClick)
against the exact new dialog-building logic: clicking Cancel and
pressing Escape both correctly report cancellation with empty
properties; clicking OK untouched returns the pre-selected first
entry; selecting the second option then OK returns that selection.

See discussion #581.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 20:39:04 +12:00
Laurent Trinques 46cceb328e Merge pull request #582 from DieterMayerOSS/pr/font-legacy-format
Write font descriptions in a version-stable format (#553)
2026-07-31 10:30:49 +02:00
Laurent Trinques 8793df4c86 Update translations files 2026-07-31 09:55:17 +02:00
Laurent Trinques 717c89fa66 Merge pull request #562 from Kellermorph/PLC-Manager
PLC Manager
2026-07-31 09:05:30 +02:00
Laurent Trinques 1836bce7e1 Merge pull request #583 from ispyisail/feature-project-usage-timer
Add a local, per-project time-spent tracker
2026-07-31 08:58:33 +02:00
Dieter Mayer 68ffacc9e6 Salvage font descriptions written by Qt 6.11+ when parsing fails
QFont::fromString() of Qt 5.x and Qt <= 6.10 rejects the >= 19 field
descriptions QFont::toString() emits since Qt 6.11, silently leaving a
broken font at every read site. Add QETUtils::fontFromString(): try the
native parser first, and on failure re-compose the legacy 10/11 field
form from the known Qt 6.11 field layout (OpenType weight mapped back
to the legacy scale) so no font information stored in existing files is
lost. Also salvage the 21 field double-serialized descriptions left
behind by some historical builds (a complete legacy description
embedded as the family name of a second one) by taking the embedded
leading description, matching what the lenient parser of Qt 6.11+
resolves them to. All font read sites now go through the helper; on
failure the default font of the caller is left untouched instead of a
cleared family.

Verified end to end on a Qt 5.15 build: a project whose 53 font
attributes were rewritten into the 19 field Qt 6.11 format loads and
autosaves byte-identical to the original legacy file (family, sizes,
bold/italic/underline, style name all preserved), and a mixed file
containing the exact 21 field string from the issue comes back
normalized as "Caladea,9,-1,5,75,1,0,0,0,0,Bold Italic".

See issue #553.
2026-07-31 08:10:34 +02:00
ispyisail 464d516537 Add a local, per-project time-spent tracker (#576)
Adds a ProjectUsageTracker (sources/project/) that accumulates how
long a project has been the active tab, using QElapsedTimer so the
value is computed on demand rather than via polling. It's hosted on
ProjectPropertiesHandler per that class's own stated design intent
("all new properties should be managed by this class").

The accumulated time is persisted as a new <usage time_spent="N"
enabled="true|false"/> element, a sibling of <properties> in the
project XML, written/read by new QETProject::writeUsageXml()/
readUsageXml(). It rides along on the existing autosave path for
free, since writeBackup() already serializes the full project via
toXml().

QETDiagramEditor::subWindowActivated() now pauses every open
project's tracker except the one whose tab just became current, so
switching between several open projects keeps each project's tracked
time isolated.

Surfaced in the existing Project Properties "Général" page: a
"Temps passé sur ce projet" display, a "Réinitialiser" button, and
an opt-out checkbox ("uniquement enregistré localement dans ce
fichier" - this is local-only, never transmitted anywhere).

Verified beyond compiling: full CMake build, then an actual runtime
session confirming the saved XML's time_spent value, that closing
and reopening the project round-trips and resumes timing, that the
reset button works, and - the key correctness check - that with two
projects open, the inactive one's time_spent stays frozen while the
active one accumulates real elapsed time, over the same interval.

See discussion #576.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 09:52:32 +12:00
Dieter Mayer 5da1fe5fc8 Write font descriptions in the stable Qt 5 10/11-field format
QFont::toString() is not stable across Qt versions: Qt 6.11 switched to
a 19-field format carrying OpenType weights, which QFont::fromString()
of Qt 5.x and Qt <= 6.10 rejects, leaving a broken font. Projects saved
by a Qt 6.11+ build were therefore unreadable by older builds.

Add QETUtils::fontToString() composing the legacy 10/11-field
description (weight mapped back to the legacy scale with the same
closest-match table Qt uses when parsing) and use it at every site that
stores a font description in a project, element, table config or
settings file. Every Qt version from 5.15 through 6.12-beta parses this
form correctly, so files stay readable by every QET build in
circulation.

See issue #553.
2026-07-30 21:45:36 +02:00
Laurent Trinques 120ca08209 Merge pull request #571 from DieterMayerOSS/pr/element-panel-tooltip
Element panel: show name and element information in the tooltip
2026-07-30 11:33:44 +02:00
Laurent Trinques 92f63d7b3c Merge pull request #573 from ispyisail/fix-dynamictext-drag-select
Fix #487: drag-selecting dynamic text fields silently converts their text source
2026-07-30 11:28:22 +02:00
Laurent Trinques c09a8f918f Merge pull request #572 from ispyisail/fix-titleblock-empty-var-override
Fix #531: page-level empty title block variable no longer shadows project-level value
2026-07-30 11:24:16 +02:00
ispyisail b60e73d93b Fix #487: drag-selecting dynamic text fields silently converts their text source
updateForm() called on_m_text_from_cb_activated() directly, intending
only to enable the sibling widget matching the combo box's current
index ("For enable the good widget"). But that slot also loops over
every currently selected part and pushes an undo command overwriting
textFrom on any part that doesn't match, since it's normally only
reached via the combo box's own activated(int) signal (real user
interaction, never fired by programmatic setCurrentIndex()).

updateForm() runs on every selection change, so during a rubber-band
drag over dynamic text fields with different sources, each time a new
field enters the selection, the representative part's textFrom gets
force-applied to every other selected part - converting e.g. a
UserText field to ElementInfo mid-drag, before the user has released
the mouse or interacted with the combo box at all.

Split the cosmetic widget-enable logic into updateTextFromWidgetsEnabled(),
called from updateForm(). on_m_text_from_cb_activated() keeps the
part-mutating loop, now only reached from real user activation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 19:11:25 +12:00
ispyisail f9357e269b Fix #531: page-level empty title block variable no longer shadows project-level value
BorderTitleBlock::updateDiagramContextForTitleBlock() merged the page's
"additional fields" over the project-level context unconditionally,
even when the page-level value was empty. Since #495 auto-adds every
template custom variable to the folio's Custom tab with an empty
value (so the user only has to fill in what's missing), simply
opening/confirming the Folio Properties dialog now permanently blanks
out any project-level custom variable of the same name — and it's
self-perpetuating, since the dialog re-adds the empty entry every time
it's reopened.

Skip page-level values that are empty when merging, so a real
project-level value shows through. An explicit non-empty page-level
override still takes precedence as before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 18:32:54 +12:00
Kellermorph 3db298996a Fix 2026-07-29 16:40:17 +02:00
Dieter Mayer db514c6a25 Element panel: show name and element information in the tooltip
The element tooltip showed only the collection path - the least useful
string exactly when a long descriptive name is truncated in the tree
(qelectrotech#552). Show instead: localized name, description,
manufacturer and manufacturer reference (each only when set), with the
collection path kept as the last line. Directories and .qetmak entries
keep the plain path tooltip.

Reuses the location/context already parsed right above for the search
index, so no additional file access or parsing.

GUI-verified on a library where every element carries these fields:
hovering an element now shows e.g. name, "Hutschienennetzteil
85-264VAC auf 24VDC, 92W, Schutzklasse II", manufacturer, order number
and path on five lines.
2026-07-29 12:32:22 +02:00
Laurent Trinques 88962570a8 Merge pull request #569 from DieterMayerOSS/pr/parttext-alignment
Element editor: optional alignment for static texts
2026-07-28 17:45:43 +02:00
Laurent Trinques f3e8cce6c8 Merge pull request #567 from DieterMayerOSS/pr/factory-drop-pugixml-pass
ElementFactory: read link_type from the cached QDom, drop the pugixml pass
2026-07-28 17:43:01 +02:00
Laurent Trinques ea846cb24e Merge pull request #566 from DieterMayerOSS/pr/hash-contains
Replace hash.keys().contains(k) with hash.contains(k) (35 call sites)
2026-07-28 17:41:23 +02:00
Laurent Trinques ca8d907e70 Merge pull request #570 from DieterMayerOSS/pr/xmlcollection-linear-lookup
Fix the Qt6 load-time regression: linear child lookup in XmlElementCollection
2026-07-28 17:39:59 +02:00
Laurent Trinques b52b1d5984 Merge pull request #565 from DieterMayerOSS/pr/qt6-partslist-printdialog
Restore two more Qt5-only code paths on Qt6 (parts list, print dialog)
2026-07-28 17:38:44 +02:00
Laurent Trinques f854fe0553 Merge pull request #564 from DieterMayerOSS/pr/restore-qt6-autosave
Restore crash-recovery autosave on Qt6
2026-07-28 17:37:07 +02:00
Laurent Trinques db759344fd Merge pull request #563 from DieterMayerOSS/pr/log-handler-before-startup
Install the log message handler before the application starts
2026-07-28 17:35:11 +02:00
Laurent Trinques e7dfa4bc64 Merge pull request #568 from DieterMayerOSS/pr/qdom-invaliddata-policy
Harden QDom serialization against invalid data (CVE-2026-15037)
2026-07-28 17:33:43 +02:00
Dieter Mayer de82b2738f XmlElementCollection::child(): linear sibling walk instead of item(i)
The child lookup iterated parent_element.childNodes() via item(i), and
QDomNodeList::item() walks the sibling chain from the start on every
call - making the loop quadratic in the number of children, with an
extra QList allocation and a second pass on top. This lookup runs
several times per element instance while loading a project, against the
"import" category that holds every embedded definition, so the cost
scales with (instances x embedded definitions).

Replace it with a firstChildElement()/nextSiblingElement() walk with an
early return. Same semantics (first tag+name match in document order).

Measured on the Kaefer_1303 reference project (3.9 MB, 23 folios,
432 instances, media of 6 runs, Windows/MinGW, same GCC for both):

           before      after
  Qt5      5.116 s     5.068 s
  Qt6      6.683 s     4.640 s   (-31 %)

This removes the entire Qt6 load-time regression discussed in #553 -
Qt6 goes from +31 % slower to 8 % faster than Qt5 on the very project
that exposed it (Qt6''s QDom makes the quadratic pattern much more
expensive than Qt5''s did). Smaller projects gain too (3.4 MB example:
-9 % on Qt6).

(cherry picked from commit 0d4ef8eca27601c37ba2b75d7c058e9d8e2beea4)
2026-07-28 14:08:27 +02:00
Dieter Mayer 787335582b Element editor: optional alignment for static texts (#549)
Static texts (PartText) gain an optional alignment, exposed via the
existing AlignmentTextDialog behind a new "Alignement" button in the
static text editor:

- The horizontal part aligns the lines of a multi-line text relative
  to each other (centered block labels no longer need one hand-placed
  text per line).
- The full alignment defines the anchor: when the content or font
  changes later, the selected corner/center of the bounding rect keeps
  its place instead of always growing right/down from the top-left
  (same prepareAlignment/finishAlignment logic as DiagramTextItem).

Format: the <text> node takes the same optional Halignment/Valignment
attributes as dynamic_text, written only when they differ from the
historical top-left behaviour - existing .elmt files are untouched and
round-trip byte-identical. The saved x/y stay the baseline-left of the
text block in all cases; ElementPictureFactory only needs the line
alignment (the anchor is editor-side behaviour), so rendered elements
match the editor exactly.

German translations for the three new strings included (qet_de stays
complete, 2686/2686).

Verified headless: a project embedding a two-line text once with
Halignment=AlignHCenter and once without exports to SVG with the short
line centered under the long one (x 102.5 vs 126.5) in the aligned
block, and identical x for both lines in the legacy block. Editor-side
anchor behaviour follows the proven DiagramTextItem implementation but
was not manually exercised in the GUI yet.
2026-07-28 13:58:51 +02:00
Dieter Mayer 1caa8920ae Harden QDom serialization against invalid data (CVE-2026-15037)
Set QDomImplementation::setInvalidDataPolicy(ReturnNullNode) at startup.
Qt before 6.12 defaults to accepting invalid data when building QDom
nodes, so untrusted text inserted into comments, CDATA sections or
processing instructions could break out of its node on serialization
(XML injection, low severity). Qt 6.12 flips the default to
ReturnNullNode; opting in explicitly gives the same behavior on any
Qt 5/6 version, so no version guard is needed.

Verified: --resave of a 5-folio project produces valid XML with all
folios intact, and --export-pdf still works on the result.

(cherry picked from commit 7804ef3864a0b8643cfe13b68b8e0e6235508ab6)
2026-07-28 13:58:34 +02:00
Dieter Mayer 139c47d1ce ElementFactory: read link_type from the cached QDom, drop pugixml pass
createElement() ran a full pugixml parse of the element definition for
every instance, only to read the link_type attribute for the subclass
dispatch - on a big example project (191 instances) 49 ms of the load,
measured with temporary instrumentation. The QDom definition is already
cached and the ctor uses it anyway; reading the attribute there costs
~17 ms in total, so the net win is ~30 ms - within run-to-run noise
end-to-end, but it removes an entire redundant parser pass per element.

Behavior unchanged: an absent and an empty link_type both fell through
to SimpleElement before and still do.

(cherry picked from commit d63275971f558c8ac59f35bf4d13d7e424682342)
2026-07-28 13:58:33 +02:00
Dieter Mayer 96f6fa44ad Replace hash.keys().contains(k) with hash.contains(k) (35 call sites)
QHash/QMap::keys() allocates a list of every key on each call, then
contains() searches it linearly - an accidental O(n) plus allocation
where a direct O(1) lookup was meant. 35 occurrences across 7 files,
found while profiling project load times (context: #553/#560).

The hot one is ElementPictureFactory::getPictures(), which runs once
per element instance on project load: on the 3399 KiB example project
(191 instances, 129 cache hits) the keys() detour cost 45 ms of the
1.34 s total - measured, not estimated; the fix reproducibly shaves
~35-45 ms off that load. The remaining call sites are UI paths
(search&replace, dynamic text model, undo commands) where the waste
scales with selection/model size.

No behavior change: for QHash/QMap, keys().contains(k) and
contains(k) are equivalent by definition.

(cherry picked from commit 0a7f8f072fa68de7c01a9fc134a4bc8e16d62062)
2026-07-28 13:58:33 +02:00
Dieter Mayer 4f7340691a Restore two more Qt5-only code paths on Qt6 (parts list, print dialog)
Two further empty Qt6 guard branches found by ispyisail in
qelectrotech#553:

- Element editor parts list: the QGraphicsItem* was only stored into
  the list item on Qt5, so on Qt6 selecting a part in the list silently
  stopped selecting it on the canvas. QVariant::fromValue() works on
  both (Qt itself declares the metatype), guard removed.

- Print dialog: setEnabledOptions() is a Qt4-era API removed in Qt6;
  setOptions() is the modern spelling with the same replace-the-set
  semantics and exists on both, guard removed.

Both builds (Qt 5.15.2 and Qt 6.11.1) compile clean.

(cherry picked from commit 759d1c078e23664482850bad2e91d668b93aadcf)
2026-07-28 13:58:32 +02:00
Dieter Mayer 6d1b4cfa61 Restore crash-recovery autosave on Qt6
QETProject::writeBackup() was Qt5-only: the Qt6 branch of the guard was
an empty placeholder, so on Qt6 no backup was ever written - a silent
data-loss risk (a crash loses everything since the last manual save),
found by ispyisail in qelectrotech#553.

The Qt5-style QtConcurrent::run(function, reference-args) call did not
survive the Qt6 API change; a lambda capturing the (implicitly shared)
document copy behaves identically on both, so the version guard goes
away entirely.

Verified at runtime on the Qt6/Windows build: opening a project creates
the autosave triple (.qetautosave + .lock + .path) with valid XML
content, and a clean exit removes it again. The CLI keeps backups
disabled via setBackupEnabled(false), unchanged.

(cherry picked from commit 0f65ae8c4b2782fbfe97111bc8b369cc105ec592)
2026-07-28 13:58:31 +02:00
Dieter Mayer 9a13fef959 Install the log message handler before the application starts
qInstallMessageHandler() ran inside the startup worker thread, which is
scheduled after QETApp construction - but QETApp's constructor performs
the entire startup (collections, editor, opening projects passed on the
command line). Everything logged during that window went to the default
handler, i.e. stderr, which is invisible in a Windows GUI session: the
daily log file ended right after the machine-info block, and exactly
the interesting lines - the elements-collection timer and the project
load timer from #560 - never reached it.

Install the handler synchronously before SingleApplication instead;
the worker keeps the old-log cleanup and machine-info dump. The CLI
path returns earlier and intentionally keeps plain stderr logging.

Verified: a GUI session now logs the full timeline, e.g.
  12:37:03.623 Elements collection reload
  12:37:03.776 ... finished to be loaded in 0.151 seconds
  12:37:04.744 Project "..." (726 KiB) opened in 1.583 seconds
(cherry picked from commit 72b1a1d9ec2e3fd3787224759797978d710880bd)
2026-07-28 13:58:30 +02:00
Kellermorph b3a8ae898e PLC Manager 2026-07-27 22:16:52 +02:00
Laurent Trinques 66129fd15c Merge pull request #560 from ispyisail/feature/project-load-timing
Log project load times, split by phase
2026-07-27 17:15:02 +02:00
ispyisail 9e070f9bce Log project load times, split by phase
Requested in #553 to compare Qt5 and Qt6 builds. QET already reports how
long the elements collection takes to load (ElementsCollectionWidget::
reload); this adds the equivalent for opening a project.

The phases are reported separately rather than as a single total. Reading
the XML and building the objects is mostly independent of the Qt version,
whereas refreshing the diagrams is graphics-scene work -- a single number
would mix the two and could suggest a Qt version makes no difference when
the part that changed is simply not where the time goes. Measured on the
example projects, XML parsing is 3-7% of the total and diagram
construction 77-83%, so the distinction matters in practice.

QETProject::openFile() reports the total with the parse/build split, and
readProjectXml() reports the build phases:

  Project content built in 1.391 seconds (elements collection 0.009,
    diagrams 1.153, terminal strips 0, refresh 0.196, database 0.033)
  Project "example.qet" (3399 KiB) opened in 1.505 seconds
    (xml parsing 0.11, content 1.395)

Logged with qInfo(), matching the existing collection timer, so it lands
in the normal log without a debug build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 17:37:49 +12:00
plc-user 0608a585d6 update binary-translation-files for FR and DE 2026-07-26 18:52:08 +02:00
Laurent Trinques baf294251f Merge pull request #557 from ispyisail/fix/qmake-build-without-kf5
qmake: allow building without KDE Frameworks 5 (CONFIG+=no_kf5)
2026-07-26 18:28:50 +02:00
Laurent Trinques 80d3b17358 Update en translations files 2026-07-26 17:05:02 +02:00
Laurent Trinques 5aef8fcca5 git submodule update --remote elements 2026-07-26 16:44:39 +02:00
Laurent Trinques 72a6534a73 Update translations files 2026-07-26 16:44:02 +02:00
Laurent Trinques 0263fed6cf Merge pull request #558 from ispyisail/fix/clear-broken-stylesheet
Clear the application stylesheet when using system colors
2026-07-26 15:32:11 +02:00
Laurent Trinques 9cbd9b429c Merge pull request #559 from ispyisail/fix/554-resolve-data-paths-from-binary
Fix language (and data paths) when opening a .qet by double-click on Windows
2026-07-26 15:00:55 +02:00
Laurent Trinques c3d511cdcd Merge pull request #555 from ispyisail/feature/edz-translatable-strings
edz: make EdzArchive error strings translatable (+ German and French)
2026-07-26 14:45:43 +02:00
ispyisail 3ffa0194c7 cmake: Windows translations live in lang/, not l10n/
QET_LANG_PATH was "l10n/" for WIN32, a value that appears nowhere else in
the tree: the MSI shortcuts pass --lang-dir="[INSTALLDIR]lang/", and the
windows-build workflow copies the .qm files into files/lang/. So the
compiled-in default pointed at a directory no packaging creates, leaving
the command-line argument to do all the work.

Align it with what is actually shipped, so the binary-relative lookup
added in the previous commit can find the translations on its own.
2026-07-26 22:41:44 +12:00
ispyisail d4ec9f9c65 Resolve relative compiled-in data paths from the binary, not the CWD
commonElementsDir(), commonTitleBlockTemplatesDir() and languagesPath()
return the compile-time path verbatim when it is not marked
*_RELATIVE_TO_BINARY_PATH. On Windows those paths are relative
("./elements/", "./titleblocks/", "./lang/"), so they resolve against the
process working directory.

That only holds when QET is started from its own installation folder.
Opening a document from a file manager sets the working directory to the
document's folder, and the data is then looked for next to the user's
file. The shortcuts hide this by passing --common-elements-dir,
--common-tbt-dir and --lang-dir explicitly; anything that launches the
binary without them does not (see #554).

Add resolveConfiguredDataPath(): absolute paths are returned unchanged,
and a relative one is tried against the working directory first (so any
setup relying on the old behaviour keeps working), then next to the
executable, then in its parent -- the layout used by the Windows
packaging, where the binary sits in bin/ with the data beside it. This is
the same fallback the no-compile-option branch already performs for
issue #86, which was unreachable whenever the compile option is set.

The *_RELATIVE_TO_BINARY_PATH defines are left alone; they are only set
for macOS in qelectrotech.pro, and the CMake guard that would set them
tests a variable that is never defined.
2026-07-26 22:41:44 +12:00
ispyisail 69c595fdb8 Windows installer: pass the data directories to the .qet file association
The MSI registers both shortcuts with the arguments QET needs to find its
data ("Point directly to qelectrotech.exe with all required arguments"),
but the QElectroTech.Document\shell\open\command registry value was
written without them:

    "[INSTALLDIR]bin\qelectrotech.exe" "%1"

Launching from the Start Menu therefore works, while double-clicking a
.qet file does not: Explorer sets the working directory to the document's
folder, and the compiled-in data paths are relative, so nothing is found
there. The most visible symptom is the interface always coming up in
French, because no translation loads and the source strings are French.

Give the file association the same arguments as the shortcuts.

Reported by mr-rfh in #554, who diagnosed it and arrived at exactly this
registry value by hand.
2026-07-26 22:41:44 +12:00
ispyisail b688baf3b6 Clear the application stylesheet when using system colors
QETApp::useSystemPalette(true) installed a one-rule application stylesheet
whose only declaration was invalid CSS:

    QAbstractScrollArea#mdiarea {
        background-color -> setPalette(initial_palette_);
    }

That is not a CSS declaration but a note-to-self, committed in e6c32bc0
("Background set to use System Palette", 2014) when a hardcoded

        background-color:#D5D2D1;

was replaced with a reminder to derive the color from the palette instead.
Qt's CSS parser silently skips invalid declarations, and at the time the
same rule still carried valid background-image/-repeat/-position
properties, so the block kept working and nothing looked wrong. Those
properties were dropped later, leaving a rule with no valid declarations
at all.

The rule has therefore styled nothing for some time. It is not harmless
though: a non-empty application stylesheet wraps every widget in
QStyleSheetStyle, which overrides per-widget QWidget::setStyle(). QET
does not currently call QWidget::setStyle() anywhere, so nothing is
visibly broken today, but it blocks that API for future work — it was
found while prototyping a palette-based dark mode (see #553).

Replace it with an explicit setStyleSheet(QString()). The behavior of the
"use system colors" branch is unchanged: it already dropped whatever
style.css had loaded (by overwriting it with the inert rule), and the
qApp->setPalette(initial_palette_) call on the line above is what actually
supplies the system colors — which is what the 2014 note was asking for.

The style.css path (use == false) is untouched.

Reported by DieterMayerOSS in #553.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 22:28:32 +12:00
ispyisail 781bde88a6 qmake: allow building without KDE Frameworks 5 (CONFIG+=no_kf5)
The CMake build has supported building without KF5 since the Qt-only
replacements landed in sources/ui/nokde (BUILD_WITH_KF5=OFF). The qmake
build had no equivalent: qelectrotech.pro hard-coded

    QT += ... KWidgetsAddons KCoreAddons ...

so a machine without KF5 fails at configure time with

    Project ERROR: Unknown module(s) in QT: KWidgetsAddons KCoreAddons

This is the error reported in discussion #393, where a contributor gave up
trying to build on Windows with Qt 5.15.2 / MinGW. Deleting the two modules
by hand (the obvious workaround) then fails at link time with undefined
references to KAutoSaveFile, because the nokde sources are never compiled.

Mirror the four things CMake does when BUILD_WITH_KF5=OFF:

  - define BUILD_WITHOUT_KF5 (the sources already guard on it)
  - add sources/ui/nokde to INCLUDEPATH
  - compile the three nokde replacements (KAutoSaveFile, KColorButton,
    KColorCombo)
  - drop KWidgetsAddons/KCoreAddons from QT

The default build is unchanged: without CONFIG+=no_kf5 the KF5 modules are
still required and sources/ui/nokde is not on the include path.

Usage:

    qmake CONFIG+=no_kf5 qelectrotech.pro

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 22:12:56 +12:00
ispyisail 7cb9695962 edz: make EdzArchive error strings translatable (+ DE/FR)
The six user-facing error strings in EdzArchive::extract() were hard-coded
QStringLiteral, so they could not be translated (reported by plc-user in
PR #513).

Wrap them in tr() via Q_DECLARE_TR_FUNCTIONS — EdzArchive is not a QObject,
so this gives it its own translation context without pulling in a moc
dependency. The *.part.xml glob stays a QStringLiteral: it is a filename
pattern, not user-facing text.

Adds German and French translations for all six. German wording for
"Cannot read %1" is plc-user's ("Kann %1 nicht lesen.").

Note for review: the rest of QET uses French source strings translated to
en/de in the .ts files, whereas these strings are English in the source
(as merged in PR #513). This commit keeps them English rather than
rewriting strings already reviewed; happy to flip them to French sources
for consistency if preferred.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 21:04:25 +12:00
Laurent Trinques e9a53dbec2 git submodule update --remote elements 2026-07-26 05:44:21 +02:00
Laurent Trinques f9f03c79ea Skip signing the Qt6 MSI, and guard against triggering a signing request from a fork (no SignPath secrets there).
cf. DieterMayerOSS:fix/msi-signing-fork-guard (d3f60c88)
2026-07-24 10:20:01 +02:00
plc-user 7247257a9b add German translations 2026-07-23 19:51:55 +02:00
Laurent Trinques a8c4612fd4 Update translations files and git submodule update --remote elements 2026-07-23 14:08:11 +02:00
Laurent Trinques d02514141c Update qelectrotech.pro for https://github.com/qelectrotech/qelectrotech-source-mirror/pull/513 2026-07-23 14:02:26 +02:00
Laurent Trinques 42d2c824d2 Merge pull request #513 from ispyisail/feature/edz-import
Import EPLAN Data Portal parts (.edz) into element collections
2026-07-23 13:58:56 +02:00
Laurent Trinques 324c818007 Merge pull request #551 from Kellermorph/master
German Translation
2026-07-20 20:17:13 +02:00
Kellermorph 0a4b337115 German Translation 2026-07-20 20:15:28 +02:00
ispyisail 1b8dea3946 Add consent dialog before EPLAN (.edz) import
Shows the licensing/liability warning text agreed on in PR #513
(scorpio810) before the file picker opens. Import stays disabled
until the "I have read and accept these terms" checkbox is ticked.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 21:11:35 +12:00
Laurent Trinques adb3473a2c Improve Windows Qt5/Qt6 CI test, enable -platform windows:fontengine=freetype add new .bat for Qt6 installer and portable version 2026-07-19 21:15:13 +02:00
Laurent Trinques 3c1eb9b970 New Windows Qt5/Qt6 CI test, enable -platform windows:fontengine=freetype add new .bat for Qt6 installer and portable version 2026-07-19 19:26:25 +02:00
Laurent Trinques fd402bb9b5 Update windows-msi.yml 2026-07-19 19:00:06 +02:00
Laurent Trinques 8e736c59ba New Windows Qt5/Qt6 CI test, enable -platform windows:fontengine=freetype 2026-07-19 18:55:26 +02:00
Laurent Trinques 6941edcdf5 New Windows Qt5/Qt6 CI test 2026-07-19 16:48:42 +02:00
Laurent Trinques e1aa65f1ee Revert "Set QET version to 0.200.1 when built with Qt6"
This reverts commit 608ca984a4.
2026-07-19 13:26:34 +02:00
scorpio810 629bd7cdef Add SQLite3::SQLite3 compat alias, use modern target name
CMake's bundled FindSQLite3 module only creates the SQLite3::SQLite3
target since CMake 4.3; earlier versions (still used by most current
Linux distros) only provide SQLite::SQLite3, which CMake >= 4.3 now
flags as deprecated.

Add the missing alias ourselves right after find_package(SQLite3)
when it isn't already provided, so the project can link against the
modern SQLite3::SQLite3 name on every supported CMake version without
triggering the deprecation warning on newer ones.
2026-07-19 12:30:19 +02:00
Laurent Trinques 88e42a435b Merge pull request #519 from ispyisail/fix/asan-memory-leaks
Fix four memory leaks found by AddressSanitizer
2026-07-19 11:51:47 +02:00
Laurent Trinques 100e924aed Fix: Windows Qt6 CI test 2026-07-19 11:26:19 +02:00
Laurent Trinques 90946537c0 Add windows Qt6 CI test 2026-07-19 11:12:48 +02:00
scorpio810 608ca984a4 Set QET version to 0.200.1 when built with Qt6
Use QT_VERSION_CHECK to detect Qt6 at compile time and report
version 0.200.1 instead of 0.100.1 in that case, so Qt6 builds
are clearly distinguishable from Qt5 builds.
2026-07-19 10:56:12 +02:00
Laurent Trinques 7dbfc5fb61 Update translations files 2026-07-18 12:00:51 +02:00
Laurent Trinques a9474d82b8 Update windows-build.yml 2026-07-18 08:24:58 +02:00
Laurent Trinques 7a87754024 Merge pull request #545 from Kellermorph/terminal-name
Implement slave contact groups — label transfer, terminal assignment and UI fixes
2026-07-18 08:00:23 +02:00
Laurent Trinques 964ec23adc Merge pull request #547 from DieterMayerOSS/fix/remaining-qt6-compile-blockers
Fix the remaining Qt6 compile blockers
2026-07-17 23:29:32 +02:00
Laurent Trinques 2eb6632f6f Merge pull request #546 from DieterMayerOSS/fix/qt5-guiprivate-component
Request the GuiPrivate component only on Qt6
2026-07-17 23:07:12 +02:00
Dieter Mayer 12afadd095 Fix the remaining Qt6 compile blockers
Clears the last five spots that stop master from compiling against Qt6
(all mirrored from the proven qt6-build line):

- qgimanager.h/.cpp: in Qt6 QVector is an alias for QList, so the
  deprecated QList overloads of manage()/release() collide with the
  QVector ones (same signature). Keep them on Qt5 only.
- diagramview.cpp: one unguarded QTextStream::setCodec() call (removed
  in Qt6, which defaults to UTF-8).
- print/projectprintwindow.cpp: QApplication::desktop() was removed in
  Qt6; use QWidget::screen() there, keep the old path on Qt5.
- titleblocktemplate.cpp: QDomDocument::setContent() returns a
  ParseResult in Qt6 whose operator bool is explicit; static_cast keeps
  the bool initialization working on both.

With these, master configures and builds to a running binary with
Qt 6.11 (mingw, BUILD_WITH_KF5=OFF); every change is guarded or
dual-safe, the Qt5 build is unaffected.
2026-07-17 21:01:26 +02:00
Dieter Mayer 683eefdac1 Request the GuiPrivate component only on Qt6
Listing GuiPrivate unconditionally in QET_COMPONENTS breaks the whole
Qt5 configure: find_package(Qt5 COMPONENTS GuiPrivate) looks for a
Qt5GuiPrivateConfig.cmake that has never existed - Qt5 creates the
Qt5::GuiPrivate target implicitly together with Gui. Only Qt6 requires
(and provides) the explicit component.

Move the request into a QT_VERSION_MAJOR-guarded find_package after the
main one, both for the application and for tests/catch (whose targets
link Qt::GuiPrivate via QET_PRIVATE_LIBRARIES). Fixes the msys2/Qt5
Windows CI configure failure:
"Could not find a package configuration file provided by Qt5GuiPrivate".

Verified: Qt 6.11 configure passes and the Qt6::GuiPrivate target is
created (the private-module warning now fires from the guarded call).
The Qt5 path simply no longer requests the component, restoring the
pre-existing implicit behaviour.
2026-07-17 19:25:26 +02:00
Laurent Trinques 5785f57679 Update windows-build.yml 2026-07-17 17:16:53 +02:00
Laurent Trinques 6903b3422a Update windows-build.yml 2026-07-17 16:43:01 +02:00
Laurent Trinques 38fa44dbf0 Update windows-build.yml 2026-07-17 16:36:53 +02:00
Laurent Trinques f9a87d81f2 Update windows-build.yml 2026-07-17 15:49:39 +02:00
Laurent Trinques fddec5cbf4 Update windows-build.yml 2026-07-17 15:41:47 +02:00
Laurent Trinques 6a4554a677 Re-enable multi-threading to load collection
The name of the elements and folders of the collection are not displayed
until we hover the item with the mouse.
This due that QtConcurent::run was disabled at loading of collection in
the goal of use QtConcurrent::run with Qt6.
Run is made to run a function once.
Map is made to run a fonction for each item of a sequence (what we need
in this case).
Remove code of run and re-enable code for map.
2026-07-17 10:44:36 +02:00
joshua ae2972a143 Fix : can't open recent file 2026-07-17 10:29:13 +02:00
scorpio810 ed1b6e9ba0 terminalstripdrawer.cpp: fix two Qt6 compile errors
- Add missing <QHash> include: QHash<QUuid, QVector<QPointF>> was
  only forward-declared transitively under Qt5's heavier headers;
  Qt6's leaner <QPainter> etc. no longer pull it in.

- Replace QPolygonF{points_} with QPolygonF(points_): under Qt6,
  QPolygonF inherits QList<QPointF>'s constructors via 'using
  QList<QPointF>::QList;', including the std::initializer_list<T>
  one. Brace-init-list construction with a single argument first
  considers only initializer-list constructors before falling
  back to regular ones, which trips up overload resolution here
  even though points_ is exactly a QList<QPointF> (QVector is a
  plain alias for QList under Qt6). Plain parenthesised
  construction sidesteps that resolution phase entirely and binds
  directly to QPolygonF(const QList<QPointF>&).
2026-07-17 10:26:43 +02:00
scorpio810 33013477ad physicalterminal.cpp: fix std::min template deduction under Qt6
QVector::size() (== QList::size() under Qt6) returns qsizetype
(64-bit), while 'level' is a plain int. std::min(level,
m_real_terminal.size()-1) therefore tries to deduce a single T
from two different argument types, which fails - GCC reports it
against the unrelated std::min(initializer_list<T>) overload,
but the real issue is the type mismatch between the two-argument
candidates. Under Qt5, QVector::size() returned int, so this
compiled fine.
Force T=int explicitly via std::min<int>(...) and cast size()
down to int (physical terminal counts are always tiny), matching
the pattern already used safely elsewhere in the codebase (e.g.
qetgraphicstableitem.cpp).
2026-07-17 10:26:43 +02:00
scorpio810 287b532953 templatescollection.cpp: fix QDomDocument::ParseResult->bool conversion
Since Qt 6.5, QDomDocument::setContent() returns a ParseResult
struct with an *explicit* operator bool(), so 'bool x =
doc.setContent(...)' (copy-initialization) no longer compiles -
explicit conversions aren't considered there.

This exact issue was already fixed in titleblocktemplate.cpp by
dropping the intermediate bool and testing the call directly in
the if condition (contextual bool conversion in an if() is fine
even for an explicit operator bool), but this second occurrence
in templatescollection.cpp used the same pattern and was missed.
Applying the same fix here for consistency.
2026-07-17 10:26:35 +02:00
scorpio810 6716c267b8 templateview.h: add missing QGraphicsGridLayout/QGraphicsLayoutItem includes
templateview.h only included <QGraphicsView> but uses
QGraphicsGridLayout (tbgrid_ member) and QGraphicsLayoutItem
(indexOf/removeItem signatures) directly. Some Qt5 header
apparently pulled these in transitively; Qt6's leaner headers
don't, so the class fields/methods silently failed to resolve
and the compiler picked bogus 'int*' overloads instead.
Other files in the same directory already get these symbols
either via the <QtWidgets> umbrella header or a direct include,
so this was an isolated gap.
2026-07-17 10:26:26 +02:00
scorpio810 62dc1e7c11 Fix QRegularExpression API misuse in ElementsLocation::setXml (Qt6 path)
The Qt6 branch of the #if QT_VERSION guard swapped QRegExp for
QRegularExpression but kept calling QRegExp-only methods
(exactMatch()/cap()), which QRegularExpression doesn't have.
Use the correct QRegularExpression API instead: match() returns
a QRegularExpressionMatch, tested with hasMatch() and read with
captured(n).
2026-07-17 10:26:17 +02:00
scorpio810 fea4752a06 Fix remaining Qt6 build errors after PR #540
- qetxml.h: add missing <QUuid> include (used in propertyUuid/
  createXmlProperty signatures but never included directly).
- machine_info.cpp: fix ambiguous QString/int operator> in
  send_info_to_debug() for it1/it2/it3; just count every
  matching file found instead of the meaningless comparison.
2026-07-17 10:25:49 +02:00
Laurent Trinques 424a1c945e Try to fix compile with cmake .. -G Ninja -DQT_VERSION_MAJOR=6 -DBUILD_WITH_KF5=OFF -DPACKAGE_TESTS=OFF 2026-07-17 10:25:30 +02:00
Kellermorph b025bd205d Implement slave contact groups — label transfer, terminal assignment and UI fixes 2026-07-17 09:47:32 +02:00
Laurent Trinques 118a62adb2 Merge pull request #540 from DieterMayerOSS/cleanup/qt6-deprecation-warnings
Cleanup/qt6 deprecation warnings
2026-07-16 11:57:13 +02:00
Laurent Trinques 9a35d6c404 Merge pull request #541 from DieterMayerOSS/cleanup/qt6-remaining-warnings
Fix remaining Qt6 build warnings (narrowing, nodiscard, qHash)
2026-07-16 11:56:22 +02:00
Laurent Trinques e84180da21 Merge pull request #542 from DieterMayerOSS/fix/translation-build-race
Run lupdate only via an explicit update_translations target
2026-07-16 11:55:38 +02:00
Laurent Trinques a220a5320b Merge pull request #543 from DieterMayerOSS/doc/qt6-private-headers-dependency
Document qt6-base-private-dev as a Qt6 build dependency
2026-07-16 11:55:16 +02:00
Laurent Trinques 34bf63e237 Merge pull request #544 from DieterMayerOSS/fix/duplicate-folio-element-uuids
Renew element uuids when duplicating a folio
2026-07-16 11:54:48 +02:00
Laurent Trinques 8884e79133 Delete QElectroTech.qch files 2026-07-15 06:19:52 +02:00
Dieter Mayer 20a7047b27 Renew element uuids when duplicating a folio
The project panel's "copy and paste" action duplicates a folio through
an XML round-trip (toXml/fromXml), and Element::fromXml adopts the uuid
stored in the XML - so every element on the duplicated folio kept the
uuid of its source element. On-diagram copy/paste already handles this
(PasteDiagramCommand::redo() calls newUuid()), but the folio-duplicate
path bypasses that command.

Since element.uuid is the PRIMARY KEY of the project database, the
duplicated elements failed to insert ("UNIQUE constraint failed:
element.uuid" spam in the log) and silently disappeared from
nomenclature/summary tables, even though they are valid on the folio.

Renew the uuid of every element on the freshly duplicated folio, exactly
as the paste command does. In-memory links established during fromXml
are pointer-based and unaffected; the new uuids are written on save.
2026-07-14 19:48:31 +02:00
Dieter Mayer b7a8759cc6 Document qt6-base-private-dev as a Qt6 build dependency
On Debian/Ubuntu qt6-base-dev ships the Qt6::GuiPrivate CMake config but
the actual private headers live in the separate qt6-base-private-dev
package, so find_package succeeds and CMake only fails later at generate
time with a non-obvious "non-existent path" error. QET genuinely needs
the private QtGui API (QPdfEngine) for clickable hyperlinks in the PDF
export, so document the package instead of degrading the feature.
Observed on Ubuntu 26.04 LTS (Qt 6.10.2); Debian sid and the Qt online
installer ship the headers together.
2026-07-14 19:31:07 +02:00
Dieter Mayer 46f479240e Run lupdate only via an explicit update_translations target
The default build ran both lupdate (qt5_create_translation, which
rewrites the tracked .ts files in the source tree) and lrelease
(qt5_add_translation, which reads the same .ts files). Under high
parallelism lrelease could read a .ts while lupdate was rewriting it,
failing the build with "Premature end of document"; every build also
modified tracked files as a side effect, and each .qm was generated
twice (once into the build dir, once into lang/).

Keep only lrelease in the default build and move lupdate behind an
explicit developer target (cmake --build . --target update_translations).
The target scans sources/ instead of the whole source tree, which also
stops lupdate from parsing unrelated third-party .js files.
2026-07-14 19:29:19 +02:00
Dieter Mayer a0f66d22cc Fix remaining Qt6 build warnings (narrowing, nodiscard, qHash)
Clears the 9 non-deprecation warnings from the Qt6 build:
- qHash(QColor): hash rgba() (unambiguous QRgb) instead of name(), and
  use the size_t seed signature on Qt6 (guarded for Qt5). Fixes the
  ambiguous-overload warning in terminalstripmodel.h.
- Two qsizetype->int narrowings in brace-init: explicit static_cast<int>
  (elementscene.cpp, terminalstrip.cpp).
- main.cpp: keep the QtConcurrent::run QFuture in a [[maybe_unused]]
  variable (nodiscard).
- qetapp.cpp: guard the stylesheet load on QFile::open() succeeding
  (nodiscard) instead of ignoring the result.
2026-07-14 19:27:37 +02:00
Dieter Mayer d5027c4ef9 Replace deprecated QVariant::canConvert(int)
QVariant::canConvert(int) is deprecated in Qt6. Use the non-deprecated
canConvert<T>() template (canConvert<QString>() / canConvert<int>()),
which is available on Qt5 too. Clears the last 2 -Wdeprecated-
declarations warnings.
2026-07-14 19:27:18 +02:00
Dieter Mayer e099fca5ad Guard Qt6-only deprecation replacements for Qt5 compatibility
Three deprecated APIs have replacements that only exist in newer Qt6:
- QLocale::nativeCountryName() -> nativeTerritoryName() (Qt 6.2)
- QDomDocument::setContent() overload -> ParseResult (Qt 6.5)
- qt_ntfs_permission_lookup -> QNtfsPermissionCheckGuard RAII (Qt 6.6)
Each is wrapped in QT_VERSION_CHECK so Qt5 keeps the old path. Clears
4 -Wdeprecated-declarations warnings.
2026-07-14 19:27:18 +02:00
Dieter Mayer f57c921b78 Fix trivial Qt6 deprecations (QString::count, QColor::setNamedColor)
QString::count() (no-arg) is deprecated -> size(); QColor::setNamedColor()
is deprecated -> the QColor(QString) constructor. Both replacements are
non-deprecated on Qt5 too. Clears 2 -Wdeprecated-declarations warnings.
2026-07-14 19:27:17 +02:00
Dieter Mayer c3392bf025 Wrap deprecated QSqlDatabase::exec() in QSqlQuery
QSqlDatabase::exec(const QString&) is deprecated in Qt6. Route the
PRAGMA/CREATE TABLE statements through QSqlQuery(db).exec() instead.
Clears 11 -Wdeprecated-declarations warnings; same statements, same
database connection, no behavioural change.
2026-07-14 19:27:17 +02:00
Dieter Mayer 7e08cd1285 Replace deprecated QVariant::Type with QMetaType in UserProperties
QVariant::type() and the QVariant::Type enum are deprecated in Qt6.
Switch on userType() (non-deprecated, returns the QMetaType id and
works on Qt5 too) with QMetaType enum cases. Clears 6 -Wdeprecated-
declarations warnings; the numeric type ids are unchanged.
2026-07-14 19:27:17 +02:00
Dieter Mayer 1265e51ebe Replace deprecated qAsConst with std::as_const
qAsConst was deprecated in Qt 6.6; std::as_const (C++17, already the
project standard) is the drop-in replacement. Clears 46 -Wdeprecated-
declarations warnings across 18 files. No behavioural change.
2026-07-14 19:27:16 +02:00
Laurent Trinques 5c65bf6486 Merge pull request #539 from DieterMayerOSS/add-qet-de-qm
Add compiled qet_de.qm for the completed German translation
2026-07-12 11:04:40 +02:00
Dieter Mayer 64a0c7abf5 Add compiled qet_de.qm for the completed German translation
Regenerate lang/qet_de.qm from lang/qet_de.ts with lrelease so the
committed binary matches the strings completed in #538
(2621 finished, 0 unfinished for de_DE).
2026-07-12 09:29:35 +02:00
Laurent Trinques 0ef47ce9b9 fix typo on publiccode.yml 2026-07-12 08:27:52 +02:00
Laurent Trinques 96e283a25b fix typo on publiccode.yml 2026-07-12 08:05:58 +02:00
Laurent Trinques f16ebeb2a6 Add publiccode.yml 2026-07-12 07:53:24 +02:00
Laurent Trinques b97a87c889 Merge pull request #537 from DieterMayerOSS/fix-collection-names-base-language
Fall back to the base language for element and folder names
2026-07-11 23:15:12 +02:00
Laurent Trinques 81d0045514 Merge pull request #538 from DieterMayerOSS/complete-german-terminal-translations
Complete German translation of unfinished terminal-label strings
2026-07-11 22:28:19 +02:00
Dieter Mayer b6124b065c Complete German translation of unfinished terminal-label strings
qet_de.ts had 20 unfinished entries in the PartTerminal and
TerminalEditor contexts: 12 empty ones (shown in French at runtime,
since French is the source language of the tr() literals) and 8 that
already carried German text but were still flagged unfinished.

Translate the 12 empty strings, matching the terminology already
established in these contexts (borne/terminal -> "Anschluss",
label -> "Beschriftung", cadre -> "Rahmen", police -> "Schriftart"),
and mark the 8 existing drafts as finished. lrelease now reports
2621 finished and 0 unfinished translations for de_DE.
2026-07-11 21:52:51 +02:00
Dieter Mayer c211b68139 Fall back to the base language for element and folder names
NamesList::name() looked up the display name using the full locale from
langFromSetting() (e.g. "de_DE") and jumped straight to English if it was
absent. Element and folder names in the collection are keyed by 2-letter
codes (<name lang="de">), so a "de_DE" UI showed the whole collection in
English/French even though German names exist.

Try the base language ("de") before the English fallback, mirroring what
setLanguage() already does for the UI translations.
2026-07-11 21:47:04 +02:00
Laurent Trinques 0f129ac504 git submodule update --remote elements 2026-07-11 06:47:58 +02:00
Laurent Trinques e23dc24283 Update translations files 2026-07-11 06:45:03 +02:00
Laurent Trinques 6d7b38c7a1 Merge pull request #536 from Kellermorph/terminal-name
Feature: Add terminal name label display in element editor
2026-07-10 15:11:49 +02:00
Kellermorph b543adcb46 Restore comments 2026-07-10 15:04:04 +02:00
Kellermorph f62ce7e4ca Update terminal name 2026-07-10 14:36:47 +02:00
Kellermorph c884d32dbe Feature: Add terminal name label display in element editor 2026-07-09 14:02:27 +02:00
Laurent Trinques 99ad9aa459 Merge pull request #535 from Kellermorph/translation
Add German translation
2026-07-08 13:33:58 +02:00
Kellermorph c1c72c5a62 Add complete German translation for QElectroTech 2026-07-08 07:09:23 +02:00
Laurent Trinques eeabea10f0 Update translations files 2026-07-08 06:57:07 +02:00
Laurent Trinques e034cfe9cf Merge pull request #534 from Kellermorph/safety-check
feat: add backup dialog when opening an existing project
2026-07-08 06:34:13 +02:00
Kellermorph 2db6c3968a feat: add backup dialog when opening an existing project 2026-07-07 21:42:45 +02:00
Laurent Trinques 14253930ae Merge pull request #509 from Kellermorph/master
Add Custom Guides
2026-07-07 16:34:56 +02:00
Laurent Trinques ec0a9f45d3 Update windows-msi.yml 2026-07-06 18:43:55 +02:00
Laurent Trinques 1121ccea26 Fix ci: update GitHub Actions to latest major versions 2026-07-06 17:57:37 +02:00
Laurent Trinques 94cc9cfef5 Fix ci: update GitHub Actions to latest major versions 2026-07-06 17:54:46 +02:00
Laurent Trinques 346aad75fd Fix ci: update GitHub Actions to latest major versions 2026-07-06 16:49:51 +02:00
Laurent Trinques 2b8cdd3d67 ci: update GitHub Actions to latest major versions 2026-07-06 16:46:54 +02:00
Laurent Trinques be008fb5f6 ci: update GitHub Actions to latest major versions 2026-07-06 16:43:16 +02:00
Laurent Trinques 82cd4d359a macOS fix: add explicit QGraphicsLayoutItem include in helpercell.h
MOC on macOS does not resolve QGraphicsLayoutItem through the bulk
QtWidgets include, causing an 'Undefined interface' error at build time.
Adding an explicit include resolves this. Linux builds are unaffected. Thanks hairykiwi 8ef4e04
2026-07-06 14:45:58 +02:00
Laurent Trinques 6dca56f823 git submodule update SingleApplication to v3.5.6 2026-07-06 13:08:34 +02:00
Laurent Trinques 545006bed2 Merge pull request #533 from geri1701/no-kf5-qt-only
Fix Qt-only build when BUILD_WITH_KF5=OFF
2026-07-05 18:00:35 +02:00
Gerhard Schwanzer dcec0bf7ff Skip actively locked autosave files
Check the QLockFile in staleFiles() before returning a no-KF5 recovery candidate, matching the KAutoSaveFile contract that actively owned autosave files are not stale.

Extend the no-KF5 Catch test so a child process keeps the autosave lock alive while allStaleFiles() runs, then verify recovery after the child is killed.

Assisted-by: pi coding agent / Mika (OpenAI GPT-5.5)
2026-07-05 17:39:38 +02:00
Gerhard Schwanzer 7a97e873d9 Test Qt-only autosave recovery fallback
Add a no-KF5 Catch regression test that leaves a KAutoSaveFile-compatible backup behind from a child process, then verifies stale-file discovery, stale-lock recovery, reading, and cleanup.

Assisted-by: pi coding agent / Mika (OpenAI GPT-5.5)
2026-07-05 15:52:25 +02:00
Gerhard Schwanzer e6124e941a Add Qt-only autosave recovery fallback
Provide a small KAutoSaveFile-compatible implementation for the no-KF5 build path and use it to keep the existing crash-recovery code active when BUILD_WITH_KF5=OFF.

The normal KF5 build still uses the KDE KAutoSaveFile implementation.

Assisted-by: pi coding agent / Mika (OpenAI GPT-5.5)
2026-07-05 13:23:49 +02:00
Gerhard Schwanzer d0cea474a6 Fix Qt-only build without KF5
The BUILD_WITH_KF5 option was checked with DEFINED, so passing -DBUILD_WITH_KF5=OFF still entered the KF5 setup path.

Skip the KF5 setup when disabled and provide small Qt-only replacements for the KDE color widgets used by .ui files in that build mode.

Assisted-by: pi coding agent / Mika (OpenAI GPT-5.5)
2026-07-05 11:10:44 +02:00
Laurent Trinques 3c8ba1b1ca Update ChangeLog with narrative summary of post-0.100 changes
Add a hand-written "Version 0.101 (Unreleased)" section covering the
headless CLI export mode, PDF hyperlink cross-references, diagram
duplication, the Windows MSI versioning fix, the macOS file-open
regression fix, and expanded Korean translation support.

Add git-cliff config for changelog generation

Add cliff.toml, configured to group commits by keyword (fix/feat/
refactor/etc. in FR+EN) since the project history doesn't follow
Conventional Commits. Handles QET's tag format (X.Y / X.Y.Z, no v
prefix), excludes the floating "nightly" tag, dedupes repeated commit
messages per release, and truncates commit bodies to their summary
line to keep the generated changelog valid Markdown.

Usage:
export GITHUB_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxx
git-cliff --config cliff.toml <last_tag>..HEAD --prepend ChangeLog.md.
2026-07-04 11:09:49 +02:00
Laurent Trinques e19f523227 fix(windows-msi): inject rev into MSI Version Build field
Version was static (X.Y.Z.0) across nightlies, so MajorUpgrade never triggered the automatic uninstall; the Windows Installer only compares Major.Minor.Build, ignoring the 4th field.
2026-06-30 18:49:43 +02:00
Laurent Trinques 5dac26d9c7 Merge pull request #524 from ispyisail/fix/collection-unicode-path
Fix #391: collection panel blank when path contains accented chars or is too long (Windows)
2026-06-29 23:11:17 +02:00
Laurent Trinques e7873b40d5 git submodule update --remote pugixml/ 2026-06-27 22:49:23 +02:00
Laurent Trinques cd6c66c3c6 git submodule update --remote elements 2026-06-27 22:01:36 +02:00
Laurent Trinques ef04497ca6 Translate debug message to English 2026-06-27 19:41:28 +02:00
Laurent Trinques 58000283e6 Merge pull request #522 from ispyisail/fix/issue-283-table-center-alignment
fix(#283): restore center alignment when loading table config
2026-06-27 13:58:19 +02:00
Laurent Trinques 73b96dd1b8 Merge pull request #530 from vinicius-ap-santos/improve-ptbr-translation
Improve Brazilian Portuguese translation (pt_BR)
2026-06-24 06:10:57 +02:00
Vinícius Santos 682c6a0ccb Improve Brazilian Portuguese translation (pt_BR)
Fix 9 critical meaning errors, remove anglicisms, standardize
terminology and fill 81 previously untranslated strings.

Details:
- Nombre de phase: Nome da fase -> Numero de fases
- Ajouter un tableau: Ajustes da tabela -> Adicionar uma tabela
- Parcourir: Personalizado -> Navegar
- Desactive: Invalido -> Desativado
- Longueur: Largura -> Comprimento
- Variables de cartouche: Variaveis de armazenamento -> Variaveis do carimbo
- Aller a la correspondance: Ir para a aba -> Ir para a ocorrencia
- Creer de nouveaux folios: Cria um novo projeto -> Criar novas paginas
- Ponter/Deponter les bornes: verb form and meaning corrected
- Removed anglicism resetar -> Restaurar/Redefinir
- conducteur standardized to condutor (was mixed with fio)
- nomenclature standardized to nomenclatura (was lista de nomes)
- WiringListExport: entire feature translated
- TerminalNumberingDialog: entire dialog translated
- QETElementEditor: Mirror, Flip, Fine-Rotation translated
- TerminalEditor: NO/NC contacts -> NA/NF (Brazilian standard)
- Include compiled .qm file
2026-06-23 18:25:22 -03:00
Laurent Trinques d3cf8f2635 Remove github action test-vs2026.yml 2026-06-23 13:44:05 +02:00
plc-user 6aa14a7536 Merge pull request #518 from ispyisail/fix/terminal-data-leak
looks plausible, compiles fine

thank you @ispyisail
2026-06-23 09:39:46 +02:00
plc-user 6ce688a709 Merge pull request #520 from ispyisail/fix/element-editor-first-click-481
fixed:
view jumps, but graphic primitives stay at position
all parts can be moved as before
compile-warning is gone

thank you @ispyisail
2026-06-23 09:26:02 +02:00
Shane Ringrose 28357b0f55 fix(editor): correct initializer list order to silence -Wreorder warning
m_first_move was initialized before _linestyle in the constructor
initializer list, but _linestyle is declared first in the class. Reorder
to match declaration order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 07:04:34 +12:00
plc-user e21f49d2cf Merge pull request #528 from ispyisail/fix/project-props-dialog-modality
Merged as discussed in #527 
Thanks @ispyisail
2026-06-22 20:28:27 +02:00
Shane Ringrose 2a115e4381 fix(editor): suppress spurious first-click element moves (#481)
When an item type is selected for the first time the properties dock
expands, causing the QGraphicsView viewport to shrink. Qt recalculates
scene coordinates and fires one or more synthetic mouseMoveEvents before
the user has actually moved the mouse.

The original code used a single-shot m_first_move flag in
CustomElementGraphicPart, which absorbed exactly one spurious event.
PartText and PartDynamicTextField had no protection at all.

Fix: compare screen-coordinate displacement against
QApplication::startDragDistance() (~4 px). Screen coordinates are
stable across viewport resizes, so the check correctly rejects
synthetic dock-expansion events while allowing genuine drags.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 05:38:11 +12:00
Shane Ringrose bab32b3764 Fix #527 follow-up: use ApplicationModal for app config dialog (configureQET)
Same root cause as ProjectPropertiesDialog: Qt::WindowModal only blocks the
direct parent window, leaving the rest of the MDI area live.  If new_project
or close_project fires while the app settings dialog is open, any raw pointers
derived from the project list become stale.  Switch to ApplicationModal to
block all windows for the duration of the dialog.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 17:48:19 +12:00
Shane Ringrose 5cc2e165bf Fix #527: use ApplicationModal for Project Properties to prevent SIGSEGV
ProjectPropertiesDialog::exec() was using Qt::WindowModal, which only
blocks the parent ProjectView window.  The MDI workspace and its other
subwindows remained interactive, so actions like new_project or
close_project could fire while the dialog's config pages still held raw
QETProject* pointers — leading to a SIGSEGV when Qt's event loop later
dispatched a signal through one of those stale pointers.

Detected by the 8-hour GUI fuzzer: action sequence add_diagram_page →
flood_wires ×18 → new_project while Project Properties was open
produced exit code -11 (SIGSEGV) on the first of 12,717 actions.

Switch to Qt::ApplicationModal so no window can receive input while the
dialog is open.  Project Properties is a short-lived dialog; blocking
the whole application for its duration matches user expectation and
removes the lifetime hazard without requiring QPointer surgery across
four config-page classes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 17:30:20 +12:00
scorpio810 1c764babd1 macOS: buffer QFileOpenEvent during cold launch before QETApp exists
Follow-up to the eventFilter fix: testing showed double-click works
when QET is already running, but a cold launch (app not running yet)
still opens an empty window. Finder/Launch Services can deliver the
QFileOpenEvent to the QApplication's native event loop before main()
reaches the point where QETApp is constructed and its real eventFilter
is installed -- there's a window between 'SingleApplication app(...)'
and 'QETApp qetapp;' during which the event can arrive and be lost.

Add a minimal EarlyFileOpenCatcher installed on app immediately after
it's constructed (before anything else can run an event loop). It only
buffers the file path. Once QETApp exists, main() swaps it out for the
real QETApp::eventFilter and drains anything that was buffered via
qetapp.openFiles(), so no cold-launch QFileOpenEvent is silently
dropped.

Confirmed by manual testing:
- 'open app --args file' on .qet/.elmt/.titleblock: OK (already worked)
- double-click while app already running: OK (already worked)
- double-click cold launch: previously opened an empty window, this
  buffers and replays the event so it now opens the right editor.
2026-06-21 13:04:07 +02:00
scorpio810 8d76354647 macOS: fix QFileOpenEvent routing for .qet/.elmt/.titleblock double-click
Root cause (see issue #218 discussion):
- QETApp::eventFiltrer (Q_OS_DARWIN) was correct in intent but never
  actually installed as an event filter anywhere, and its name didn't
  match the QObject::eventFilter virtual signature, so even if it had
  been installed it would never have been invoked as an override.
  Confirmed dead code.
- main.cpp instead routed Finder's QFileOpenEvent through
  MacOSXOpenEvent -> SingleApplication::sendMessage(), which is the
  secondary-to-primary IPC channel. Called from within the primary
  instance itself, sendMessage() fails silently and the file path is
  dropped, which is exactly what double-click does (Finder doesn't spawn
  a secondary process, the running instance receives the FileOpen event
  directly).
- openFiles() takes a QETArguments, not a QStringList. The dead code's
  openFiles(QStringList() << filename) only worked via an untested
  implicit QStringList -> QList<QString> -> QETArguments conversion
  chain.

Fix:
- Rename eventFiltrer -> eventFilter (qetapp.h/.cpp) so it's a proper
  override of QObject::eventFilter, and make it public so main.cpp can
  install it on QApplication.
- Build the QETArguments explicitly instead of relying on implicit
  conversion.
- main.cpp: drop MacOSXOpenEvent entirely (include + instantiation),
  install qetapp as the Q_OS_MACOS event filter on app right after
  QETApp qetapp; is constructed and before app.exec(). No race window:
  the event loop hasn't started, so no QFileOpenEvent can be delivered
  before the filter is installed.

QETArguments::handleFileArgument() already sorts files by extension
(.elmt, titleblock, else project) and openFiles() already fans out to
openProjectFiles()/openElementFiles()/openTitleBlockTemplateFiles()
accordingly, so this single fix covers .qet, .elmt and .titleblock
double-click/drag-to-dock on macOS, not just .qet.

SingleApplication's sendMessage/receivedMessage flow (used for CLI args
on all platforms) is untouched; this only touches the Q_OS_MACOS block,
so there is no behavior change on Windows/Linux.

Follow-up (not included here): the macOS Info.plist (misc/Info.plist)
has an empty CFBundleShortVersionString, which may affect macOS's
willingness to retain file associations across app updates.
2026-06-21 12:10:13 +02:00
Shane Ringrose 31edf30c61 Fix #391: use wide-char path for pugixml on Windows to handle Unicode paths
pugi::xml_document::load_file(const char*) calls fopen/fopen_s on Windows,
which uses the ANSI codepage — not UTF-8. This silently fails when the
collection path contains accented characters (é, ü, ñ, …) or is longer
than the narrow-API MAX_PATH limit, leaving the collection panel with no
element names or illustrations.

Switch both call sites to toStdWString().c_str() which invokes the
load_file(const wchar_t*) overload. On Windows pugixml calls _wfopen,
the wide Unicode API that handles all valid Unicode paths. On Linux/macOS
the same overload converts wchar_t to UTF-8 internally and calls fopen,
so behaviour is unchanged on those platforms.

Affected files:
  sources/ElementsCollection/fileelementcollectionitem.cpp  (qet_directory load)
  sources/ElementsCollection/elementslocation.cpp           (element .elmt load, both branches)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 22:08:11 +12:00
Shane Ringrose 734391eabf Merge master: add cli_export, pdf_links; EDZ: 10-slot grid, group headers, ToS notice
- Resolve cmake/qet_compilation_vars.cmake conflict: keep both upstream's
  cli_export.cpp/h and pdf_links.cpp/h and the EDZ source additions.

- 10-position grid alignment: pin_y values are multiples of 10 so terminals
  snap cleanly to QET's default grid.  group_gap raised to 10 (one full slot).

- Named connector groups get a header label (group name) placed in the gap
  above the first pin, so the electrician sees block names (XDI, XPOW, …)
  without reading individual terminal designations.

- Device-tag dynamic_text now uses 9pt LABEL_FONT and y = min_y - 9 so it
  clears the element body and is legible at normal zoom.

- Add EPLAN Data Portal Terms of Use disclaimer to sources/import/edz/README.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 21:00:49 +12:00
Shane Ringrose f55ba568f6 fix(#283): restore center alignment when loading table config
saveConfig() serialises Qt::AlignHCenter as the string "AlignHCenter"
via QMetaEnum::valueToKey(), but loadConfig() matched against
Qt::AlignCenter (0x0084 = AlignHCenter|AlignVCenter) instead of
Qt::AlignHCenter (0x0004).  The two values differ, so the switch fell
through to the default (Right) every time center was saved and reloaded.

Add Qt::AlignHCenter as the primary case and keep Qt::AlignCenter as a
fallthrough for any config files that were hand-edited by users following
the workaround documented in issue #283.

Verified with a standalone Qt test: QMetaEnum::keyToValue("AlignHCenter")
returns 4 (AlignHCenter), which now correctly resolves to combobox index 1
(Center) instead of 2 (Right).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 20:41:25 +12:00
plc-user 1410e70d13 Merge pull request #521 from ispyisail/fix/iscustom-collection-prefix
Sounds plausible! 
Thank you @ispyisail
2026-06-21 09:24:42 +02:00
Shane Ringrose 7669a95694 fix(collection): isCustomCollection() false-positive on company path
The default user-collection path ends in "elements" and the default
company-collection path ends in "elements-company".  Both
FileElementCollectionItem::isCustomCollection() and
ElementsLocation::isCustomCollection() used startsWith(customDir),
so "…/elements-company/…" matched "…/elements" and returned true.

This caused ElementsCollectionModel::addLocation() to insert a
newly-saved user-collection element as a child of the company-
collection branch in the tree, making it appear in the wrong panel.

Fix: require the path to equal the directory root exactly, or to
start with the directory root followed by '/'.

  path == dir || path.startsWith(dir + QLatin1Char('/'))

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 12:10:22 +12:00
Shane Ringrose 8268df8a80 edz: name terminals terminalNr.designation (e.g. "XDI.2")
Previous logic resolved duplicate connectionDesignation values by
appending sanitised description text or numeric suffixes, which was
fragile and produced names an electrician could not easily map back to
the physical wiring.

New scheme:
  • terminalNr present  → "XDI.2", "XRO1.3", "XPOW.1" …
  • terminalNr absent   → designation as-is ("L1/U1", "UDC+", "PE") —
    these are busbar / power connections and are already globally unique
  • collision (malformed data) → numeric suffix as safety net

The description (connectiondescription) remains as the human-readable
label beside the terminal symbol, exactly as suggested by plc-user.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 08:58:30 +12:00
Shane Ringrose 03e6e76b3f edz: fix missing-field-initializer warning in EdzElementBuilder
EdzPin gained a third field (group) but the fallback initializer at
line 70 still listed only two fields, triggering -Wmissing-field-initializers.
Added the explicit QString() for group.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 03:02:31 +12:00
Shane Ringrose 2aeeb74e72 edz: group terminals by physical connector (terminalNr), drop separator lines
EPLAN 2022-style part.xml files (e.g. IFM AL1122) use a numeric
functiondefgroup attribute and do not carry the text functiondefinition
block name that the previous grouping logic relied on.  Those parts
fell back to grouping by pin designation, producing symbols with all
pin "1"s stacked together, then all "2"s, etc. — the bug reported in
PR #513.

Fix: read terminalNr first (the physical M12/connector socket identifier,
e.g. "X01", "X31") as the primary group key; fall back to functiondefinition
text for older EPLAN formats that omit terminalNr.  Pins within each
connector group are still sorted by designation using natural sort.

Also remove the dashed inter-group separator lines; the existing 5 px
gap between groups provides sufficient visual separation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 02:57:14 +12:00
Laurent Trinques ee53f20303 Merge pull request #517 from ispyisail/fix/snap-xcb-cursor-missing
snap: stage libxcb-cursor0 to fix xcb plugin crash on Ubuntu 24.04 (issue #373)
2026-06-20 16:44:59 +02:00
Shane Ringrose 756cfd98c0 Fix terminal grouping: use functiondefinition block, not pin designation
The previous grouping compared `connectionDesignation` values (e.g. "1",
"2", "3") to detect group boundaries.  Those values are pin position
numbers within a terminal block, not functional group identifiers, so
devices like the ABB ACS880 produced 11+ tiny single-pin "groups" instead
of the ~8 functional blocks (AC-IN, Motor-OUT, DC-Bus, Brake-Resistor,
Analog-I/O, Digital-I/O, ...) the reviewer identified.

Fix:
- Add `group` field to EdzPin, populated from the `functiondefinition`
  attribute on <functiontemplate> (newer EPLAN) or its parent <function>
  element (older EPLAN).
- Sort pins by group first (preserving XML appearance order per group),
  then by designation within each group using natural sort.
- Use `groupKey()` — group when present, designation as fallback — for
  the group-break detection that drives separator lines and Y-gaps.

Parts without any functiondefinition data retain the previous
designation-based behaviour unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 01:54:23 +12:00
Shane Ringrose 8a837a11d4 Fix four memory leaks found by AddressSanitizer
- StyleEditor: QGridLayout(this) pre-empted the widget's layout slot,
  causing setLayout(main_layout) to silently fail and orphan main_layout.
  Fix: use QGridLayout() without a parent so setLayout() succeeds.

- ExportDialog: ~ExportDialog() was empty, leaving ExportDiagramLine
  heap objects in diagram_lines_ unfreed. Fix: qDeleteAll(diagram_lines_).

- GenericPanel::getItemForDiagram: when called without the bool* created
  arg, it created a parentless QTreeWidgetItem that callers immediately
  discarded. Fix: return nullptr when created==nullptr and item not found
  (all callers already guard with if (item)).

- ElementScene: m_paste_area (created in initPasteArea) was temporarily
  added/removed from the scene during XML loading but never freed in the
  destructor. Fix: delete it if not currently in the scene.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 01:45:07 +12:00
Shane Ringrose df5f622418 Fix TerminalData memory leak in Terminal destructor
Terminal stores its TerminalData* as member d but never deletes it.
Every Element creation (placing on diagram, loading icon for the
element browser, drag previews) leaks one TerminalData per terminal.
ASan confirmed 112 leaked objects (9856 bytes) in a short session
across four call sites all rooted in Element::parseTerminal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 00:38:07 +12:00
plc-user 71ff7f925d Merge pull request #515 from ispyisail/fix/valgrind-uninit-machineinfo-firstshow
Thank you @ispyisail
2026-06-20 13:19:39 +02:00
Shane Ringrose 2fdc07b81b snap: stage libxcb-cursor0 to fix xcb plugin load failure on Ubuntu 24.04
Qt 5.15.x added libxcb-cursor0 as a hard runtime dependency of the xcb
platform plugin (libqxcb.so).  The kf5-5-110-qt-5-15-11-core22 content
snap does not bundle this library, so when the snap runs on an Ubuntu 24.04
host the dlopen() of the plugin fails with:

  qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in ""
  even though it was found.

Staging libxcb-cursor0 from the Ubuntu 22.04 archive satisfies the
dependency without changing the snap base, Qt version, or any other
dependency.  No ABI mismatch: the plugin and the staged library are
both built against the core22 (22.04) ABI.

Fixes issue #373.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 22:51:11 +12:00
Shane Ringrose 27534a379b Group terminals by designation with visual separator lines
Pins are already sorted by designation (see EdzPart::parse natural sort).
This change makes same-designation groups visually obvious in the element
symbol by:

  - Inserting a 5 px gap between consecutive terminals whose designation
    differs, so each group reads as a distinct block.
  - Drawing a thin dashed horizontal line through each gap, mirroring
    the grouped-I/O style shown in typical manufacturer datasheets.

The body rectangle and bounding box grow automatically to accommodate the
extra gaps, so no fixed sizes change.  Unique terminal names (added in the
previous commit) are unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 22:44:06 +12:00
plc-user 32bb247d68 Merge pull request #514 from ispyisail/fix/thread-safe-datadir-configdir
Thank you @ispyisail
2026-06-20 12:39:12 +02:00
Shane Ringrose 207c0c9544 Fix copyright years and deduplicate terminal names
Two issues raised in PR review:

1. Copyright year: new files carried "2006" (the project's founding year).
   Updated to "2006-2026" to reflect the actual authorship period.

2. Duplicate terminal names: EPLAN parts can have multiple connection
   templates sharing the same connectionDesignation (e.g. a drive where
   both wire entries of terminal "1" are labelled "1").  QET requires
   unique terminal names for wiring and terminal-diagram generation.

   Resolution order in EdzElementBuilder::build():
   - Unique designation → used as-is.
   - Duplicated designation with distinct sanitised description →
     "designation_description" (e.g. "1_L_P").
   - Otherwise → numeric suffix: "1", "1_2", "1_3", …

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 22:39:11 +12:00
Shane Ringrose c586a2d3a3 Fix three uninitialised-value bugs found by Valgrind
1. machine_info.h: zero-initialise Screen struct members
   Max_width, Max_height, count, width[] and height[] were bare
   int32_t with no initialiser. The comparisons in
   init_get_Screen_info() read them before any write, producing
   undefined behaviour flagged by Valgrind as 'Conditional jump
   or move depends on uninitialised value(s)'.

2. main.cpp: pre-initialise MachineInfo on the main thread
   MachineInfo::instance() was first called inside QtConcurrent::run(),
   causing its constructor (which calls qApp->screens()) to run on a
   background thread. QScreen methods are not thread-safe in Qt5.
   Calling instance() once on the main thread before the worker
   launches guarantees the singleton is fully built first; subsequent
   calls from the worker just return the cached pointer.

3. qetdiagrameditor.h: move m_first_show before the QActionGroup members
   C++ initialises members in declaration order. m_first_show was
   declared after the QActionGroup members (line 256 vs 168). During
   construction of m_row_column_actions_group(this), Qt dispatches a
   QObject parent-change event that reaches QETDiagramEditor::event(),
   which reads m_first_show before it has been initialised.
   Moving the declaration to the top of the first private: block
   ensures it is initialised before any member that can trigger events.

All three found via Valgrind --tool=memcheck on Ubuntu 22.04 / Qt 5.15.3.
Relates-to: PR #514 (same QtConcurrent thread-safety pattern).
2026-06-20 22:31:06 +12:00
Shane Ringrose f301196f61 Rename static locals to match original variable names per review
Reviewer requested configdir/datadir instead of cached for consistency
with the surrounding code style.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 22:26:32 +12:00
Shane Ringrose f2d297b0d8 Fix thread-unsafe QStandardPaths calls in dataDir/configDir
QStandardPaths::writableLocation() is not thread-safe in Qt5.
ElementsCollectionModel::reload() launches:

  QtConcurrent::map(m_items_list_to_setUp, setUpData)

Each worker calls FileElementCollectionItem::setUpData()
→ collectionPath() → isCollectionRoot()
→ QETApp::userMacrosDir() → QETApp::dataDir()
→ QStandardPaths::writableLocation()  ← SIGSEGV (null deref)

The crash was confirmed by Valgrind (address 0x0, inside
libQt5Core's writableLocation internals).

Fix: replace the bare QStandardPaths calls in dataDir() and
configDir() with a C++11 static-local lambda.  The compiler
guarantees the lambda body runs exactly once across all threads
(magic statics, ISO C++11 §6.7).  After the first (main-thread)
call the result is returned lock-free.

Relates-to: #492 (same QtConcurrent lifetime pattern fixed in
QETProject::writeBackup by PR #512).
2026-06-20 20:09:48 +12:00
Shane Ringrose d56bca66da Harden EPLAN import: format detection, trim SDK, docs (M4)
- EdzArchive checks the archive magic up front: gives a clear message for
  zip-format .edz (not yet supported) and unrecognised data, instead of an
  opaque 7z decode error.
- Trim the vendored LZMA SDK headers to the decode closure actually used
  (removes 21 unused encoder/multithread/Xz/Aes headers; 18 .c + 18 .h remain).
- Add sources/import/edz/README.md documenting the feature, the data mapping
  and the bundled SDK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 08:14:53 +12:00
Shane Ringrose aa6a0b941a Add English translations for the EPLAN import strings
The new import action and dialogs used French source strings (QET convention)
but had no English translation, so they showed French in the English UI. Add
the five strings to qet_en.ts (menu action, dialog title, file filter, error
box title and message).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 08:03:00 +12:00
Shane Ringrose e4b4ba875b Bundle LZMA SDK for .edz extraction; drop 7z CLI dependency (M3)
Replaces the QProcess->7z shim with the public-domain LZMA SDK (23.01) 7-Zip
reader, vendored under sources/import/edz/lzma/ (decode-only subset). New
edzsevenzip.cpp wraps SzArEx_Open/SzArEx_Extract and writes entries via Qt, so
EdzArchive no longer needs an external 7-Zip at runtime. Enables the C language
in CMake for the vendored sources.

Verified: the bundled decoder extracts all three ifm sample .edz and the
generated elements still match the Python oracle exactly (byte-correct decode),
with no 7z on the path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 07:49:14 +12:00
Shane Ringrose f4ff6c81f9 Add EdzImporter + collection-panel import action (M2)
Wires EPLAN .edz import into the elements panel. EdzImporter orchestrates
EdzArchive -> EdzPart -> EdzElementBuilder and writes the generated .elmt into a
destination collection folder (named by order number). A right-click
"Importer une piece EPLAN (.edz)..." action on a writable collection directory
opens a file picker, runs the importer into that folder's fileSystemPath() and
reloads the panel; errors surface via QetMessageBox. Modeled on newElement().

EdzImporter verified headless against the Python oracle for KG6000/MFH200/
R1D200; the panel wiring is built/tested via the WSL Qt5+KF5 build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 07:34:52 +12:00
Shane Ringrose 7cc5790f7c Add EdzPart + EdzElementBuilder: .edz part -> .elmt (M1)
Port of the edz2qet.py prototype's mapping to C++. EdzPart parses an EPLAN
part.xml into a portable model (identity/metadata, localized names, connection
list); EdzElementBuilder turns that into a QET element (generic symbol: body
rectangle + one west-facing terminal per pin, per-pin labels, localized <name>s,
and elementInformations for the BOM).

Pins are natural-sorted by designation so they stack 1,2,3,4 regardless of the
order EPLAN lists them (MFH200 lists 1,3,4,2). Output verified structurally
identical (uuids aside) to the Python oracle for three ifm samples — KG6000
(4-pin), MFH200 (4-pin, reordered) and R1D200 (5-pin, incl. Dutch name) — and
the generated element loads in the QET editor with correct UTF-8.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 07:11:09 +12:00
Shane Ringrose 82884d1cf7 Add EdzArchive: extract EPLAN .edz packages (M0)
First piece of native EPLAN Data Portal (.edz) import. A .edz is a 7-Zip
archive; QET previously had no archive handling. EdzArchive unpacks one to a
temporary directory and locates the contained part.xml.

The extraction backend is isolated behind extractWithSevenZipCli() so it can be
replaced with a bundled decompressor later without touching callers; this M0
step shells out to a 7-Zip CLI via QProcess. Verified on three ifm sample
parts (KG6000, MFH200, R1D200 — all 7z), plus the corrupt/missing-file paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 07:05:42 +12:00
plc-user a8e2a7acff concat QString with '%' 2026-06-19 17:06:58 +02:00
plc-user 53096fa3be Merge pull request #512 from ispyisail/fix-492-backup-uaf
Although I haven’t encountered the problems described myself (nor do I need to!), the changes and additions look plausible and compile without errors or warnings, so I’m approving this PR!

But as a remark:
I'm not particularly familiar with the Qt functions used here. But when I see that there is a version-specific implementation for Qt5 and only a debug-message mentioned for Qt6, it makes me wonder:
Is it implemented differently there, or is it not even needed there — which I don't think is the case...
If possible, "we" should also include something for Qt6 and later versions in another PR.
Do you know what’s needed for Qt6, @ispyisail ?
2026-06-19 12:35:47 +02:00
ispyisail bf4d3353ae Fix #492: wait for async backup before destroying QETProject
writeBackup() fires QtConcurrent::run(QET::writeToFile, ..., &m_backup_file)
fire-and-forget: the QFuture was discarded and nothing kept m_backup_file
alive until the worker finished. If the QETProject was destroyed first, the
worker wrote through the freed member -> use-after-free crash in
QET::writeToFile (intermittent; ~1/6 on short-lived CLI runs).

Store the QFuture and waitForFinished() in ~QETProject (and before
setFilePath() re-points the managed backup file). Also skip launching a new
backup while one is still running, so two threads never write m_backup_file
at once.

The Qt6 path is still a TODO stub and the QtConcurrent block is KF5-only, so
this affects only the Qt5/KF5 build that actually has the backup code.
2026-06-19 08:34:02 +12:00
Kellermorph 2e4fa9ae02 Drop translation files from this PR to handle them separately 2026-06-18 21:48:12 +02:00
Kellermorph cd0ae76141 Clean up lang/qet_de diff by isolating new translation strings 2026-06-18 21:45:31 +02:00
plc-user bd37f12edc upgrade 'osifont.ttf' from upstream 2026-06-18 15:20:25 +02:00
plc-user b873b05245 concat QString with '%' 2026-06-18 14:51:56 +02:00
plc-user eb02e1dce0 fix warning: 'if does not guard...' 2026-06-18 14:33:10 +02:00
plc-user ac1e8b3502 fix warning: name 'label_11' already used... 2026-06-18 14:30:35 +02:00
plc-user 215962873b fix warning: unused variable "pdfExport" (should be fixed properly by original author by evaluating function-result) 2026-06-18 14:26:32 +02:00
Kellermorph 0555cd9045 Address review feedback: double spinboxes, tabs, and fix export bug 2026-06-18 13:37:24 +02:00
plc-user 02cc4f043b Merge pull request #511 from ispyisail/fix-highlight-unused-reset
With this change we can close #159 as well!
Thank you @ispyisail
2026-06-18 12:42:00 +02:00
ispyisail 8791d2d202 Highlight reset: only clear the red unused-highlight
Per review (plc-user): scope the reset to items currently painted with the
red Dense4Pattern instead of clearing every item's background. This avoids
clobbering other backgrounds (e.g. the amber "show this dir" highlight)
and skips needless item updates on large collections.
2026-06-18 22:29:42 +12:00
ispyisail 4625964bb1 Fix #159: reset unused-element highlight when elements become used again
ElementsCollectionModel::highlightUnusedElement() only ever painted the
currently-unused elements red; it never cleared the background of items
that were no longer unused. So when an element was re-added to a project
and saved, its red 'unused' highlight persisted until the model was
rebuilt from scratch.

Reset every item's background before re-applying the highlight to the
current unused set.
2026-06-18 21:09:49 +12:00
Kellermorph 7a80ce40b7 Sync with upstream master and resolve conflict in qet_de.qm 2026-06-17 22:07:41 +02:00
plc-user 342ac3626d fix also German binary-translation 2026-06-17 14:11:11 +02:00
Laurent Trinques 19a75186b7 Merge pull request #506 from ispyisail/fix/snap-pin-sources
snap: pin qet-tb-generator source to tag v1.31
2026-06-17 13:24:10 +02:00
plc-user 9122f5d687 fix German translation 2026-06-16 21:13:14 +02:00
plc-user 482fd32dc2 element-editor: no warning on save, when element without terminals is frontview (and fix indention) 2026-06-16 20:40:08 +02:00
Kellermorph 4e0c075575 Fix grid and guide startup behavior and update german translation 2026-06-16 17:41:03 +02:00
plc-user ae42198882 Merge pull request #508 from ispyisail/fix-tbgen-drop-legacy-appdata
Drop dead ~/Application Data/qet fallback for qet_tb_generator (Windows)
As discussed in #199 ...
2026-06-16 12:37:07 +02:00
Shane Ringrose a666d2d0ae qet_tb_generator: drop dead ~/Application Data/qet fallback (Windows)
The Windows search list for the qet_tb_generator plugin included
`~/Application Data/qet/qet_tb_generator.exe` as a fallback. That legacy
junction path is the same inaccessible location the standard-directories
change moved QET away from, and it never matched a pip install anyway:
`pip install qet_tb_generator` puts the executable in the Python Scripts
directory (`...\PythonXX\Scripts` on PATH, or
`%APPDATA%\Python\PythonXX\Scripts` for --user installs), not in
`QETApp::dataDir()`.

Pip installs are already found via QStandardPaths::findExecutable (PATH),
and manual binary drops via dataDir()/binary and the working directory.
The legacy entry only matched old manual drops into the inaccessible
folder, so remove it.

Refs qelectrotech-source-mirror#199

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 22:11:22 +12:00
Shane Ringrose 15e7d39243 snap: pin qet-tb-generator source to tag v1.31
Without a source-tag or source-commit, snapcraft pulls the latest
HEAD of qet_tb_generator-plugin on every build.  This makes builds
non-reproducible and risks breakage whenever the upstream repo changes.

Pin to the only published release tag (v1.31, commit d6ee3cf) so
the snap always builds against a known-good version of the plugin.

Closes #202

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 10:15:09 +12:00
Laurent Trinques 5574b4d3e9 CMake: guard Linux-only install rules with UNIX AND NOT APPLE
The .desktop, MIME package, and appdata install rules are
freedesktop.org conventions and only apply on Linux. Wrapping
them in if(UNIX AND NOT APPLE) prevents a configure failure on
macOS and Windows where QET_APPDATA_PATH and QET_MIME_PACKAGE_PATH
are not defined.

Also replace the hardcoded share/mime/packages path with
${QET_MIME_PACKAGE_PATH} for consistency with
paths_compilation_installation.cmake.

No change to Linux build behaviour.
2026-06-14 13:58:37 +02:00
Laurent Trinques 918db632ed Update windows-msi.yml 2026-06-13 10:56:07 +02:00
Laurent Trinques f465b33e2b Update windows-msi.yml 2026-06-13 10:45:41 +02:00
Laurent Trinques 492528136e Update windows-msi.yml 2026-06-13 10:40:37 +02:00
Laurent Trinques 564a0e64a4 Update windows-msi.yml 2026-06-13 10:31:58 +02:00
Laurent Trinques 7f5a42a055 Update windows-msi.yml 2026-06-13 10:14:05 +02:00
Laurent Trinques 14d4aa772b Update windows-msi.yml 2026-06-13 09:59:17 +02:00
Laurent Trinques 81419bd27d Merge pull request #498 from ispyisail/fix-lang-path-fallback
Find translations when lang/ is beside bin/, not inside it (fixes #86)
2026-06-13 04:49:14 +02:00
Laurent Trinques ebefc269af Update Changelog file 2026-06-12 19:43:57 +02:00
Laurent Trinques c6bfd46981 Merge pull request #501 from ispyisail/fix-parttext-position-158
PartText: keep text position stable across save/reopen on font-size change (#158)
2026-06-12 16:49:26 +02:00
Laurent Trinques 998c5e8a0d Update translations files 2026-06-12 13:28:08 +02:00
Shane Ringrose ae382f6b12 parttext: re-anchor on font change so text position is stable across save/reopen (#158) 2026-06-12 23:05:15 +12:00
Laurent Trinques d4061d17fa Merge pull request #497 from ispyisail/fix-readonly-after-saveas
Clear read-only state when a project is saved to a writable file
2026-06-12 11:48:05 +02:00
Laurent Trinques fb8c86ec12 Merge pull request #495 from ispyisail/feature-folio-template-variables
Folio properties: auto-add a title block's custom variables
2026-06-12 11:47:33 +02:00
Laurent Trinques d62479e6e4 Merge pull request #496 from ispyisail/fix-system-regional-locale
Fix regional system locale loading the wrong translation (pt_BR/nl_BE/nl_NL)
2026-06-12 11:46:56 +02:00
Laurent Trinques 1a4bb91285 Merge pull request #494 from ispyisail/fix-elementdialog-filename-label
Element editor Save As: label the field as a file name, not 'element name'
2026-06-12 11:46:12 +02:00
Laurent Trinques aa96d5e7df Merge pull request #493 from ispyisail/cli-set-titleblock
CLI: add --set-titleblock, and fix headless backup crash
2026-06-12 11:42:04 +02:00
Shane Ringrose e63c303078 Find translations when lang/ is beside the bin/ folder, not inside it (#86)
QETApp::languagesPath() defaulted to applicationDirPath() + "/lang/".
The Windows installer puts the executable in a bin/ subfolder while the
lang/ folder sits next to it (../lang), so that default points at a
non-existent bin/lang/ — qetTranslator.load() fails and setLanguage()
silently falls back to the French source language. This is the root
cause behind the long-standing 'language won't change / resets to
French' reports, and why launching via 'Lancer QET.bat' (which passes
--lang-dir=lang/) works around it.

When the folder next to the binary doesn't exist, fall back to the
sibling ../lang folder if present. Behaviour is unchanged for builds
that already ship lang/ next to the binary, and for the QET_LANG_PATH
and --lang-dir paths.

Fixes #86.
2026-06-12 09:09:39 +12:00
Shane Ringrose a8e408ad39 Clear read-only state when a project is saved to a writable file (#217)
Saving a read-only project to a writable location (e.g. Save As to /tmp)
left it marked read-only, so it stayed uneditable until closed and
reopened. Two issues in QETProject::write():

- The guard refused to write whenever QFileInfo(path).isWritable() was
  false. For a Save As to a *new* file that test is always false (the file
  doesn't exist yet), so it could wrongly block saving a read-only project
  elsewhere. Now it checks the directory's writability for a new file.
- After a successful write the read-only flag was never cleared. Since the
  file was just written, it is writable, so clear it (setReadOnly(false)
  emits readOnlyChanged, re-enabling editing live).

Fixes #217.
2026-06-12 06:09:48 +12:00
Shane Ringrose 0eb3e1e436 Fix regional system locale loading the wrong translation (#421)
langFromSetting() truncated the system locale to two letters
(QLocale::system().name().left(2)), so a user on the default 'Système'
language whose locale is regional got the base-language translation
instead of their regional one. QET ships qet_pt_BR, qet_nl_BE and
qet_nl_NL, so e.g. a Brazilian user saw European Portuguese (and
untranslated strings fell back to the French source).

Keep the full locale name and, in setLanguage(), try the exact
translation, then the base language, then English (French stays the
native source). Brazilian/Belgian/Dutch users on 'system' now get their
regional translation; everyone else is unaffected.

Refs #421.
2026-06-12 05:58:04 +12:00
Shane Ringrose ba6320bff8 Folio properties: auto-add a title block's custom variables (#271)
When a title block template uses custom variables (e.g. %{department},
%{owner}), the user previously had to declare each one by hand in the
folio properties 'Custom' tab before a value could be entered. Now the
template's undefined custom variables are added automatically, so the
user only fills in the values.

- listOfVariables() now extracts %{name} placeholders with a regex
  (deduplicated) instead of a crude '%' strip that returned '{name}'.
- The folio properties widget merges the template's custom variables into
  the Custom tab both on open (setProperties) and when the template is
  changed, preserving any values already entered and skipping the
  standard fields (title, author, date, ...) which have their own inputs.

Fixes #271 (variable auto-population; the revision-history request in the
thread is a separate feature).
2026-06-12 05:45:08 +12:00
Shane Ringrose 86b5d7ac95 Element editor Save As: label the field as a file name, not element name (#469)
The Save As 'location' dialog used a QFileNameEdit (accepts only
[0-9a-z_-.]) but labelled it 'New element name', which is confusing —
QET also has a separate, translatable display name shown in the
collection. Users reasonably tried to type a display name (spaces,
capitals) and it was rejected.

Rename the placeholder to 'Element file name' and add a tooltip noting
the accepted characters and that the display name is edited separately
in the element properties. Updates the English translation; other
languages fall back until re-translated.

Fixes #469.
2026-06-12 05:34:20 +12:00
Shane Ringrose 19e99aab02 CLI: disable async crash-recovery backup in headless mode (fixes segfault)
QETProject schedules an asynchronous crash-recovery backup on construction
(writeBackup() -> QtConcurrent::run(QET::writeToFile, ..., &m_backup_file)).
In one-shot CLI mode the QETProject is destroyed as soon as the command
returns, while that background write still references its m_backup_file
member — an intermittent use-after-free segfault during teardown (~1 in 6
runs; observed on --resave and --set-titleblock).

A crash-recovery backup is meaningless for a short-lived headless command,
so add QETProject::setBackupEnabled(false), called from the CLI entry in
main(). writeBackup() then early-returns, so no background write is ever
launched. Fixes the crash for all CLI commands. See #492.
2026-06-12 05:25:44 +12:00
Shane Ringrose 44f0abbb56 CLI: add --set-titleblock to stamp title-block fields headlessly
The first write-to-project CLI command, aimed at CI / revision workflows:
stamp title-block metadata onto every folio (and the project default),
then save. Each argument is key=value:

  qelectrotech --set-titleblock in.qet out.qet revision=B date=today

Standard keys map to the documented title-block fields (title, author,
date, plant, location, revision, version, filename); date=today uses the
current date and an explicit date forces UseDateValue mode; any other key
is stored as a custom title-block field. Assignments are parsed up front
so a malformed one fails before writing.

Addresses the 'saving' side of the CLI-for-scripts request (#162).
2026-06-12 05:25:44 +12:00
Laurent Trinques e7787daa2c Update qet_zh.qm 2026-06-11 16:26:31 +02:00
Laurent Trinques c7fd3416f6 Merge pull request #491 from zi-mozhuang/master
Update qet_zh.ts
2026-06-11 16:25:42 +02:00
子墨庄 b782a1612a Update qet_zh.ts
Checked the format and conducted a pre-test using the linguist.
2026-06-11 22:16:49 +08:00
Laurent Trinques 2fdbc3c243 Merge pull request #490 from ispyisail/cli-pdf-links
CLI: clickable cross-reference hyperlinks in PDF export
2026-06-11 14:39:53 +02:00
Shane Ringrose a219c3f587 CLI: clickable cross-reference hyperlinks in PDF export
Wire the shared PdfLinks helper into the headless --export-pdf path so
CLI-exported PDFs get the same internal cross-reference / folio-report
navigation as the GUI print export.

For each page, after rendering, the scene-to-page geometry is rebuilt
from the QPdfWriter (96 dpi, zero margins, page sized to the diagram so
the scale is ~1 with no centering) — deliberately NOT reusing the
QPrinter-based mapping — and passed to PdfLinks::injectCrossRefLinks().
After the painter closes, PdfLinks::convertUriToGoTo() rewrites the URI
annotations into native GoTo/FitR actions.

Builds on the helper extracted in the previous commit; no change to the
other CLI tools.
2026-06-11 23:48:45 +12:00
Shane Ringrose 6b3b55b0e1 PDF links: extract pdf_links.{cpp,h} shared helper
Move the PDF cross-reference hyperlink logic out of ProjectPrintWindow
into a standalone translation unit so it can be reused (the CLI PDF
export will call it next):

- injectCrossRefLinks(): emits the URI link annotations for a diagram's
  cross-references and folio reports. The scene-to-page mapping is passed
  in as a PageGeometry (transform + devToPdf + source-rect lookup) so each
  caller supplies its own correct geometry, rather than the helper assuming
  a QPrinter.
- convertUriToGoTo(): the PDF post-processor, moved verbatim.

ProjectPrintWindow stays a pure caller: it builds its PageGeometry from the
printer page layout exactly as before and calls the helper. No behavioural
change to GUI PDF export; no class-structure changes.

Per review guidance on #483.
2026-06-11 23:48:14 +12:00
Laurent Trinques 36d0121038 Merge pull request #489 from ispyisail/cli-tools
CLI: add verification & data-export tools (info, BOM, nets, links, check-elements, resave)
2026-06-11 13:42:59 +02:00
Laurent Trinques 8235ecdbc9 Merge pull request #488 from Kellermorph/master
Issues 482
2026-06-11 13:32:30 +02:00
Shane Ringrose b6e4cd4786 CLI: add --export-nets, --export-links and --resave
Three more read-only command-line tools for verifying connectivity and
cross-reference intelligence (useful for import / migration pipelines):

  qelectrotech --export-nets    <project.qet> <output.json>
  qelectrotech --export-links   <project.qet> <output.csv>
  qelectrotech --resave         <project.qet> <output.qet>

- --export-nets walks Conductor::relatedPotentialConductors() to group
  every electrically-connected terminal into a net (potential), following
  folio reports and terminal blocks across all folios. Output is JSON:
  per net, the wire number and the list of {element, terminal, folio}.
  This is the connectivity ground truth.

- --export-links reports each linkable element (master/slave/report/
  terminal), its link type and the elements it links to, flagging
  masters/slaves with no link as UNRESOLVED. Verifies coil<->contact
  cross-references. Verified on examples/industrial.qet: 436 linkable
  (76 master, 41 slave, ...), 37 unresolved.

- --resave loads the project and writes its XML back out, so an external
  diff can reveal markup QET silently normalises on load
  (tolerated-but-invalid XML). Round-trip verified: the re-saved project
  reloads with identical diagram/element/conductor counts.
2026-06-11 23:23:13 +12:00
Shane Ringrose fb35027624 CLI: add --info, --export-bom and --check-elements verification tools
Extends the headless command-line interface with three read-only tools
aimed at validating projects and element libraries (useful for batch
import / migration pipelines):

  qelectrotech --info           <project.qet> [output.json]
  qelectrotech --export-bom     <project.qet> <output.csv>
  qelectrotech --check-elements <element.elmt | directory>

- --info dumps a structural summary as JSON straight from QET's loaded
  model: per-diagram element / conductor counts, page size, and the
  number of unconnected ("free") terminals, plus project totals. Because
  it uses the real loader it reports what the editor actually sees.

- --export-bom writes a bill of materials (one row per element) as CSV,
  querying the project's own element_nomenclature_view (the same source
  as the GUI BOM export). updateDB() is called first so the database is
  populated in a headless run.

- --check-elements validates one .elmt file, or every .elmt under a
  directory (recursively), against the element schema: XML well-formed,
  root <definition type="element">, a usable bounding box, and terminal
  count. Reports OK / WARN / FAIL per file and a summary; exit code is
  non-zero if any file fails. Verified against the full bundled
  collection (8483 elements): 0 false failures, agreeing with QET's own
  loader (e.g. a negative-height element it tolerates is a WARN, not a
  FAIL).

run() is restructured to handle the differing argument arity (info takes
an optional output, check-elements takes a path rather than a project).
2026-06-11 23:23:13 +12:00
Kellermorph 08a441d1f6 Issues 482 2026-06-11 13:22:01 +02:00
Laurent Trinques e9840728b4 Merge pull request #486 from qelectrotech/revert-484-master
Revert "Update-UI-Chinese-translation"
2026-06-11 12:28:55 +02:00
Laurent Trinques ffbcd12d9b Revert "Update-UI-Chinese-translation" 2026-06-11 12:22:45 +02:00
Laurent Trinques 0a124f6695 Merge pull request #484 from zi-mozhuang/master
Update-UI-Chinese-translation
2026-06-11 12:19:03 +02:00
Laurent Trinques 3f6f99b50f Merge pull request #485 from ispyisail/fix-cli-pdf-grid
CLI export: don't draw the editor grid in PDF/PNG/SVG output
2026-06-11 12:15:42 +02:00
Shane Ringrose 42b64a7f0a CLI export: disable the editor grid in rendered output
renderDiagram() had a no-op stub: was_drawing_grid was set to false and
Q_UNUSED'd, so the editor grid still leaked into exported PDF/PNG/SVG.
Toggle Diagram::setDisplayGrid(false) around the render and restore the
previous state afterwards. Fixes all three export formats (they share
renderDiagram).

Reported by scorpio810 on #483.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 22:00:07 +12:00
Laurent Trinques 361719ca74 Fix my error in .pro 2026-06-11 11:39:25 +02:00
Laurent Trinques 181e2b555d Fix missing closing parenthesis in function call in .pro 2026-06-11 11:10:08 +02:00
Laurent Trinques 420512595d Update qelectrotech.pro
add sourc'es files missing in .pro Add headless command-line export (PDF / PNG / SVG / cable-list / wire-list)
#483
2026-06-11 11:07:25 +02:00
zi-mozhuang 9b4bed361d Update qet_zh.ts 2026-06-11 16:59:31 +08:00
Laurent Trinques 339bc8700b Merge pull request #483 from ispyisail/cli-export-master
Add headless command-line export (PDF / PNG / SVG / cable-list / wire-list)
2026-06-11 10:53:23 +02:00
Shane Ringrose 87d5ae5580 CLI export: add wiring list and wire-number list (CSV)
Extends the headless command-line export with two CSV outputs:

  qelectrotech --export-cables <project.qet> <output.csv>   wiring list
  qelectrotech --export-wires  <project.qet> <output.csv>   wire numbers

- --export-cables reuses WiringListExport (one row per conductor).
- --export-wires reuses ConductorNumExport::wiresNum() (distinct wire numbers).

WiringListExport::toCsv() mixed CSV generation with the file dialog and
writing.  Extracted the generation into a new const method toCsvString()
that returns the CSV; toCsv() now calls it and writes the result.  This
makes the wiring list usable headlessly with no behavioural change to the
GUI export.

Addresses part of the CLI export requests (#162, #309): @pkess specifically
asked to "export all connections as a list".
2026-06-11 12:39:43 +12:00
Shane Ringrose 1070179617 Add headless command-line export (PDF/PNG/SVG)
Implements the long-requested batch/headless export
(bugtracker #171, GitHub #309): render a project's diagrams to files
without opening the GUI.

  qelectrotech --export-pdf <project.qet> <output.pdf>   one multi-page PDF
  qelectrotech --export-png <project.qet> <output_dir>   one PNG per diagram
  qelectrotech --export-svg <project.qet> <output_dir>   one SVG per diagram

main.cpp detects an export request before SingleApplication is created (so the
arguments are not forwarded to a running instance), spins up a plain
QApplication for rendering, and exits with the export's status code.

Rendering reuses Diagram::render() over
BorderTitleBlock::borderAndTitleBlockRect(), the same geometry the GUI
print/export path uses, so output matches the editor. Image files are named
NN_Title.<ext>.

New files: sources/cli_export.{h,cpp}, registered in
cmake/qet_compilation_vars.cmake.
2026-06-11 11:10:09 +12:00
Laurent Trinques 6c4711a8d0 Update qet_compilation_vars.cmake 2026-06-07 15:11:06 +02:00
Laurent Trinques 8a8a338a2e Update CMakeLists.txt 2026-06-07 15:06:23 +02:00
Laurent Trinques 407cc7a4c2 Update CMakeLists.txt 2026-06-07 14:59:36 +02:00
Laurent Trinques 5cb8930732 Update fetch_pugixml.cmake 2026-06-07 14:57:29 +02:00
Laurent Trinques a24acfac24 Update fetch_pugixml.cmake 2026-06-07 14:57:05 +02:00
Laurent Trinques 8b0b1d10d4 git submodule update --remote elements 2026-06-05 11:28:36 +02:00
Laurent Trinques 57dfa28674 fix erroneous comments in drawContact for SW terminal names
The comments describing the terminal_names layout were inherited from a
previous version and no longer matched the actual assignment order:

    terminal_names << nc_name << no_name << common_name;
    i.e. [0]=NC, [1]=NO, [2]=Common

Update all affected comments to reflect the current storage order.
2026-06-05 11:21:08 +02:00
Laurent Trinques 3848c7821a Merge pull request #479 from ChuckNr11/master
fix possible crashes in crossrefitem
2026-06-05 11:01:44 +02:00
Laurent Trinques 1572c23d51 Fix FTBFS https://github.com/qelectrotech/qelectrotech-source-mirror/
pull/477
2026-06-05 10:46:00 +02:00
achim e234f063f8 fix possible crashes in crossrefitem
fix access to QList with potentially out-of-bounds index
2026-06-04 14:49:43 +02:00
Laurent Trinques be21604ad0 Merge pull request #477 from Kellermorph/update-german-translation
Fix: Dynamic element text shifting/jumping when duplicating diagrams
2026-06-01 21:10:06 +02:00
Kellermorph e1ccc1e568 Fix: Dynamic element text shifting/jumping when duplicating diagrams 2026-06-01 11:25:25 +02:00
Laurent Trinques e202b5bc2b Merge pull request #475 from Kellermorph/update-german-translation
Update German translations for duplicate diagram feature
2026-05-31 16:05:45 +02:00
Laurent Trinques 2b7e62f901 [PATCH] print: fix black screen on macOS arm64 after PDF export
On macOS arm64 (Apple Silicon, Sequoia), exporting a PDF via
QPrintPreviewWidget leaves a black screen with only the mouse cursor
visible.  Cmd+Tab restores the display; the exported PDF itself is
correct and clickable cross-reference links work fine.

Root cause
----------
requestPaint() is a slot connected to QPrintPreviewWidget::paintRequested.
Inside this slot the code was calling painter.end() manually, then
pdfConvertUriToGoTo().  On macOS arm64 the Qt5 paint cycle backed by
Metal/CALayer is asynchronous: closing the QPainter from *within* the
paintRequested slot interrupts the compositor before it has flushed the
backing store.  The window goes black and never repaints because the
close() that follows immediately destroys it.

On x86_64 / older macOS (raster/CoreGraphics backend) the paint cycle is
synchronous, so the same code happened to work.

Fix
---
1. Remove the manual painter.end() and pdfConvertUriToGoTo() call from
   requestPaint().  The QPainter is stack-allocated; it destructs normally
   when the slot returns, which is the correct moment to flush the PDF.

2. In print(), capture the output file name before m_preview->print(),
   then defer both pdfConvertUriToGoTo() and this->close() to the next
   event-loop iteration via QTimer::singleShot(0, ...).  This gives the
   Metal compositor one full event-loop turn to finish compositing the
   backing store before the window is torn down.

The fix is a no-op on all other platforms: QTimer::singleShot(0) posts
an event that fires in the very next iteration, so there is no perceptible
delay.

Tested
------
- macOS Sequoia 15.x, Apple M-series, Qt 5.15.x (arm64): black screen gone
- macOS 10.15 x86_64 VM, Qt 5.15.x: no regression
- Linux/Debian Qt 5.15.x: no regression
- PDF cross-reference links and GoTo/FitR destinations: unaffected

Fixes: black screen after PDF export on macOS arm64
2026-05-31 13:01:52 +02:00
Kellermorph 23e8258ae1 Update German translations for duplicate diagram feature 2026-05-31 10:48:09 +02:00
Laurent Trinques 457d265f0a Update translation files 2026-05-30 21:27:19 +02:00
Laurent Trinques cd76b6a1d6 This adds native, clickable hyperlinks to PDF exports: cross-references jump
directly to the related component on its folio, framing the target element.

When a project is exported to PDF, every cross-reference becomes an internal
link. Four kinds are covered:

- **Master → contact**: the contact list on a coil/relay (`CrossRefItem`)
- **Folio report → report**: report element labels (`DynamicElementTextItem`)
- **Slave → master**: the `(folio-position)` reference shown on a slave
  (both standalone `DynamicElementTextItem` and grouped `ElementTextItemGroup`)

Clicking a link navigates **inside** the open document (no new viewer
instance) and zooms to frame the target element.

1. **Injection** (`printDiagram`, only when the paint engine is a `QPdfEngine`):
   link rectangles are added with `QPdfEngine::drawHyperlink()`. The scene→page
   mapping is rebuilt to match exactly what `QGraphicsScene::render()` does
   (top-left anchored, `KeepAspectRatio`, **no centering**), and rectangles are
   passed in device pixels — `pageMatrix()` already applies the 72/resolution
   scale and Y-flip internally.

2. Each link URL encodes the target page and the target element's rectangle, in
   PDF points on its own page: `#page=N&fitr=L_B_R_T`.

3. **Post-processing** (`pdfConvertUriToGoTo`, run after the painter is closed):
   the `/S /URI` annotations are rewritten to native `/S /GoTo` actions with a
   `/D [pageObj 0 R /FitR L B R T]` destination, and the xref table is rebuilt.
   Pages are enumerated from the `/Pages /Kids` tree (reliable), not by scanning
   for `/Type /Page` in raw bytes.

- `sources/print/projectprintwindow.{cpp,h}` — injection + post-processing
- `sources/qetgraphicsitem/crossrefitem.{cpp,h}` — `hoveredContactsMap()` accessor; store text rect for hit area
- `sources/qetgraphicsitem/dynamicelementtextitem.h` — `slaveXrefItem()` / `masterElement()` accessors
- `sources/qetgraphicsitem/elementtextitemgroup.h` — `slaveXrefItem()` accessor
- `qelectrotech.pro`, `cmake/qet_compilation_vars.cmake` — enable Qt gui-private headers (`<private/qpdf_p.h>`)

- **Fit-to-page mode only.** Links are not injected in tiled mode (multiple
  pages per folio), which would require a per-tile transform.
- Uses Qt private API (`QPdfEngine::drawHyperlink`), stable since Qt 4 but not
  part of the public API; the build links against `gui-private`.
- Page-tree enumeration assumes the flat `/Kids` array Qt produces (no nested
  page trees).
- The frame zoom is controlled by two constants in `destRectPdf` (`pad`,
  `minSide`) and can be tuned.
- Tested on Qt5; the `/Kids` parsing and `pageMatrix` behaviour are identical on
  Qt6.
2026-05-30 18:48:28 +02:00
Laurent Trinques b522a94556 Merge pull request #473 from Kellermorph/makro-fix
Feat: Add ability to duplicate diagrams/folios with all metadata and …
2026-05-30 05:27:35 +02:00
Kellermorph 399bc0e897 Feat: Add ability to duplicate diagrams/folios with all metadata and elements 2026-05-29 19:14:36 +02:00
Laurent Trinques de9eeed542 Merge pull request #472 from Kellermorph/makro-fix
Feature: Allow excluding specific elements from BOM (Nomenclature)
2026-05-28 14:17:46 +02:00
Kellermorph c071e92c58 Feature: Allow excluding specific elements from BOM (Nomenclature) 2026-05-28 12:23:54 +02:00
Laurent Trinques d22e4abf96 Update translations files 2026-05-28 10:00:09 +02:00
Laurent Trinques 38b91e8083 Merge pull request #471 from Kellermorph/makro-fix
Potential Isolation option for terminals
2026-05-28 09:45:04 +02:00
Kellermorph 19704cf5ca Potential Isolation option for terminals 2026-05-27 21:20:50 +02:00
Laurent Trinques 471d1f2538 Merge pull request #470 from Kellermorph/makro-fix
Fix: Wiring list filter and dynamic text timing
2026-05-27 06:19:06 +02:00
Kellermorph dc52105868 Fix: Wiring list filter and dynamic text timing 2026-05-26 18:31:37 +02:00
Laurent Trinques 1b8dc5f410 texteditor: set font size spinbox minimum to 4pt to prevent SIGSEGV 2026-05-25 13:14:57 +02:00
Laurent Trinques 26d5d019cc texteditor: fix SIGSEGV caused by font size spinbox reaching 0 2026-05-25 13:04:37 +02:00
Laurent Trinques 6dd7c2d926 crossrefitem: fix SIGSEGV crash + improve double-click navigation + fix PDF/SVG/print rendering
Windows Build / build-windows (push) Has been cancelled
Test Windows VS2026 migration / Build on windows-2025-vs2026 (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
Fix a use-after-free crash (SIGSEGV in QRegion::begin, Qt5Gui+0x49af60)
confirmed by analysis of 19 coredumps. The crash was triggered when the
scene viewport clip region was freed during zoom/resize events while
QPicture::play() replayed drawPolyline commands through the scene painter.
Qt's raster engine then dereferenced a stale QRegionData pointer.

Root cause: CrossRefItem used three nested QPicture objects (m_drawing,
m_hdr_no_ctc, m_hdr_nc_ctc). The nested drawPicture() calls amplified
the use-after-free risk on any repaint event.

Fix: remove all QPicture from CrossRefItem entirely.
- updateLabel() now uses a QImage-backed dummy painter to compute
  m_bounding_rect, m_shape_path and m_hovered_contacts_map geometry.
  A bool m_update_map flag prevents the map from being overwritten
  during paint().
- paint() calls drawAsCross()/drawAsContacts() directly on the scene
  painter — no QPicture::play() anywhere in the class.
- buildHeaderContact() now draws NO/NC symbols directly onto the painter
  instead of recording them into QPicture members.

Also fix mouseDoubleClickEvent: the element under the click is now found
directly from m_hovered_contacts_map using the event position, rather
than relying on m_hovered_contact which could be reset by hoverMoveEvent
between the two clicks of a double-click.

Also remove setBold(true) on terminal name labels: the Qt PDF/SVG/print
engine rendered bold at 4pt as extremely thick glyphs, making exports
unreadable. Normal weight at 4pt is correct and legible on all backends.

Fixes: SIGSEGV in CrossRefItem::paint() on zoom/resize
Fixes: double-click navigation unreliable on Xref contact symbols
Fixes: terminal name labels unreadable in PDF/SVG/print export
2026-05-24 16:00:45 +02:00
Laurent Trinques 47796e183a Update translation files 2026-05-23 19:28:22 +02:00
Laurent Trinques ddf5ffcd89 xref: add option to hide terminal names in cross-references
Add a new boolean property 'showTerminalName' (default: true) to
XRefProperties, with full persistence in XML and QSettings.

A new checkbox "Afficher les numéros de bornes dans les Xrefs" is
added to the XRefPropertiesWidget in the main display group (not in
the cross-only group), so it is active in both Cross and Contacts modes.

When unchecked, terminal names are hidden in all three rendering paths:
  - drawContact()            (Contacts mode: NO/NC/SW symbols)
  - fillCrossRef()           (Cross mode: NO and NC columns)
  - setUpCrossBoundingRect() (Cross mode: bounding rect sizing)

Backward compatible: existing project files without the attribute
default to showTerminalName=true (no visual change).

Files changed:
  sources/properties/xrefproperties.h
  sources/properties/xrefproperties.cpp
  sources/ui/xrefpropertieswidget.ui
  sources/ui/xrefpropertieswidget.cpp
  sources/qetgraphicsitem/crossrefitem.cpp
2026-05-23 19:23:43 +02:00
Laurent Trinques 7d718bb9a0 crossrefitem: fix terminal name sorting for power contacts
The previous sort used QString::toInt() to order terminal names,
which returns 0 for any string containing a non-numeric prefix
(e.g. "R1", "R2", "L1", "L2"...). This caused undefined sort order
and incorrect pole pairing in the Xref contact mirror.

Example: a 4-pole NC power contact with terminals R1..R8 was
displaying R1/R3, R5/R7, R2/R4, R6/R8 instead of the correct
R1/R2, R3/R4, R5/R6, R7/R8.

Fix: extract the trailing numeric part of each terminal name and
compare prefixes separately. If both names share the same prefix
and both have a trailing number, sort numerically on that number;
otherwise fall back to full string comparison.

This covers all naming conventions: "1"/"2"/"3", "R1"/"R2"/"R3",
"L1"/"L2"/"L3", etc.

Applied in both drawContact() and setUpCrossBoundingRect().
2026-05-23 15:50:11 +02:00
Laurent Trinques d949e6eb8c crossrefitem: fix SIGSEGV when dropping power NC contact on diagram
Three bugs were causing a crash (SIGSEGV in QRegion::begin) when
a power NC slave element was placed on a folio, even before linking
it to a master element.

1. m_drawing (QPicture) was never reset between updateLabel() calls.
   QPicture accumulates paint commands — calling qp.begin() on an
   existing QPicture appends to it rather than replacing its content.
   After several updates (load, move, hover...) the picture became
   corrupted and crashed on play().
   Fix: reset m_drawing = QPicture() at the start of updateLabel().

2. m_drawed_contacts was only initialized to 0 in drawAsContacts(),
   but not in drawAsCross(). When drawing in Cross mode, fillCrossRef()
   called drawContact() with an uninitialized m_drawed_contacts value,
   producing a garbage offset. The NC contact symbol uses drawPolyline()
   with a sub-pixel Y coordinate (offset+2.5); with a random offset Qt
   generated an invalid QRegion and crashed.
   This explains why NC contacts crashed but NO contacts did not: the
   NO symbol only uses drawLine() which is more tolerant of bad coords.
   Fix: add m_drawed_contacts = 0 at the start of drawAsCross().

3. setUpCrossBoundingRect() used QRectF() (null rect) as the reference
   rect for painter.boundingRect(), which can return invalid dimensions.
   Additionally, height was accumulated incorrectly: united() + setHeight()
   doubled the height at each iteration, causing an exponentially growing
   bounding rect with multiple contacts.
   Fix: use QRectF(0, 0, 500, 20) as reference rect and accumulate
   height and width independently.
2026-05-23 15:32:28 +02:00
Laurent Trinques dbda958261 terminaldata: add No, Nc, Common types for SW contacts
Extend TerminalData::Type enum with three new semantic values:
- No     : Normally Open terminal of a switch (SW) contact
- Nc     : Normally Closed terminal of a switch (SW) contact
- Common : Common terminal of a switch (SW) contact

Update typeToString() and typeFromString() accordingly.
Fully backward compatible: existing Generic/Inner/Outer types
are unchanged. Elements without typed terminals fall back
to the previous behavior (first 2 named terminals).

terminal: expose terminalType() as public accessor

Add Terminal::terminalType() returning the TerminalData::Type
of this terminal. This allows crossrefitem and other consumers
to filter terminals by semantic role (No, Nc, Common) without
accessing TerminalData internals directly.

terminaleditor: add No, Nc, Common entries to type combobox

Expose the three new TerminalData types (No, Nc, Common) in
the element editor UI so users can assign a semantic role to
each terminal of a SW contact element.

Also fix a pre-existing bug in updateForm() where m_type_cb
was incorrectly using m_orientation_cb->findData() instead
of m_type_cb->findData(), preventing the type from being
restored correctly when selecting a terminal.

terminaleditor: add No, Nc, Common entries to type combobox

Expose the three new TerminalData types (No, Nc, Common) in
the element editor UI so users can assign a semantic role to
each terminal of a SW contact element.

Also fix a pre-existing bug in updateForm() where m_type_cb
was incorrectly using m_orientation_cb->findData() instead
of m_type_cb->findData(), preventing the type from being
restored correctly when selecting a terminal.
2026-05-23 02:09:32 +02:00
Laurent Trinques d691489165 Update translations files 2026-05-22 10:16:24 +02:00
Laurent Trinques 202ea38e40 Merge pull request #464 from Kellermorph/makro-fix
New element: Line definition
2026-05-22 10:06:20 +02:00
Kellermorph 86dafcb576 Merge branch 'master' into makro-fix 2026-05-21 20:52:18 +02:00
Kellermorph f416c2a97e New element: Line definition 2026-05-21 20:47:44 +02:00
Laurent Trinques 7426fedba3 crossrefitem: display terminal names on contact symbols in Xref
When a slave element has named terminals in its element definition
(.elmt), the terminal names (e.g. 13/14 for NO, 11/12 for NC,
12/13/14 for SW) are now displayed on each side of the contact
symbol in the cross-reference view.

- NO/NC contacts: name[0] on the left, name[1] on the right
- SW contacts: name[0] (NO) top-left, name[1] (common) top-right,
  name[2] (NC) bottom-left

Terminal names are read from Terminal::name() which is populated
from TerminalData::m_name during element parsing. If terminals are
not named, nothing is displayed (fully backward compatible).

Users are expected to name their own terminals in the element
editor to avoid duplicating elements in the official collection.
2026-05-21 17:10:44 +02:00
Laurent Trinques 027050c7e7 Update windows-msi.yml 2026-05-21 14:32:07 +02:00
Laurent Trinques fc948ad963 QElectroTech.wxs — add ProductCode="$(var.ProductCode)" to <Package>
+ AllowSameVersionUpgrades="yes" to <MajorUpgrade>
windows-msi.yml — in the ‘Extract version’ step, calculate a unique GUID
based on the commit’s SHA, then pass -d ‘ProductCode=$productGuid’ to the WIX build
This ensures that each build will have a different ProductCode → MajorUpgrade will always be triggered
2026-05-21 14:10:17 +02:00
Laurent Trinques 24d075b64c msi: add The AllowSameVersionUpgrades="yes" setting forces uninstallation
even when the MSI version is identical (which is the case here, as 0.100.1.0
remains unchanged between two nightly builds).
2026-05-21 13:59:53 +02:00
Laurent Trinques f914f91e77 tests sign msi installer 2026-05-21 12:33:46 +02:00
Laurent Trinques fe03a0f643 Restore workflows to pre-test state 2026-05-21 12:29:22 +02:00
Laurent Trinques a7ad0278a6 Update windows-build.yml 2026-05-21 11:49:30 +02:00
Laurent Trinques f517489421 Update windows-build.yml 2026-05-21 11:15:25 +02:00
Laurent Trinques c8fa1c9fa4 test 2026-05-21 10:45:14 +02:00
Laurent Trinques d85aff0c0f Update CMakeLists.txt 2026-05-21 03:40:14 +02:00
Laurent Trinques 96b8e4b19c fix(msi): move Custom element conditions to Condition attribute (WIX0400)
WiX v7 requires conditions to be set via the Condition attribute,
not as inner text of the Custom element.
2026-05-21 03:00:49 +02:00
Laurent Trinques 9760288db6 fix(msi): correct CustomAction pattern for elements\ read-only
- Use SetProperty + WixQuietExec two-step pattern to pass runtime
  INSTALLDIR to a deferred CustomAction (fixes WIX1077 and WIX0400)
- Add WixToolset.Util.wixext/7.0.0 extension (required for WixQuietExec)
- Fix condition syntax: collapse multi-line conditions to single line
- Add -ext WixToolset.Util.wixext to wix build command in windows-msi.yml
2026-05-21 02:55:28 +02:00
Laurent Trinques eeaa059a77 This PR improves the MSI installer by removing the Lancer QET.bat wrapper and handling everything natively in QElectroTech.wxs.
**`build-aux/windows/QElectroTech.wxs`**
- Desktop and Start Menu shortcuts now point directly to `bin\qelectrotech.exe` with all required arguments (`--common-elements-dir`, `--common-tbt-dir`, `--lang-dir`, `-style windowsvista`) — no `.bat` wrapper needed
- Added a deferred `CustomAction` that runs after `InstallFiles` and recursively sets all files in `elements\` to read-only using an inline PowerShell command

**`.github/workflows/windows-msi.yml`**
- Replaced the step that created `Lancer QET.bat` with a step that removes it from the artifact before the WiX build, so it is not embedded in the MSI
- The `.bat` file remains untouched in the ZIP portable build (managed by `windows-build.yml`)

- No console window flashing when launching QElectroTech from the MSI shortcuts
- The `elements\` directory is properly set to read-only after installation, as required
- Cleaner MSI package — no `.bat` file shipped to end users installing via MSI
2026-05-21 02:34:38 +02:00
Laurent Trinques fa334d34a4 git submodule update --remote elements 2026-05-20 22:14:43 +02:00
Laurent Trinques 9664ff54ea Merge pull request #462 from Kellermorph/makro-fix
follow up: wiring list
2026-05-19 13:15:14 +02:00
Laurent Trinques be0da461bd Merge pull request #463 from zakb120/patch-Tr-Translate
Turkish Lang Update
2026-05-19 13:14:45 +02:00
Kellermorph 8c557a7f29 follow up: wiring list 2026-05-18 21:31:11 +02:00
zakb120 413e13a38c Tr Translate Zekeriya AKBABA 2026-05-18 09:29:46 +03:00
Laurent Trinques 87f9f40e91 Update MacQetDeploy_arm64.sh
Windows Build / build-windows (push) Has been cancelled
Test Windows VS2026 migration / Build on windows-2025-vs2026 (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-14 13:12:43 +02:00
Laurent Trinques 43fb34f852 Update MacQetDeploy_arm64.sh 2026-05-14 12:44:45 +02:00
Laurent Trinques d6251c901e Update MacQetDeploy_arm64.sh 2026-05-14 12:11:21 +02:00
Laurent Trinques c0ba961fb3 Update MacQetDeploy_arm64.sh 2026-05-14 11:45:11 +02:00
Laurent Trinques 56f5a09737 Update MacQetDeploy_arm64.sh 2026-05-14 11:20:36 +02:00
Laurent Trinques b5eebb2123 Update MacQetDeploy_arm64.sh 2026-05-14 11:06:56 +02:00
Laurent Trinques 7bf395afab Every Monday at 2 am (UTC) (cron) or manually
↓
  windows-build.yml
  ├── build-windows  →  generates an exe file + zip + portable artefact
  └── deploy-pages   →  clears old files, uploads the exe file + zip to the ‘release nightly’ repository
        ↓ (workflow_run: completed + successful)
  windows-msi.yml
  ├── uploads the portable artefact
  ├── builds the MSI with WiX v7
  ├── deletes the old .msi, uploads the MSI to the nightly version
  └── generates and deploys GitHub Pages  ← the 3 URLs are known here
The GitHub Pages page is no longer generated by windows-build.yml but by windows-msi.yml once the MSI is in the release
2026-05-14 09:53:29 +02:00
Laurent Trinques 93baa00d00 Update windows-msi.yml 2026-05-14 09:26:01 +02:00
Laurent Trinques 6a7ae44ce5 git submodule update --remote elements
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-14 03:21:35 +02:00
Laurent Trinques 89131a21a3 Merge pull request #459 from Kellermorph/makro-fix
Fix and Improve Multi-selection for Diagram Operations
2026-05-14 03:18:20 +02:00
Kellermorph 0b79dfd149 Fix and Improve Multi-selection for Diagram Operations 2026-05-13 19:42:11 +02:00
Laurent Trinques 72178714fd git submodule update --remote elements
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-13 17:25:39 +02:00
Laurent Trinques c7e236cd48 Update test-vs2026.yml 2026-05-13 13:43:55 +02:00
Laurent Trinques fb769b884c Try to fix https://github.com/qelectrotech/qelectrotech-source-mirror/issues/307
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-13 13:16:43 +02:00
Laurent Trinques 2ae9ec87bb Update windows-build.yml
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-13 01:40:55 +02:00
Laurent Trinques 76d311cb35 Move build-aux/windows/generate-page.py -> build-aux/generate-page.py 2026-05-13 01:23:18 +02:00
Laurent Trinques b016cc9f54 Update windows-build.yml 2026-05-13 01:06:17 +02:00
Laurent Trinques 526e39e909 Add files via upload 2026-05-13 01:05:31 +02:00
Laurent Trinques d781105dfd Update windows-build.yml 2026-05-13 01:02:32 +02:00
Laurent Trinques f6b93c6b71 Update windows-build.yml 2026-05-13 00:40:17 +02:00
Laurent Trinques 61319bbbd6 Update windows-build.yml 2026-05-13 00:08:18 +02:00
Laurent Trinques 1b522c251b Update windows-build.yml 2026-05-12 23:34:10 +02:00
Laurent Trinques acfdab77fa Update windows-msi.yml 2026-05-12 23:33:27 +02:00
Laurent Trinques 8e327448cc Update QElectroTech.wxs 2026-05-12 23:01:54 +02:00
Laurent Trinques 7a8cee0ce6 Update QElectroTech.wxs 2026-05-12 22:58:00 +02:00
Laurent Trinques 82b8e7947e Update windows-msi.yml 2026-05-12 22:54:20 +02:00
Laurent Trinques e542a05d3f Update QElectroTech.wxs 2026-05-12 22:53:33 +02:00
Laurent Trinques 0b337a1514 Update windows-msi.yml 2026-05-12 22:44:12 +02:00
Laurent Trinques 48fec1db98 Update QElectroTech.wxs 2026-05-12 22:30:23 +02:00
Laurent Trinques 31f946426b Update QElectroTech.wxs 2026-05-12 22:18:58 +02:00
Laurent Trinques efa74dd0f5 Update QElectroTech.wxs 2026-05-12 22:12:42 +02:00
Laurent Trinques 703797bb97 Update QElectroTech.wxs 2026-05-12 22:08:24 +02:00
Laurent Trinques ef75ee736a Update windows-msi.yml 2026-05-12 22:02:53 +02:00
Laurent Trinques 15e623ac5f Update QElectroTech.wxs 2026-05-12 21:58:07 +02:00
Laurent Trinques df82a1125d Update windows-msi.yml 2026-05-12 21:56:59 +02:00
Laurent Trinques 7edc2e0241 Update windows-msi.yml 2026-05-12 21:50:32 +02:00
Laurent Trinques 55ae3fc3c6 Update QElectroTech.wxs 2026-05-12 21:46:11 +02:00
Laurent Trinques 2f72e6164c Update windows-msi.yml
Removal of all envs: WIX_ACCEPT_EULA: true (does not work)
Addition of a dedicated ‘Accept WiX EULA’ step with wix eula accept wix7 before any other WiX command — this is the official CI/CD method, which writes a sentinel file to the user profile, thereby authorising all subsequent WiX commands in the same job.
2026-05-12 21:39:36 +02:00
Laurent Trinques e40f9c6b72 Update windows-msi.yml 2026-05-12 21:33:05 +02:00
Laurent Trinques ef261a7afd Update windows-msi.yml
Remove --accept-eula on dotnet tool install
2026-05-12 21:25:57 +02:00
Laurent Trinques 1550944011 Update windows-msi.yml
dotnet tool install --global wix --version 7.0.0 --accept-eula
wix extension add WixToolset.UI.wixext/7.0.0 --accept-eula
wix build ... --accept-eula
2026-05-12 20:55:22 +02:00
Laurent Trinques 32733187b8 Update windows-build.yml
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-12 20:12:02 +02:00
Laurent Trinques 79542edd3b Try to build Windows msi files 2026-05-12 19:50:20 +02:00
Laurent Trinques 9e0ec69c61 Add windows spec files for msi test 2026-05-12 19:49:34 +02:00
Laurent Trinques 2e0a1a55e3 Test Windows VS2026 migration on GitHUB action 2026-05-12 19:43:06 +02:00
Laurent Trinques 308f2ea838 Update windows-build.yml 2026-05-12 15:01:21 +02:00
Laurent Trinques 9a9a5446cf Update windows-build.yml 2026-05-12 14:53:03 +02:00
Laurent Trinques 0f8d835a1b git submodule update --remote elements 2026-05-12 14:22:35 +02:00
Laurent Trinques 663336a7bc Update windows-build.yml
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-11 15:00:59 +02:00
Laurent Trinques 4fab90d5b9 Update windows-build.yml
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
2026-05-10 21:27:10 +02:00
Laurent Trinques f67df92f0e Update windows-build.yml
Windows Build / build-windows (push) Has been cancelled
Windows Build / deploy-pages (push) Has been cancelled
Use 7-Zip is already installed on all GitHub Windows computers (C:\Program Files\7-Zip\7z.exe), and it is much faster than Compress-Archive when dealing with 9,931 files.
2026-05-10 13:04:31 +02:00
Laurent Trinques 7e2c2cccf8 Update windows-build.yml
Add Ready_to_use packages
2026-05-10 12:42:30 +02:00
362 changed files with 117387 additions and 40148 deletions
-1
View File
@@ -11,7 +11,6 @@ jobs:
permissions:
contents: write
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v4
with:
+421 -206
View File
@@ -1,22 +1,23 @@
name: Windows Build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
workflow_dispatch:
schedule:
- cron: '0 2 * * 1' # Every Monday at 2:00 UTC
workflow_dispatch: # Manual trigger available at any time
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# =============================================================================
# Job 1: Qt5 build (stable track)
# =============================================================================
build-windows:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
submodules: recursive
fetch-depth: 0
@@ -37,6 +38,7 @@ jobs:
mingw-w64-ucrt-x86_64-qt5-svg
mingw-w64-ucrt-x86_64-qt5-tools
mingw-w64-ucrt-x86_64-qt5-translations
mingw-w64-ucrt-x86_64-qt5-pdf
mingw-w64-ucrt-x86_64-sqlite3
mingw-w64-ucrt-x86_64-pkg-config
mingw-w64-ucrt-x86_64-kwidgetsaddons
@@ -46,7 +48,7 @@ jobs:
mingw-w64-ucrt-x86_64-angleproject
- name: Cache ccache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: C:\Users\runneradmin\AppData\Local\ccache
key: ccache-windows-${{ github.ref_name }}-${{ github.sha }}
@@ -112,6 +114,7 @@ jobs:
-DQT_VERSION_MAJOR=5 \
-DCMAKE_DISABLE_FIND_PACKAGE_Qt6=ON \
-DBUILD_TESTING=OFF \
-DCMAKE_POLICY_DEFAULT_CMP0077=NEW \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_CXX_FLAGS="-DQET_EXPORT_PROJECT_DB" \
-DCMAKE_C_COMPILER_LAUNCHER=/ucrt64/bin/ccache \
@@ -308,7 +311,7 @@ jobs:
- name: Upload build logs on failure
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: build-logs
path: |
@@ -316,46 +319,431 @@ jobs:
nsis_root/files/bin/
if-no-files-found: warn
- name: Zip portable (readytouse)
id: zip_portable
shell: pwsh
run: |
$version = "${{ steps.qet_version.outputs.base_version }}"
$head = "${{ steps.qet_version.outputs.head }}"
$zipName = "qelectrotech-${version}+git${head}-x86-win64-readytouse.zip"
$src = "$env:GITHUB_WORKSPACE\nsis_root\files"
$dst = "$env:GITHUB_WORKSPACE\dist\$zipName"
$7z = "C:\Program Files\7-Zip\7z.exe"
New-Item -ItemType Directory -Force -Path "$env:GITHUB_WORKSPACE\dist" | Out-Null
& $7z a -tzip -mx=5 -mmt=on $dst "$src\*"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$sizeMB = [math]::Round((Get-Item $dst).Length / 1MB, 1)
Write-Output "ZIP created: $zipName ($sizeMB MB)"
"zip_name=$zipName" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
- name: Upload portable (files/ without installer)
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: qelectrotech-${{ steps.qet_version.outputs.base_version }}+git${{ steps.qet_version.outputs.head }}-x86-win64-readytouse
path: nsis_root/files/
path: dist/${{ steps.zip_portable.outputs.zip_name }}
retention-days: 14
- name: Upload NSIS installer
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: qelectrotech-windows-installer
path: dist/Installer_*.exe
retention-days: 14
- name: Upload portable (nom fixe pour le workflow MSI)
uses: actions/upload-artifact@v7
with:
name: qelectrotech-windows-portable
path: nsis_root/files/
retention-days: 14
# =============================================================================
# Job 2: Qt6 build (EXPERIMENTAL track)
#
# Version label: the C++ source (sources/qetversion.cpp) intentionally stays
# Qt-agnostic — QT_VERSION-based detection inside the binary proved unreliable
# (see commit e1aa65f, reverting 608ca984). Here the CI job itself knows with
# certainty which Qt major version it configured (-DQT_VERSION_MAJOR=6), so it
# is safe to label the *package* "0.200.1" at this level, without touching
# QetVersion::currentVersion() or making the binary self-detect its Qt build.
# =============================================================================
build-windows-qt6:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v7
with:
submodules: recursive
fetch-depth: 0
- name: Install MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
cache: true
install: >-
git
mingw-w64-ucrt-x86_64-ccache
mingw-w64-ucrt-x86_64-gcc
mingw-w64-ucrt-x86_64-cmake
mingw-w64-ucrt-x86_64-ninja
mingw-w64-ucrt-x86_64-qt6-base
mingw-w64-ucrt-x86_64-qt6-svg
mingw-w64-ucrt-x86_64-qt6-tools
mingw-w64-ucrt-x86_64-qt6-translations
mingw-w64-ucrt-x86_64-qt6-pdf
mingw-w64-ucrt-x86_64-sqlite3
mingw-w64-ucrt-x86_64-pkg-config
mingw-w64-ucrt-x86_64-nsis
mingw-w64-ucrt-x86_64-angleproject
- name: Cache ccache
uses: actions/cache@v5
with:
path: C:\Users\runneradmin\AppData\Local\ccache
key: ccache-windows-qt6-${{ github.ref_name }}-${{ github.sha }}
restore-keys: |
ccache-windows-qt6-${{ github.ref_name }}-
ccache-windows-qt6-
- name: Configure ccache
shell: msys2 {0}
run: |
/ucrt64/bin/ccache --set-config=max_size=500M
/ucrt64/bin/ccache --set-config=compression=true
/ucrt64/bin/ccache -z
echo "=== ccache config ==="
/ucrt64/bin/ccache -p
- name: Patch NSIS Welcome page — fix title font size
shell: msys2 {0}
run: |
set -euo pipefail
WELCOME_NSH=$(find /ucrt64 -path "*/Modern UI 2/Pages/Welcome.nsh" | head -1)
if [ -z "$WELCOME_NSH" ]; then
echo "WARNING: Welcome.nsh not found, skipping font patch"
else
echo "Patching: $WELCOME_NSH"
sed -i '/WelcomePage\.Title\.Font/s/"[0-9]\+" "700"/"10" "700"/' "$WELCOME_NSH"
grep 'WelcomePage.Title.Font' "$WELCOME_NSH"
echo " OK font size patched to 10"
fi
FINISH_NSH=$(find /ucrt64 -path "*/Modern UI 2/Pages/Finish.nsh" | head -1)
if [ -z "$FINISH_NSH" ]; then
echo "WARNING: Finish.nsh not found, skipping font patch"
else
echo "Patching: $FINISH_NSH"
sed -i '/FinishPage\.Title\.Font/s/"[0-9]\+" "700"/"10" "700"/' "$FINISH_NSH"
grep 'FinishPage.Title.Font' "$FINISH_NSH"
echo " OK font size patched to 10"
fi
- name: Build with cmake
shell: msys2 {0}
run: |
set -euo pipefail
cd "$GITHUB_WORKSPACE"
mkdir build && cd build
NPROC=$(nproc)
echo "Available CPUs: $NPROC"
cmake -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=/ucrt64 \
-DQt6_DIR=/ucrt64/lib/cmake/Qt6 \
-DQT_VERSION_MAJOR=6 \
-DBUILD_WITH_KF5=OFF \
-DPACKAGE_TESTS=OFF \
-DCMAKE_POLICY_DEFAULT_CMP0077=NEW \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_CXX_FLAGS="-DQET_EXPORT_PROJECT_DB" \
-DCMAKE_C_COMPILER_LAUNCHER=/ucrt64/bin/ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=/ucrt64/bin/ccache \
-DSQLite3_INCLUDE_DIR=/ucrt64/include \
-DSQLite3_LIBRARY=/ucrt64/lib/libsqlite3.dll.a \
..
ninja -j"$NPROC"
- name: Show ccache stats
shell: msys2 {0}
run: |
echo "=== ccache statistics ==="
/ucrt64/bin/ccache -s
- name: Verify exe was built
shell: msys2 {0}
run: |
set -euo pipefail
EXE=$(find "$GITHUB_WORKSPACE/build" -maxdepth 3 -iname "qelectrotech.exe" | head -1)
if [ -z "$EXE" ]; then
echo "ERROR: no qelectrotech.exe found in build/"
find "$GITHUB_WORKSPACE/build" -maxdepth 3 -name "*.exe" || true
exit 1
fi
SIZE=$(stat -c%s "$EXE")
echo "Exe found: $EXE ($SIZE bytes)"
[ "$SIZE" -gt 100000 ] || { echo "ERROR: exe too small"; exit 1; }
- name: Deploy — copy exe + windeployqt + DLLs
shell: msys2 {0}
run: |
set -euo pipefail
NSIS_ROOT="$GITHUB_WORKSPACE/nsis_root"
FILES="$NSIS_ROOT/files"
BIN="$FILES/bin"
mkdir -p "$BIN"
EXE=$(find "$GITHUB_WORKSPACE/build" -maxdepth 3 -iname "qelectrotech.exe" | head -1)
echo "Copying exe: $EXE -> $BIN/QElectroTech.exe"
cp "$EXE" "$BIN/QElectroTech.exe"
cd "$BIN"
/ucrt64/bin/windeployqt6 \
--release \
--no-translations \
--no-compiler-runtime \
./QElectroTech.exe || true
echo "=== 3-pass transitive DLL scan ==="
set +e
for PASS in 1 2 3; do
echo "-- Pass $PASS --"
for bin_file in "$BIN"/*.dll "$BIN"/*.exe "$BIN"/sqldrivers/*.dll "$BIN"/platforms/*.dll "$BIN"/imageformats/*.dll; do
[ -f "$bin_file" ] || continue
while IFS= read -r line; do
dll_path=$(echo "$line" | awk '{print $3}')
[ -f "$dll_path" ] || continue
dll_name=$(basename "$dll_path")
dst="$BIN/$dll_name"
if [ ! -f "$dst" ]; then
cp "$dll_path" "$dst"
echo " Copied (pass $PASS): $dll_name"
fi
done < <(ldd "$bin_file" 2>/dev/null | grep -i '/ucrt64/bin/')
done
done
set -e
DLL_COUNT=$(find "$BIN" -name "*.dll" | wc -l)
echo "=== $DLL_COUNT DLLs present after scan ==="
ls -lh "$BIN/QElectroTech.exe" || { echo "ERROR: exe missing from bin/"; exit 1; }
[ "$DLL_COUNT" -gt 5 ] || { echo "ERROR: too few DLLs"; exit 1; }
cd "$GITHUB_WORKSPACE"
cp /ucrt64/bin/libgcc_s_seh-1.dll "$BIN/"
cp /ucrt64/bin/libstdc++-6.dll "$BIN/"
cp /ucrt64/bin/libwinpthread-1.dll "$BIN/"
SQLITE=$(find /ucrt64/bin -name "libsqlite3*.dll" | head -1)
if [ -n "$SQLITE" ]; then
cp "$SQLITE" "$BIN/"
echo "SQLite3 copied: $(basename $SQLITE)"
else
echo "WARNING: libsqlite3 not found in /ucrt64/bin/"
fi
cp "$GITHUB_WORKSPACE/build-aux/windows/QET64.nsi" "$NSIS_ROOT/"
cp "$GITHUB_WORKSPACE/build-aux/windows/lang_extra.nsh" "$NSIS_ROOT/"
cp "$GITHUB_WORKSPACE/build-aux/windows/lang_extra_fr.nsh" "$NSIS_ROOT/"
cp "$GITHUB_WORKSPACE/build-aux/windows/lang_extra_missing.nsh" "$NSIS_ROOT/"
cp -r "$GITHUB_WORKSPACE/build-aux/windows/nsis_base/." "$NSIS_ROOT/"
curl -fsSL \
"https://raw.githubusercontent.com/qelectrotech/qelectrotech-source-mirror/refs/heads/master/misc/Lancer%20QET_qt6.bat" \
-o "$FILES/Lancer QET_qt6.bat"
cp -r "$GITHUB_WORKSPACE/elements" "$FILES/elements" || true
cp -r "$GITHUB_WORKSPACE/titleblocks" "$FILES/titleblocks" || true
cp -r "$GITHUB_WORKSPACE/examples" "$FILES/examples" || true
cp -r "$GITHUB_WORKSPACE/fonts" "$FILES/fonts" || true
cp -r "$GITHUB_WORKSPACE/lang" "$FILES/lang" || true
find "$GITHUB_WORKSPACE/build" -name "*.qm" -exec cp {} "$FILES/lang/" \; 2>/dev/null || true
echo "=== .qm files in files/lang/ ==="
ls "$FILES/lang/"*.qm 2>/dev/null | wc -l || echo "0 .qm files"
for f in LICENSE ChangeLog CREDIT README ELEMENTS.LICENSE; do
cp "$GITHUB_WORKSPACE/$f" "$FILES/$f" 2>/dev/null || true
done
echo "=== Verification of key files in files/ ==="
for f in LICENSE ChangeLog CREDIT README ELEMENTS.LICENSE \
qet_uninstall_file_associations.reg register_filetypes.bat "Lancer QET_qt6.bat"; do
[ -f "$FILES/$f" ] \
&& echo " OK : $f" \
|| echo " MISSING: $f"
done
for d in ico elements lang titleblocks fonts examples bin; do
[ -d "$FILES/$d" ] \
&& echo " OK : $d/" \
|| echo " MISSING: $d/"
done
- name: Extract version for installer name (Qt6 — labelled 0.200.1)
shell: msys2 {0}
id: qet_version
run: |
set -euo pipefail
GITCOMMIT=$(git -C "$GITHUB_WORKSPACE" rev-parse --short HEAD)
A=$(git -C "$GITHUB_WORKSPACE" rev-list HEAD --count)
HEAD=$(( A + 473 ))
# Deliberately NOT parsed from sources/qetversion.cpp: the CI job
# already knows for certain it configured Qt6 (-DQT_VERSION_MAJOR=6),
# so the "0.200.1" experimental-track label is set here explicitly
# rather than relying on in-binary Qt version detection.
VERSION="0.200.1"
FULL_VERSION="${VERSION}-qt6-r${HEAD}-${GITCOMMIT}_x86_64-win64"
BASE_VERSION="${VERSION}-qt6"
echo "version=$FULL_VERSION" >> "$GITHUB_OUTPUT"
echo "base_version=$BASE_VERSION" >> "$GITHUB_OUTPUT"
echo "gitcommit=$GITCOMMIT" >> "$GITHUB_OUTPUT"
echo "head=$HEAD" >> "$GITHUB_OUTPUT"
echo "VERSION : $VERSION"
echo "GITCOMMIT : $GITCOMMIT"
echo "HEAD (rev) : $HEAD"
echo "FULL : $FULL_VERSION"
- name: Patch QET64.nsi — version + exe name + absolute paths
shell: msys2 {0}
run: |
set -euo pipefail
VERSION="${{ steps.qet_version.outputs.version }}"
NSI="$GITHUB_WORKSPACE/nsis_root/QET64.nsi"
FILES_WIN=$(cygpath -w "$GITHUB_WORKSPACE/nsis_root/files")
SCRIPT="$GITHUB_WORKSPACE/build-aux/windows/patch_nsi.py"
python3 "$SCRIPT" "$NSI" "$VERSION" "$FILES_WIN"
echo "=== Verification ==="
grep 'SOFT_VERSION' "$NSI" | head -1
grep -m2 'nsis_root' "$NSI" | head -2
echo "=== Contents of nsis_root/files/ ==="
ls "$GITHUB_WORKSPACE/nsis_root/files/"
- name: Build NSIS installer
shell: msys2 {0}
run: |
set -euo pipefail
NSIS_ROOT="$GITHUB_WORKSPACE/nsis_root"
cd "$NSIS_ROOT"
echo "=== CWD : $(pwd) ==="
MSYS2_ARG_CONV_EXCL="*" makensis /V4 QET64.nsi
RC=$?
echo "=== Contents of nsis_root after makensis ==="
ls "$NSIS_ROOT/"
[ $RC -eq 0 ] || { echo "ERROR: makensis failed (exit $RC)"; exit 1; }
- name: Move installer to dist/
shell: msys2 {0}
run: |
set -euo pipefail
mkdir -p "$GITHUB_WORKSPACE/dist"
INSTALLER=$(find "$GITHUB_WORKSPACE/nsis_root" -maxdepth 1 -iname "installer_*.exe" | head -1)
if [ -z "$INSTALLER" ]; then
echo "ERROR: no installer .exe found in nsis_root/"
ls "$GITHUB_WORKSPACE/nsis_root/"
exit 1
fi
echo "Moving: $INSTALLER -> dist/"
mv "$INSTALLER" "$GITHUB_WORKSPACE/dist/"
- name: Upload build logs on failure
if: failure()
uses: actions/upload-artifact@v7
with:
name: build-logs-qt6
path: |
build/CMakeFiles/*.log
nsis_root/files/bin/
if-no-files-found: warn
- name: Zip portable (readytouse)
id: zip_portable
shell: pwsh
run: |
$version = "${{ steps.qet_version.outputs.base_version }}"
$head = "${{ steps.qet_version.outputs.head }}"
$zipName = "qelectrotech-${version}+git${head}-x86-win64-readytouse.zip"
$src = "$env:GITHUB_WORKSPACE\nsis_root\files"
$dst = "$env:GITHUB_WORKSPACE\dist\$zipName"
$7z = "C:\Program Files\7-Zip\7z.exe"
New-Item -ItemType Directory -Force -Path "$env:GITHUB_WORKSPACE\dist" | Out-Null
& $7z a -tzip -mx=5 -mmt=on $dst "$src\*"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$sizeMB = [math]::Round((Get-Item $dst).Length / 1MB, 1)
Write-Output "ZIP created: $zipName ($sizeMB MB)"
"zip_name=$zipName" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
- name: Upload portable (files/ without installer)
uses: actions/upload-artifact@v7
with:
name: qelectrotech-${{ steps.qet_version.outputs.base_version }}+git${{ steps.qet_version.outputs.head }}-x86-win64-readytouse
path: dist/${{ steps.zip_portable.outputs.zip_name }}
retention-days: 14
- name: Upload NSIS installer
uses: actions/upload-artifact@v7
with:
name: qelectrotech-windows-installer-qt6
path: dist/Installer_*.exe
retention-days: 14
- name: Upload portable (nom fixe pour le workflow MSI)
uses: actions/upload-artifact@v7
with:
name: qelectrotech-windows-portable-qt6
path: nsis_root/files/
retention-days: 14
# ---------------------------------------------------------------------------
# Job 2 : Publie une release nightly + page GitHub Pages
# Job 3 : Publie les assets nightly (exe + zip, Qt5 et Qt6) sur la release
# Ne tourne que sur push master (pas sur les PRs)
# ---------------------------------------------------------------------------
deploy-pages:
needs: build-windows
publish-nightly-assets:
needs: [build-windows, build-windows-qt6]
runs-on: ubuntu-latest
if: github.event_name != 'pull_request'
permissions:
contents: write
steps:
- name: Checkout gh-pages branch
uses: actions/checkout@v4
- name: Download installer artifacts (Qt5 + Qt6)
uses: actions/download-artifact@v8
with:
ref: gh-pages
path: gh-pages
- name: Download installer artifact
uses: actions/download-artifact@v4
with:
name: qelectrotech-windows-installer
pattern: qelectrotech-windows-installer*
path: downloaded/installer/
merge-multiple: true
- name: Download portable artifacts (Qt5 + Qt6)
uses: actions/download-artifact@v8
with:
pattern: qelectrotech-*-readytouse
path: downloaded/portable/
merge-multiple: true
- name: Delete old nightly assets (.exe and .zip)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
run: |
gh release view nightly --repo "$REPO" --json assets \
--jq '.assets[] | select(.name | test("\\.(exe|zip)$")) | .name' \
| while read -r name; do
echo "Deleting old asset: $name"
gh release delete-asset nightly "$name" --repo "$REPO" --yes
done
echo "Old .exe and .zip assets deleted."
- name: Update nightly release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
tag_name: nightly
name: "Nightly Build Windows"
@@ -368,190 +756,17 @@ jobs:
| **Run** | https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} |
| **Date** | ${{ github.event.head_commit.timestamp }} |
> ⚠️ This release is overwritten on every push to `master`.
> ⚠️ This is a development version; it introduces new features you want, but may cause bugs that have not yet been identified yet.
> For stable releases, see the [Releases page](https://github.com/${{ github.repository }}/releases).
> 🧪 **Qt6 builds are experimental.** Files tagged `-qt6-` are built against Qt6 and are not yet
> as tested as the Qt5 track. Expect rough edges; report issues clearly labelled "Qt6".
prerelease: true
make_latest: false
files: downloaded/installer/*.exe
files: |
downloaded/installer/*.exe
downloaded/portable/*.zip
token: ${{ secrets.GITHUB_TOKEN }}
- name: Generate download page (index.html)
run: |
DATE=$(date -u '+%Y-%m-%d %H:%M UTC')
SHORT="${{ github.sha }}"
SHORT="${SHORT:0:7}"
REPO="${{ github.repository }}"
RUN_URL="https://github.com/$REPO/actions/runs/${{ github.run_id }}"
EXE_NAME=$(ls downloaded/installer/*.exe | xargs -I{} basename {})
INSTALLER_URL="https://github.com/$REPO/releases/download/nightly/$EXE_NAME"
mkdir -p gh-pages
cat > gh-pages/index.html << HTMLEOF
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>QElectroTech Nightly Builds</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #f0f4f8;
color: #2d3748;
min-height: 100vh;
}
header {
background: linear-gradient(135deg, #1a365d 0%, #2b6cb0 100%);
color: white;
padding: 48px 24px 40px;
text-align: center;
}
header h1 { font-size: 2.2em; letter-spacing: -0.5px; margin-bottom: 8px; }
header p { opacity: 0.8; font-size: 1.05em; }
main {
max-width: 680px;
margin: 40px auto;
padding: 0 20px 60px;
}
.card {
background: white;
border-radius: 12px;
padding: 28px;
margin-bottom: 24px;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
}
.card h2 {
font-size: 1em;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #718096;
margin-bottom: 16px;
}
.meta {
font-size: 0.875em;
color: #4a5568;
line-height: 1.8;
margin-bottom: 20px;
}
.meta a { color: #2b6cb0; text-decoration: none; }
.meta a:hover { text-decoration: underline; }
.badge {
display: inline-block;
background: #ebf8ff;
color: #2b6cb0;
border-radius: 4px;
font-size: 0.8em;
font-weight: 600;
padding: 2px 8px;
margin-left: 6px;
vertical-align: middle;
}
.warning {
background: #fffbeb;
border-left: 4px solid #f6ad55;
border-radius: 4px;
padding: 12px 16px;
font-size: 0.875em;
color: #744210;
margin-bottom: 24px;
line-height: 1.5;
}
.warning a { color: #c05621; }
.downloads { display: flex; flex-direction: column; gap: 12px; }
.btn {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 20px;
border-radius: 8px;
font-size: 0.95em;
font-weight: 600;
text-decoration: none;
transition: transform 0.1s, box-shadow 0.1s;
}
.btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.btn-primary { background: #2b6cb0; color: white; }
.btn-secondary { background: #edf2f7; color: #2d3748; }
.btn-icon { font-size: 1.3em; }
.btn-text small {
display: block;
font-weight: 400;
font-size: 0.8em;
opacity: 0.75;
margin-top: 1px;
}
footer {
text-align: center;
font-size: 0.8em;
color: #a0aec0;
padding: 32px 0 0;
}
footer a { color: #718096; text-decoration: none; }
</style>
</head>
<body>
<header>
<h1>⚡ QElectroTech</h1>
<p>Nightly Windows Builds</p>
</header>
<main>
<div class="card">
<h2>Build info</h2>
<div class="meta">
📅 &nbsp;<strong>$DATE</strong><br>
🔀 &nbsp;Commit <a href="https://github.com/$REPO/commit/${{ github.sha }}"><code>$SHORT</code></a><br>
🔧 &nbsp;<a href="$RUN_URL">CI Run #${{ github.run_number }}</a>
<span class="badge">nightly</span>
</div>
<div class="warning">
⚠️ These builds are generated automatically on every commit to <code>master</code>
and may be unstable or incomplete.
For production use, download a
<a href="https://github.com/$REPO/releases">stable release</a>.
</div>
</div>
<div class="card">
<h2>🪟 Windows — x86_64</h2>
<div class="downloads">
<a class="btn btn-primary" href="$INSTALLER_URL">
<span class="btn-icon">⬇</span>
<span class="btn-text">
Windows Installer
<small>.exe — recommended, includes all dependencies</small>
</span>
</a>
<a class="btn btn-secondary" href="https://github.com/$REPO/releases/tag/nightly">
<span class="btn-icon">📦</span>
<span class="btn-text">
All nightly files on GitHub
<small>Release page with checksums</small>
</span>
</a>
</div>
</div>
</main>
<footer>
Auto-generated by GitHub Actions &nbsp;·&nbsp;
<a href="https://github.com/$REPO">Source on GitHub</a> &nbsp;·&nbsp;
<a href="https://qelectrotech.org">qelectrotech.org</a>
</footer>
</body>
</html>
HTMLEOF
- name: Commit and push to gh-pages
run: |
cd gh-pages
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add index.html
git diff --staged --quiet \
|| git commit -m "nightly: build #${{ github.run_number }} (${{ github.sha }})"
git push origin gh-pages
# GitHub Pages is generated and deployed by windows-msi.yml
# after the MSI upload, so that all URLs (exe/zip/msi, Qt5+Qt6) are known.
+434
View File
@@ -0,0 +1,434 @@
name: Windows MSI (WiX v7)
on:
# Triggered automatically after a successful Windows build on master
workflow_run:
workflows: ["Windows Build"]
types: [completed]
branches: [master]
# Manual trigger still available (e.g. for a specific run_id)
workflow_dispatch:
inputs:
run_id:
description: "Run ID of 'Windows Build' (leave empty for automatic)"
required: false
default: ""
jobs:
build-msi:
name: Build MSI with WiX v7 (${{ matrix.flavor }})
runs-on: windows-latest
# Only runs if Windows Build succeeded (or triggered manually)
if: >
github.event_name == 'workflow_dispatch' ||
github.event.workflow_run.conclusion == 'success'
strategy:
fail-fast: false
matrix:
include:
- flavor: qt5
portable_artifact: qelectrotech-windows-portable
version_source: qetversion # parsed from sources/qetversion.cpp
label_suffix: ""
experimental: false
- flavor: qt6
portable_artifact: qelectrotech-windows-portable-qt6
version_source: hardcoded # see note in "Extract version" step
label_suffix: "-qt6-EXPERIMENTAL"
experimental: true
permissions:
contents: write
pages: write
id-token: write # Required by SignPath
outputs:
qt5_msi: ${{ steps.export.outputs.msi_name_qt5 }}
qt6_msi: ${{ steps.export.outputs.msi_name_qt6 }}
steps:
# ----------------------------------------------------------------
# 1. Checkout (to retrieve QElectroTech.wxs and sources)
# ----------------------------------------------------------------
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 0
# ----------------------------------------------------------------
# 2. Download the portable artifact for this flavor
# ----------------------------------------------------------------
- name: Download portable artifact
uses: actions/download-artifact@v8
with:
name: ${{ matrix.portable_artifact }}
path: artifact\files
# workflow_run => use the triggering run's ID
# workflow_dispatch => use input run_id if provided, otherwise current run
run-id: ${{ github.event.workflow_run.id || github.event.inputs.run_id || github.run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }}
# ----------------------------------------------------------------
# 3. Extract version
# Qt5: parsed from sources/qetversion.cpp (single source of truth
# for the software's own reported version).
# Qt6: hardcoded "0.200.1" here — deliberately NOT parsed from the
# binary/source, since Qt-version self-detection inside the
# compiled code proved unreliable (see commit e1aa65f). The CI
# matrix entry already knows for certain which flavor it is
# packaging, so the experimental-track label is set explicitly
# at the packaging level instead.
# ----------------------------------------------------------------
- name: Extract version
id: version
shell: pwsh
run: |
if ("${{ matrix.version_source }}" -eq "hardcoded") {
$ver = "0.200.1"
} else {
$src = Get-Content "sources\qetversion.cpp" -Raw -ErrorAction SilentlyContinue
if ($src -match 'return QVersionNumber\{([^}]+)\}') {
$ver = $Matches[1] -replace '\s','' -replace ',','.'
} else {
# Fallback: CMakeLists.txt
$cmake = Get-Content "CMakeLists.txt" -Raw
if ($cmake -match 'project\s*\([^)]*VERSION\s+([\d]+\.[\d]+\.[\d]+)') {
$ver = $Matches[1]
} else {
$ver = "0.0.0"
}
}
}
# Short SHA for the display version
$sha = git rev-parse --short HEAD 2>$null
if (-not $sha) { $sha = "unknown" }
# Cumulative revision number (same calculation as windows-build.yml)
$count = git rev-list HEAD --count 2>$null
$rev = [int]$count + 473
# MSI Version only compares Major.Minor.Build (the 4th field is
# ignored by Windows Installer for upgrade detection), so we
# inject rev into Build to force a real increase between
# nightlies and trigger MajorUpgrade's automatic uninstall.
$verParts = $ver -split '\.'
$verMsi = "$($verParts[0]).$($verParts[1]).$($rev % 65535)"
$flavorTag = "${{ matrix.label_suffix }}"
$verDisplay = "${ver}${flavorTag}-r${rev}-${sha}_x86_64-win64"
echo "VERSION_MSI=$verMsi" >> $env:GITHUB_OUTPUT
echo "VERSION_DISPLAY=$verDisplay" >> $env:GITHUB_OUTPUT
Write-Host "Version MSI : $verMsi"
Write-Host "Version display : $verDisplay"
# Qt platform argument: only Qt5 needs the QTBUG-83161 font-rendering
# workaround (GDI backend). Qt6 uses DirectWrite by default and does
# not need it. Computed once here so both the bundled "Lancer QET.bat"
# and the MSI shortcuts (wix build -d QtPlatformArgs=...) stay in sync.
if ("${{ matrix.flavor }}" -eq "qt5") {
$qtArgs = "-platform windows:fontengine=freetype"
} else {
$qtArgs = ""
}
echo "QT_ARGS=$qtArgs" >> $env:GITHUB_OUTPUT
Write-Host "Qt platform args: '$qtArgs'"
# ----------------------------------------------------------------
# 4. Install WiX v7, accept EULA and install WixUI extension
# ----------------------------------------------------------------
- name: Install WiX v7
shell: pwsh
run: |
dotnet tool install --global wix --version 7.0.0
$toolsPath = [System.IO.Path]::Combine($env:USERPROFILE, '.dotnet', 'tools')
$env:PATH = "$toolsPath;$env:PATH"
echo $toolsPath >> $env:GITHUB_PATH
wix eula accept wix7
wix extension add WixToolset.UI.wixext/7.0.0
wix extension add WixToolset.Util.wixext/7.0.0
Write-Host "WiX v7 installed, EULA accepted, UI + Util extensions added."
# ----------------------------------------------------------------
# 5. Check that the WXS file exists in the repository
# ----------------------------------------------------------------
- name: Check WXS file
shell: pwsh
run: |
$wxs = "build-aux\windows\QElectroTech.wxs"
if (-not (Test-Path $wxs)) {
Write-Error "WXS file not found: $wxs"
exit 1
}
Write-Host "WXS found: $wxs"
# ----------------------------------------------------------------
# 6. Check the artifact structure and locate files/
# ----------------------------------------------------------------
- name: Check artifact structure
shell: pwsh
run: |
$exe = Get-ChildItem -Path "artifact\files" -Filter "qelectrotech.exe" -Recurse | Select-Object -First 1
if (-not $exe) {
$exe = Get-ChildItem -Path "artifact\files" -Filter "QElectroTech.exe" -Recurse | Select-Object -First 1
}
if (-not $exe) {
Write-Error "qelectrotech.exe not found in artifact"
exit 1
}
Write-Host "Executable: $($exe.FullName) ($([math]::Round($exe.Length/1MB,1)) MB)"
$binDir = $exe.Directory.FullName
$filesDir = Split-Path $binDir -Parent
echo "FILES_DIR=$filesDir" >> $env:GITHUB_ENV
Write-Host "FILES_DIR: $filesDir"
# ----------------------------------------------------------------
# 7. Convert LICENSE (GPL-2) to RTF for the WixUI licence screen
# ----------------------------------------------------------------
- name: Convert LICENSE to RTF
shell: pwsh
run: |
$licSrc = "LICENSE"
$licRtf = "$env:TEMP\License.rtf"
if (-not (Test-Path $licSrc)) {
Write-Error "LICENSE file not found in repository root"
exit 1
}
$lines = Get-Content $licSrc -Encoding UTF8
$rtf = New-Object System.Text.StringBuilder
[void]$rtf.AppendLine('{\rtf1\ansi\ansicpg1252\deff0')
[void]$rtf.AppendLine('{\fonttbl{\f0\fmodern\fprq1\fcharset0 Courier New;}}')
[void]$rtf.AppendLine('{\colortbl;\red0\green0\blue0;}')
[void]$rtf.AppendLine('\f0\fs18\cf1')
foreach ($line in $lines) {
$escaped = $line `
-replace '\\', '\\\\' `
-replace '\{', '\{' `
-replace '\}', '\}'
[void]$rtf.AppendLine("$escaped\par")
}
[void]$rtf.AppendLine('}')
[System.IO.File]::WriteAllText($licRtf, $rtf.ToString(), [System.Text.Encoding]::ASCII)
echo "LICENSE_RTF=$licRtf" >> $env:GITHUB_ENV
Write-Host "License.rtf generated: $licRtf"
# ----------------------------------------------------------------
# 8. Replace Lancer QET.bat with the MSI-specific version
# ----------------------------------------------------------------
- name: Replace Lancer QET.bat for MSI
shell: pwsh
run: |
$qtArgs = "${{ steps.version.outputs.QT_ARGS }}"
$bat = "$env:FILES_DIR\Lancer QET.bat"
$content = "@echo off`r`nstart `"`" `"%~dp0bin\qelectrotech.exe`" --common-elements-dir=`"%~dp0elements/`" --common-tbt-dir=`"%~dp0titleblocks/`" --lang-dir=`"%~dp0lang/`" $qtArgs`r`n"
[System.IO.File]::WriteAllText($bat, $content, [System.Text.Encoding]::ASCII)
Write-Host "Lancer QET.bat replaced for MSI installation (qtArgs: '$qtArgs')."
# ----------------------------------------------------------------
# 9. Build the MSI (unsigned)
# Qt6 (experimental) uses a distinct ProductCode seed so it never
# collides with / upgrades over the Qt5 MSI — they must be able to
# coexist as clearly separate installs.
# ----------------------------------------------------------------
- name: Build MSI
shell: pwsh
run: |
$version = "${{ steps.version.outputs.VERSION_MSI }}"
$verDisplay = "${{ steps.version.outputs.VERSION_DISPLAY }}"
$filesDir = $env:FILES_DIR
$licRtf = $env:LICENSE_RTF
$wxs = "build-aux\windows\QElectroTech.wxs"
$outputName = "QElectroTech-${verDisplay}.msi"
$seed = "qelectrotech-msi-${{ matrix.flavor }}-$version"
$sha1 = [System.Security.Cryptography.SHA1]::Create()
$hash = $sha1.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($seed))
$hash[6] = ($hash[6] -band 0x0F) -bor 0x50
$hash[8] = ($hash[8] -band 0x3F) -bor 0x80
$productCode = "{$([System.Guid]::new([byte[]]$hash[0..15]).ToString().ToUpper())}"
New-Item -ItemType Directory -Force -Path "dist" | Out-Null
Write-Host "=== wix build (${{ matrix.flavor }}) ==="
Write-Host " Version : $version"
Write-Host " ProductCode : $productCode"
Write-Host " Output : dist\$outputName"
$qtArgs = "${{ steps.version.outputs.QT_ARGS }}"
wix build $wxs `
-arch x64 `
-d "Version=$version" `
-d "ProductVersion=$verDisplay" `
-d "ProductCode=$productCode" `
-d "FilesDir=$filesDir" `
-d "LicenseRtf=$licRtf" `
-d QtPlatformArgs="$qtArgs" `
-ext WixToolset.UI.wixext `
-ext WixToolset.Util.wixext `
-o "dist\$outputName"
if (-not (Test-Path "dist\$outputName")) {
Write-Error "MSI not generated: dist\$outputName"
exit 1
}
$size = [math]::Round((Get-Item "dist\$outputName").Length / 1MB, 1)
Write-Host "MSI generated: dist\$outputName ($size MB) OK"
echo "MSI_NAME=$outputName" >> $env:GITHUB_ENV
- name: Upload unsigned MSI artifact (pre-signing)
id: upload_unsigned
uses: actions/upload-artifact@v7
with:
name: qelectrotech-windows-msi-unsigned-${{ matrix.flavor }}
path: dist\*.msi
retention-days: 1
if-no-files-found: error
# Qt6 stays on the unsigned/experimental track (no signing request for
# this flavor), and forks never have the SignPath secrets, so guard on
# both: only qt5, and only in the upstream repo.
# (cf. DieterMayerOSS:fix/msi-signing-fork-guard, d3f60c88)
- name: Sign MSI via SignPath
if: matrix.flavor == 'qt5' && github.repository == 'qelectrotech/qelectrotech-source-mirror'
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }}
project-slug: 'qelectrotech-source-mirror'
signing-policy-slug: 'test-signing'
artifact-configuration-slug: 'MSI'
github-artifact-id: ${{ steps.upload_unsigned.outputs.artifact-id }}
wait-for-completion: true
output-artifact-directory: 'dist\'
- name: Upload signed MSI artifact
uses: actions/upload-artifact@v7
with:
name: qelectrotech-windows-msi-${{ matrix.flavor }}
path: dist\*.msi
retention-days: 14
if-no-files-found: error
- name: Delete old nightly .msi asset for this flavor
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
run: |
$pattern = if ("${{ matrix.flavor }}" -eq "qt6") { "-qt6-EXPERIMENTAL.*\\.msi$" } else { "\\.msi$" }
$names = gh release view nightly --repo $env:REPO --json assets --jq ".assets[] | select(.name | test(`"$pattern`")) | .name"
foreach ($name in ($names -split "`n" | Where-Object { $_ })) {
if ("${{ matrix.flavor }}" -eq "qt5" -and $name -match "-qt6-EXPERIMENTAL") { continue }
Write-Host "Deleting old asset: $name"
gh release delete-asset nightly $name --repo $env:REPO --yes
}
shell: pwsh
- name: Upload MSI to nightly release
uses: softprops/action-gh-release@v3
with:
tag_name: nightly
files: dist/*.msi
fail_on_unmatched_files: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Export MSI name for page generation
id: export
shell: pwsh
run: |
$name = "$env:MSI_NAME"
if ("${{ matrix.flavor }}" -eq "qt6") {
echo "msi_name_qt6=$name" >> $env:GITHUB_OUTPUT
} else {
echo "msi_name_qt5=$name" >> $env:GITHUB_OUTPUT
}
- name: Summary
if: always()
shell: pwsh
run: |
Write-Host "=== MSI build summary (${{ matrix.flavor }}) ==="
Write-Host "Version : ${{ steps.version.outputs.VERSION_DISPLAY }}"
Write-Host "Experimental : ${{ matrix.experimental }}"
# ---------------------------------------------------------------------------
# Job 2 : Génère et déploie la page GitHub Pages une fois les DEUX MSI
# (Qt5 + Qt6) publiés, pour que toutes les URLs soient connues.
# ---------------------------------------------------------------------------
deploy-pages:
needs: build-msi
runs-on: ubuntu-latest
if: always() && needs.build-msi.result == 'success'
permissions:
contents: write
pages: write
id-token: write
steps:
- name: Checkout master (for generate-page.py)
uses: actions/checkout@v7
with:
ref: master
path: source
sparse-checkout: build-aux/generate-page.py
sparse-checkout-cone-mode: false
- name: Generate download page (index.html)
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
REPO="${{ github.repository }}"
ASSETS=$(gh release view nightly --repo "$REPO" --json assets --jq '.assets[].name')
EXE_NAME=$(echo "$ASSETS" | grep -v 'qt6' | grep '\.exe$' | head -1)
ZIP_NAME=$(echo "$ASSETS" | grep -v 'qt6' | grep '\.zip$' | head -1)
MSI_NAME=$(echo "$ASSETS" | grep -v 'qt6' | grep '\.msi$' | head -1 || echo "")
EXE_QT6_NAME=$(echo "$ASSETS" | grep 'qt6' | grep '\.exe$' | head -1 || echo "")
ZIP_QT6_NAME=$(echo "$ASSETS" | grep 'qt6' | grep '\.zip$' | head -1 || echo "")
MSI_QT6_NAME=$(echo "$ASSETS" | grep 'qt6' | grep '\.msi$' | head -1 || echo "")
BASE="https://github.com/$REPO/releases/download/nightly"
INSTALLER_URL="$BASE/$EXE_NAME"
PORTABLE_URL="$BASE/$ZIP_NAME"
MSI_URL=""
[ -n "$MSI_NAME" ] && MSI_URL="$BASE/$MSI_NAME"
INSTALLER_QT6_URL=""
PORTABLE_QT6_URL=""
MSI_QT6_URL=""
[ -n "$EXE_QT6_NAME" ] && INSTALLER_QT6_URL="$BASE/$EXE_QT6_NAME"
[ -n "$ZIP_QT6_NAME" ] && PORTABLE_QT6_URL="$BASE/$ZIP_QT6_NAME"
[ -n "$MSI_QT6_NAME" ] && MSI_QT6_URL="$BASE/$MSI_QT6_NAME"
SHA="${{ github.event.workflow_run.head_sha || github.sha }}"
SHORT="${SHA:0:7}"
DATE=$(date -u '+%Y-%m-%d %H:%M UTC')
RUN_URL="https://github.com/$REPO/actions/runs/${{ github.run_id }}"
RUN_NUMBER="${{ github.run_number }}"
export DATE SHORT REPO SHA RUN_URL RUN_NUMBER
export INSTALLER_URL PORTABLE_URL MSI_URL
export INSTALLER_QT6_URL PORTABLE_QT6_URL MSI_QT6_URL
python3 source/build-aux/generate-page.py
- name: Add .nojekyll
shell: bash
run: touch gh-pages/.nojekyll
- name: Upload GitHub Pages artifact
uses: actions/upload-pages-artifact@v5
with:
path: gh-pages/
- name: Deploy to GitHub Pages
uses: actions/deploy-pages@v4
+187 -90
View File
@@ -16,23 +16,43 @@
include(cmake/hoto_update_cmake_message.cmake)
cmake_minimum_required(VERSION 3.14...3.19 FATAL_ERROR)
cmake_minimum_required(VERSION 3.5...4.2)
project(qelectrotech
VERSION 0.9.0
DESCRIPTION "QET is a CAD/CAE editor focusing on schematics drawing features."
HOMEPAGE_URL "https://qelectrotech.org/"
LANGUAGES CXX)
VERSION 0.100.1
DESCRIPTION "QET is a CAD/CAE editor focusing on schematics drawing features."
HOMEPAGE_URL "https://qelectrotech.org/"
LANGUAGES C CXX)
include(cmake/copyright_message.cmake)
set(QET_DIR ${PROJECT_SOURCE_DIR})
# QT_VERSION_MAJOR is chosen explicitly by whoever configures the build,
# via -DQT_VERSION_MAJOR=5 or -DQT_VERSION_MAJOR=6.
# Default to Qt5 (current stable) when not specified, so existing
# CI/scripts that don't pass this option keep working unchanged.
# This must happen BEFORE add_subdirectory(tests) and the fetch_*.cmake
# includes below, so every subdirectory and every FetchContent dependency
# sees a consistent, already-defined value.
if(NOT DEFINED QT_VERSION_MAJOR)
set(QT_VERSION_MAJOR 5)
endif()
# Some third-party CMake projects we pull in via FetchContent (e.g.
# SingleApplication) don't know about QET_VERSION_MAJOR: they follow Qt's
# own "QT_DEFAULT_MAJOR_VERSION" convention instead, and silently default
# to Qt5 if it isn't set. Propagate our choice so they stay in sync,
# otherwise they can end up linked against a different Qt major version
# than the rest of QET, which breaks AUTOMOC with an
# INTERFACE_QT_MAJOR_VERSION mismatch at generate time.
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (5 or 6)" FORCE)
# Add sub directories
option(PACKAGE_TESTS "Build the tests" ON)
if(PACKAGE_TESTS)
message("Add sub directory tests")
add_subdirectory(tests)
message("Add sub directory tests")
add_subdirectory(tests)
endif()
include(cmake/paths_compilation_installation.cmake)
@@ -46,110 +66,187 @@ include(cmake/fetch_pugixml.cmake)
include(cmake/qet_compilation_vars.cmake)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
SET(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(
QT
NAMES
Qt5
COMPONENTS
${QET_COMPONENTS}
REQUIRED
)
Qt${QT_VERSION_MAJOR}
COMPONENTS
${QET_COMPONENTS}
REQUIRED)
# Qt6 only creates the Qt::GuiPrivate target (used for QPdfEngine::drawHyperlink)
# when the GuiPrivate component is explicitly requested. Qt5 has no such
# component package and creates the target implicitly with Gui, so only
# request it on Qt6 - requesting it on Qt5 fails the whole configure.
if(QT_VERSION_MAJOR GREATER_EQUAL 6)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS GuiPrivate)
endif()
find_package(
Qt${QT_VERSION_MAJOR}
COMPONENTS
${QET_COMPONENTS}
REQUIRED)
find_package(SQLite3 REQUIRED)
# CMake < 4.3 only creates the SQLite::SQLite3 target (no SQLite3::SQLite3
# alias yet), while CMake >= 4.3's bundled FindSQLite3 creates SQLite3::SQLite3
# and deprecates the old name. Add the missing alias ourselves so we can use
# the modern target name everywhere regardless of the CMake version in use
# (this project must keep building on CMake versions below 4.3, e.g. on most
# current Linux distros).
if(NOT TARGET SQLite3::SQLite3 AND TARGET SQLite::SQLite3)
add_library(SQLite3::SQLite3 ALIAS SQLite::SQLite3)
endif()
set(CMAKE_AUTOUIC_SEARCH_PATHS ${QET_DIR}/sources/ui)
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
# The default build only compiles the tracked .ts files to .qm (lrelease).
# Refreshing the .ts from the sources (lupdate) is a developer action behind
# the explicit "update_translations" target below: running lupdate on every
# build rewrote tracked files as a side effect, and under high parallelism
# lupdate rewriting a .ts while lrelease read the same file made the build
# fail with "Premature end of document".
set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION "${QET_DIR}/lang")
qt5_add_translation(QM_FILES ${TS_FILES})
if(QT_VERSION_MAJOR EQUAL 6)
qt6_add_translation(QM_FILES ${TS_FILES})
else()
qt5_add_translation(QM_FILES ${TS_FILES})
endif()
add_custom_target(update_translations
COMMAND $<TARGET_FILE:Qt${QT_VERSION_MAJOR}::lupdate> ${CMAKE_SOURCE_DIR}/sources -ts ${TS_FILES}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Updating .ts files from sources/ (lupdate) - developer target, run explicitly"
VERBATIM
)
# als laatse
include(cmake/define_definitions.cmake)
add_executable(
${PROJECT_NAME}
${QET_RES_FILES}
${QET_SRC_FILES}
${QM_FILES}
${QET_DIR}/qelectrotech.qrc
)
# On Windows, WIN32 sets /SUBSYSTEM:WINDOWS to suppress the console window.
# Qt automatically links qtmain.lib which provides the WinMain entry point,
# so no source code change is needed.
if(WIN32)
add_executable(
${PROJECT_NAME}
WIN32
${QET_RES_FILES}
${QET_SRC_FILES}
${QM_FILES}
${QET_DIR}/qelectrotech.qrc
)
else()
add_executable(
${PROJECT_NAME}
${QET_RES_FILES}
${QET_SRC_FILES}
${QM_FILES}
${QET_DIR}/qelectrotech.qrc
)
endif()
# Optional precompiled headers -- see QET_ENABLE_PCH in
# cmake/developer_options.cmake for what this trades away.
#
# target_precompile_headers() needs CMake 3.16; the project still declares a
# 3.5 minimum, so guard rather than raise it for an opt-in developer feature.
#
# The generator expressions are load-bearing, not decoration: this target also
# compiles the 18 C files of the bundled LZMA decoder
# (sources/import/edz/lzma/*.c), and an unguarded list applies to every
# language in the target, so the Qt headers would reach the C compiler and fail
# with "unknown type name 'namespace'". $<ANGLE-R> is required because a
# literal '>' would terminate the generator expression.
if(QET_ENABLE_PCH)
if(CMAKE_VERSION VERSION_LESS 3.16)
message(WARNING
"QET_ENABLE_PCH needs CMake 3.16 or newer (found ${CMAKE_VERSION}); "
"building without precompiled headers.")
else()
target_precompile_headers(${PROJECT_NAME} PRIVATE
"$<$<COMPILE_LANGUAGE:CXX>:<QtCore/QtCore$<ANGLE-R>>"
"$<$<COMPILE_LANGUAGE:CXX>:<QtGui/QtGui$<ANGLE-R>>"
"$<$<COMPILE_LANGUAGE:CXX>:<QtWidgets/QtWidgets$<ANGLE-R>>"
"$<$<COMPILE_LANGUAGE:CXX>:<QtXml/QtXml$<ANGLE-R>>"
)
message(STATUS "QET_ENABLE_PCH: precompiled headers enabled")
endif()
endif()
target_link_libraries(
${PROJECT_NAME}
PUBLIC
PRIVATE
pugixml::pugixml
SingleApplication::SingleApplication
SQLite::SQLite3
${KF5_PRIVATE_LIBRARIES}
${QET_PRIVATE_LIBRARIES}
)
${PROJECT_NAME}
PUBLIC
PRIVATE
pugixml::pugixml
SingleApplication::SingleApplication
SQLite3::SQLite3
${KF5_PRIVATE_LIBRARIES}
${QET_PRIVATE_LIBRARIES}
)
target_include_directories(
${PROJECT_NAME}
PRIVATE
${QET_DIR}/sources/titleblock
${QET_DIR}/sources/ui
${QET_DIR}/sources/qetgraphicsitem
${QET_DIR}/sources/qetgraphicsitem/ViewItem
${QET_DIR}/sources/qetgraphicsitem/ViewItem/ui
${QET_DIR}/sources/richtext
${QET_DIR}/sources/factory
${QET_DIR}/sources/properties
${QET_DIR}/sources/dvevent
${QET_DIR}/sources/editor
${QET_DIR}/sources/editor/esevent
${QET_DIR}/sources/editor/graphicspart
${QET_DIR}/sources/editor/ui
${QET_DIR}/sources/editor/UndoCommand
${QET_DIR}/sources/undocommand
${QET_DIR}/sources/diagramevent
${QET_DIR}/sources/ElementsCollection
${QET_DIR}/sources/ElementsCollection/ui
${QET_DIR}/sources/autoNum
${QET_DIR}/sources/autoNum/ui
${QET_DIR}/sources/ui/configpage
${QET_DIR}/sources/SearchAndReplace
${QET_DIR}/sources/SearchAndReplace/ui
${QET_DIR}/sources/NameList
${QET_DIR}/sources/NameList/ui
${QET_DIR}/sources/utils
${QET_DIR}/pugixml/src
${QET_DIR}/sources/dataBase
${QET_DIR}/sources/dataBase/ui
${QET_DIR}/sources/factory/ui
${QET_DIR}/sources/print
)
${PROJECT_NAME}
PRIVATE
${QET_DIR}/sources/titleblock
${QET_DIR}/sources/ui
${QET_DIR}/sources/qetgraphicsitem
${QET_DIR}/sources/qetgraphicsitem/ViewItem
${QET_DIR}/sources/qetgraphicsitem/ViewItem/ui
${QET_DIR}/sources/richtext
${QET_DIR}/sources/factory
${QET_DIR}/sources/properties
${QET_DIR}/sources/dvevent
${QET_DIR}/sources/editor
${QET_DIR}/sources/editor/esevent
${QET_DIR}/sources/editor/graphicspart
${QET_DIR}/sources/editor/ui
${QET_DIR}/sources/editor/UndoCommand
${QET_DIR}/sources/undocommand
${QET_DIR}/sources/diagramevent
${QET_DIR}/sources/ElementsCollection
${QET_DIR}/sources/ElementsCollection/ui
${QET_DIR}/sources/autoNum
${QET_DIR}/sources/autoNum/ui
${QET_DIR}/sources/ui/configpage
${QET_DIR}/sources/SearchAndReplace
${QET_DIR}/sources/SearchAndReplace/ui
${QET_DIR}/sources/NameList
${QET_DIR}/sources/NameList/ui
${QET_DIR}/sources/utils
${QET_DIR}/pugixml/src
${QET_DIR}/sources/dataBase
${QET_DIR}/sources/dataBase/ui
${QET_DIR}/sources/factory/ui
${QET_DIR}/sources/print
${QET_DIR}/sources/svg
)
if(NOT BUILD_WITH_KF5)
target_include_directories(
${PROJECT_NAME}
PRIVATE
${QET_DIR}/sources/ui/nokde
)
endif()
install(TARGETS ${PROJECT_NAME})
if (NOT MINGW)
install(DIRECTORY ico/breeze-icons/16x16 DESTINATION ${QET_ICONS_PATH})
install(DIRECTORY ico/breeze-icons/22x22 DESTINATION ${QET_ICONS_PATH})
install(DIRECTORY ico/breeze-icons/32x32 DESTINATION ${QET_ICONS_PATH})
install(DIRECTORY ico/breeze-icons/48x48 DESTINATION ${QET_ICONS_PATH})
install(DIRECTORY ico/breeze-icons/64x64 DESTINATION ${QET_ICONS_PATH})
install(DIRECTORY ico/breeze-icons/128x128 DESTINATION ${QET_ICONS_PATH})
install(DIRECTORY ico/breeze-icons/256x256 DESTINATION ${QET_ICONS_PATH})
install(DIRECTORY elements DESTINATION share/qelectrotech)
install(DIRECTORY examples DESTINATION share/qelectrotech)
install(DIRECTORY titleblocks DESTINATION share/qelectrotech)
install(FILES LICENSE ELEMENTS.LICENSE CREDIT README ChangeLog DESTINATION share/doc/qelectrotech)
install(FILES misc/org.qelectrotech.qelectrotech.desktop DESTINATION share/applications)
install(FILES misc/qelectrotech.xml DESTINATION share/mime/packages)
install(FILES misc/qelectrotech.appdata.xml DESTINATION ${QET_APPDATA_PATH})
install(FILES ${QM_FILES} DESTINATION ${QET_LANG_PATH})
install(DIRECTORY ico/breeze-icons/16x16 DESTINATION ${QET_ICONS_PATH})
install(DIRECTORY ico/breeze-icons/22x22 DESTINATION ${QET_ICONS_PATH})
install(DIRECTORY ico/breeze-icons/32x32 DESTINATION ${QET_ICONS_PATH})
install(DIRECTORY ico/breeze-icons/48x48 DESTINATION ${QET_ICONS_PATH})
install(DIRECTORY ico/breeze-icons/64x64 DESTINATION ${QET_ICONS_PATH})
install(DIRECTORY ico/breeze-icons/128x128 DESTINATION ${QET_ICONS_PATH})
install(DIRECTORY ico/breeze-icons/256x256 DESTINATION ${QET_ICONS_PATH})
install(DIRECTORY elements DESTINATION share/qelectrotech)
install(DIRECTORY examples DESTINATION share/qelectrotech)
install(DIRECTORY titleblocks DESTINATION share/qelectrotech)
install(FILES LICENSE ELEMENTS.LICENSE CREDIT README ChangeLog DESTINATION share/doc/qelectrotech)
if(UNIX AND NOT APPLE)
install(FILES misc/org.qelectrotech.qelectrotech.desktop DESTINATION share/applications)
install(FILES misc/qelectrotech.xml DESTINATION ${QET_MIME_PACKAGE_PATH})
install(FILES misc/qelectrotech.appdata.xml DESTINATION ${QET_APPDATA_PATH})
endif()
install(FILES ${QM_FILES} DESTINATION ${QET_LANG_PATH})
endif()
+805
View File
@@ -1,5 +1,810 @@
# QElectroTech - CHANGELOG
## Version 0.101 (Unreleased)
_Compiled from commit history since 0.100._
## Overview
Since 0.100, development has centered on a new headless command-line workflow, terminal-strip and cross-reference improvements, a wave of crash and stability fixes, and continued packaging/CI modernization (Windows MSI, macOS, Snap). Translation coverage was extended again, notably with new Korean support.
## Highlights / Key Features
- **Headless CLI export**
- New command-line export mode for PDF, PNG and SVG, letting QET projects be rendered without opening the GUI.
- Added `--export-bom`, `--export-nets`, `--export-links`, `--check-elements`, `--info`, `--set-titleblock` and `--resave` CLI options for scripted/batch workflows.
- The editor grid is now disabled in CLI-rendered output, and async crash-recovery backups are disabled in headless mode to avoid a segfault.
- **PDF export improvements**
- Cross-references are now clickable hyperlinks in exported PDFs, jumping directly to the linked element or terminal.
- Wiring list and wire-number list can now be exported to CSV, both from the GUI and the CLI.
- **Diagram duplication**
- Diagrams/folios can now be duplicated along with all their metadata and embedded elements.
- **BOM / Nomenclature**
- Specific elements can now be excluded from the Bill of Materials (Nomenclature).
- **Terminal & cross-reference (Xref) work**
- Continued iteration on a `max_slaves` limit for Master elements (added, tuned, briefly reverted, then re-implemented with proper save/load handling).
- New "potential isolation" option for terminals, and new No/Nc/Common contact types for switch (SW) terminals.
- Cross-reference items gained an option to hide terminal names, fixed terminal-name sorting for power contacts, and fixed a couple of SIGSEGV crashes (including one when dropping a power NC contact on the diagram).
- **Windows installer**
- MSI nightlies now inject the Git revision count into the MSI version's Build field, fixing the long-standing regression where `MajorUpgrade` never triggered because the version string never changed — nightlies had to be uninstalled by hand before.
- The old `Lancer QET.bat` wrapper was removed in favor of handling launch natively in `QElectroTech.wxs`.
- NSIS installer updated to the 3.x line, with a bundled Fonts directory and corrected language tables.
## Detailed Changes
### Editor & UX
- Element panel and context-menu/link-button logic refactored for clarity; `MasterElement::isFull` and related conditional checks simplified.
- Font-size spin boxes in the text editor now enforce a 4pt minimum, fixing a SIGSEGV that occurred when the value reached 0.
- Text position (`PartText`) now stays stable across save/reopen when the font changes.
- Element editor: no more spurious warning on save for front-view elements without terminals; "Save As" field is now correctly labelled as a file name rather than an element name.
### Bug Fixes
- Fixed a Unicode-path bug in pugixml on Windows: collection or element paths containing accented characters (é, ü, ñ, …) silently failed to load because the narrow-char API was used instead of the wide-char one.
- Fixed center-alignment not being restored when reloading a saved table configuration (config serialised `AlignHCenter`, but the loader compared against `AlignCenter`).
- Fixed a `TerminalData` memory leak in the `Terminal` destructor (confirmed via ASan across several call sites).
- Fixed several uninitialised-value bugs found with Valgrind, and a couple of thread-unsafe `QStandardPaths` calls in the data/config directory lookup.
- Fixed a crash when destroying a `QETProject` before its asynchronous backup had finished.
- Fixed regional system locale sometimes loading the wrong translation.
- Fixed dynamic element text shifting/jumping when duplicating diagrams, and a wiring-list filter/timing issue.
- Various smaller fixes: a first-click spurious element move right after selecting a tool, an -Wreorder compiler warning, and several translation corrections (German, French, Chinese).
### macOS
- Fixed the long-standing bug (open since 2022) where double-clicking a `.qet`, `.elmt` or `.titleblock` file launched the app without opening the file — the file-open event is now buffered during a cold launch, before `QETApp` exists, and routed correctly once the app is already running.
- Continued work on the black-screen-after-PDF-export regression on Apple Silicon, tied to the Metal compositor's async timing.
### Build, Packaging & CI
- Windows nightly build pipeline continued its migration to GitHub Actions; test builds for the VS2026 toolchain were run against the same pipeline.
- Snap package now stages `libxcb-cursor0`, fixing an xcb plugin load failure on Ubuntu 24.04.
- CMake install rules for Linux-only targets are now correctly guarded against macOS.
- `qet-tb-generator` in the Snap build is now pinned to a fixed upstream tag instead of floating.
### Internationalization & Translations
- Expanded Korean (ko) support: UI strings, comments, generic names, installer language strings, translator credits and a new Korean man page.
- Additional updates to German, French, Chinese (zh), and other translation files, including strings related to the new duplicate-diagram and macro features.
## Developer & Contributor Notes
- Terminal strip and element data model continued to evolve incrementally (multiple passes on `max_slaves` handling, access modifiers, and storage), reflecting ongoing refinement of the terminal editor introduced in 0.100.
- PDF link handling was factored out into a shared `pdf_links.{cpp,h}` helper, reused by both the interactive export and the new CLI export path.
## Upgrade / Migration Notes
- No project file format changes are expected to break compatibility with 0.100 projects.
- Users relying on Windows nightlies should note that upgrades will now uninstall the previous nightly automatically, instead of requiring a manual uninstall first.
## How to get help / report bugs
- Use the project issue tracker to report regressions or new bugs, with detailed reproduction steps and example `.qet` files where possible.
- Include the output of Help → About (application version and Git revision) when reporting build/packaging issues.
## Version 0.100
_Compiled from provided commit logs and contributor notes._
## Overview
This release (v0.100) collects a large set of new features, UI and editor improvements, element and symbol updates, build and packaging fixes, dependency upgrades, translations, and a broad set of bug fixes and stability improvements. It is intended as a stable, feature-rich stepping stone toward the next major workflows for symbol editing, terminal/strip handling and export improvements.
## Highlights / Key Features
- **Terminal Strip / Terminal Strip Editor**
- New TerminalStripItem type and related editor workflow added.
- Support for drawing and displaying terminal bridges and links in the editor.
- Full editor support (layout preview, save/load into .qet files) and undo support for terminal strip operations.
- **New Example Projects**
- Several new example projects included, notably photovoltaic (PV) examples to help users getting started with PV designs.
- **Improved Export / Print Handling**
- Export limits adjusted and better handling of QPainter/printing boundaries to avoid export artefacts and out-of-range errors.
- Export dialog updated to allow larger pixel limits where appropriate.
- **Element & Symbol Additions**
- New elements and symbols added (including vendor-specific elements and additional sensors/Arduino components).
- Improvements to element import & metadata handling.
- **Packaging & Multi-arch Support**
- Updated packaging scripts for AppImage, Flatpak, Snap and macOS deployment. Improved aarch64/arm64 support.
## Detailed Changes
### Editor & UX
- Better handling for **rotation, flip and mirror** operations in the element editor:
- Primitives and text rotation behavior improved.
- Finer rotation increments and predictable text orientation after flips/rotations.
- **Wiring and conductor behavior**:
- More robust creation and movement of wires and conductor bundles.
- Improved text attachment and positioning for wires and improved stability while editing complex conductor networks.
- **TerminalStrip editor**: see Highlights - includes drawing, preview, layout editing, persistent storage in the project file and undo support.
- **Element Editor & Symbol Trim/Sort**:
- Improved trimming/normalization of element metadata.
- Better sorting and error handling for element imports (DXF and other formats).
- Small UI improvements: About dialog updates, autosave spinbox ranges, improved tooltips and mouse-hover help for dynamic texts.
### New & Updated Elements
- New elements added for industrial and automation workflows (including Siemens-related elements, logic elements, sensors and Arduino components).
- Symbol library additions and cleanup; improved defaults for newly added symbols.
- Element meta-data cleanup: article numbers, descriptions, and manufacturer fields were normalized and trimmed on import.
### Export / Printing / PDF
- Adjusted internal export limits to avoid hitting QPainter size restrictions; users can now export larger, high-resolution images/prints in more cases.
- Better handling of page sizes and printer-related geometry using QRectF improvements.
- PDF export improvements to increase reliability of exported vector content.
### Build, Dependencies, Packaging
- Upgrades of core test and build dependencies:
- Catch2 upgraded to v2.13.10.
- googletest upgraded to v1.17.0.
- CMake fixes and i18n handling corrected for nl_BE and other locales.
- Packaging scripts updated across platforms (AppImage/Flatpak/Snap/macOS deploy) including fixes for aarch64/arm64.
- Submodule updates (e.g., qelectrotech-elements, pugixml, SingleApplication) synchronized where needed.
### Internationalization & Translations
- Large translation updates across many languages: German (DE), French (FR), Dutch (NL, including nl_BE), Swedish (SV), Italian (IT), Polish (PL), Portuguese-BR (PT-BR), Serbian (SR), Chinese (Simplified) and others.
- Fixes and corrections for many UI strings and localized resources.
### Tests, QA & Logging
- Improved logging and machine/config-path reporting; Git revision display refined to only show a revision when available.
- Unit test updates and fixes to align with updated testing frameworks.
## Bug Fixes (selected)
- Fixed crashes and various null pointer access issues discovered by static and dynamic testing.
- Resolved multiple reported bugs that caused build failures on some platforms (FTBFS fixes for macOS and others).
- Fixed issues with automatic conductor/strand numbering in several edge cases (referenced Bug 293 in the commit logs).
- Resolved text/summary headline issues in the German-language summary generator.
- Fixes for a number of visually incorrect renderings and layout corner-cases during element transformation (rotate/flip/mirror).
- Fixed issues that affected export sizes and caused export artifacts (referenced fixes for bug IDs around #329/#330 in commit notes).
## Developer & Contributor Notes
- Reworked parts of the codebase to use QRectF consistently for better compatibility with QPrinter and export pipelines.
- Code-style cleanups and comment improvements applied throughout the project.
- Expanded test coverage and dependency refresh to keep CI builds stable.
## Contributors (selected)
Thanks to the many contributors who made this release possible. Selected contributors mentioned in the commit logs include:
- Laurent Trinques
- joshua
- plc-user
- Achim
- Pascal Sander
- Andre Rummler
- Magnus Hellströmer
- Martin Marmsoler
- Remi Collet
(See the full commit history for the complete contributor list.)
## Upgrade / Migration Notes
- No database or project file format breaking changes were reported in the provided logs. As always, back up projects before opening them with a new version.
- If you rely on custom element libraries or third-party submodules, verify submodule synchronization after upgrading.
- If you are using custom packaging pipelines, review the updated packaging scripts for any changes required by new dependency versions, especially on aarch64/arm64.
## Known Issues & Limitations
- Some very large exports may still be limited by platform-specific rendering restrictions; the export dialog now allows larger pixel limits but extreme sizes may still hit system-level limits.
- If you use niche element-import workflows (DXF → element import), occasionally metadata normalization may alter whitespace/trim rules - verify newly imported elements in the element editor.
## How to get help / report bugs
- Use the project issue tracker (see repository) to report regressions or new bugs with detailed reproduction steps and example .qet files where possible.
- Include the output of Help → About (application version and Git revision) when reporting build/packaging issues.
====== ChangeLog from 0.8 to 0.9 ======
*Diagram editor :
Improved QElectroTech speed (launch qet, open project, function)
A drop-down list has been added to the toolbar to change the size of the resize handles.
*Element Editor:
The "keep visual rotation" property of element texts is editable from the element editor.
Thanks to the work of antonioaja it is now possible to import a dxf directly from the element editor in a completely transparent way for the user.
In the background QElectroTech uses the dxf2elmt software. https://qelectrotech.org/forum/viewtopic.php?id=2265 https://github.com/antonioaja/dxf2elmt
Improved responsiveness when multiple shapes are selected or deleted, especially when working on a large converted DXF element.
https://qelectrotech.org/forum/viewtopic.php?pid=16612#p16612
*Other:
Add a "side project" tab in the "about" window.
In the general QElectroTech configuration, a drop down list allows to choose the scaling method for hdpi screens.
Allow open polygons (i.e. polylines) when saving in dxf format.
https://qelectrotech.org/forum/viewtopic.php?pid=16611#p16611
Added 'Other' option for slave device contact type.
https://github.com/qelectrotech/qelectrotech-source-mirror/pull/222
https://qelectrotech.org/forum/viewtopic.php?id=2264
*Logs:
Added a QElapsedTimer to calculate the time used to reload the item collection.
Improved QElapsedTimer to calculate the time used to reload the item collection in seconds instead of ms.
Added Linux pc.gpu.RAM information, but requires mesa-utils dependency on the Linux OS.
Added information about mounted disk volumes.
Added CPU architecture for which Qt was compiled in the aboutqetdialog widget and in the logs.
Added MSVC support to MachineInfo.
Added RAM information on Windows of available RAM.
Added QElectroTech version to the log file.
* Elements collection :
Improve collection 8274 elements in 1097 categories (i.e. 9371 files).
* macOS :
Fix sqlite3 database export on macOS, now I use macports with Digikam scripts instead off Homebrew Package Manager.
See: https://invent.kde.org/graphics/digikam/-/tree/master/project/bundles/macports
*Bug fix:
see: https://git.tuxfamily.org/qet/qet.git/log/?h=0.8.1
====== ChangeLog from 0.7 to 0.8 ======
* Moving the main QET repository SVN trunk to GIT.
* Add a mirror of the main QET repository, which is synced to hosted at tuxfamily.org to Github.
* Doxyfile update add QCH file for doxygen-generated docs using qhelpgenerator and then import them into Qt Creator.
* Improve portability of Doxyfile (Simon).
* Fix deprecated warnings (Simon).
* Wrap code for better readability (Simon, Damian).
* Elements collection :
Improve collection 7378 elements in 1092 categories
Thanks Sebastien, Dinozzo, Simon, Rafał, Arnaud, Bisku, Fernando, Riesermauf, Alexis, david, Plc-user, Christophe, Michele, Galexis, Jevgenijs, Gabor,
Benoit Michel, Franck, Ludovic, Cyrille, ossau2mation, Mitzot, Edgar, Nuri, Friskolon, Baboune41, Wiktor, shateks, Dik, Shateks, Marcin
* Diagram editor :
* The export of the nomenclature to csv file has been completely rewritten :
It is now possible to choose which information to export as well as the order in which it should be displayed.
An option allows filtering by type of element: all, terminal block, button / switch.
Another option allows to display or not the column headers in the csv file.
With these options, it is possible to create a nomenclature, an order list, but also for printing labels: list of terminals and list of buttons / switches.
It is possible to save / load a configuration easily.
Finally, the work being done by an SQLite database, a text field allows the user to create his own SQL query.
* Add Conductors numbering to csv file.
* Add new summary table.
* add BOM creation dialog :
nomenclature is now integrated into the project (accompanied by several parameters in order to be customizable).
Tables can have a name.
Tables an be added to any folio.
Font margin and alignment (right center left) separately adjustable for headers and table cells.
Position size and number of lines is adjustable.
Possibility of linking several tables together, especially when the entire nomenclature cannot be contained in a folio.
Automatic adjustment of the size of the table in relation to the folio.
Option to apply the geometry of an array to all the array linked to it, so that everything is homogeneous.
Save / load table configuration and content to make creation faster.
Option to automatically adjust the table to the folio.
Option to automatically add new tables in new folios if the nomenclature cannot be contained in 1 to N folios / tables.
* Table content:
Fully customizable, you display what you want or want (info to display in the desired order, filter on type of element, filter on content of the info "contains, not contains, not empty etc ..." ).
The content being generated from a sqlite database, you can write your own request.
* Loading of element collections is now faster (thanks to the pugixml parser)
* The loading of collections of elements no longer freezes QElectroTech.
* The appearance and disappearance of the search / replace menu is animated.
* Fix wrong position of slave xref after open a saved project
* Add font color of the conductors (Simon).
* Add section and color properties for wires.
* config dialog :
* Add QScrollArea to configdialog and resize to max_screen (Simon).
* Add gui resize depending on screen size (Simon).
* Add Screen info user (Simon).
* Mod ScrollArea on demand (Simon).
* Element editor:
* Polygon editing widget, when you click on a point in the list, the corresponding point changes color in order to better visualize what you are doing.
On this same list, a right click opens a contextual menu allowing to delete the selected point or inserted a point after the selected one.
When holding the ctrl + directional arrow key, the selected parts move by 0.1 instead of 1 point.
* with Ctrl key you can moving by keyboard primitives selected by 0.1 point instead by 1 points, added the same feature for moving the selected aera.
* Added 140 web standard colors In Element editor (Arnaud).
* Add multiedit feature (martin).
* Add terminals uuid for next features (martin).
* Windows :
* Fix bad fonts rendering if Qt version >= 5.13.1.
See : https://bugreports.qt.io/browse/QTBUG-83161
* Fix backup file on windows
For unknown reason KautoSaveFile don't write the file on Windows if file
is open in another part of the code.
No error is returned and use the method :
qint64 QIODevice::write(const QByteArray &byteArray) return the good
number of bytes written but the real file stay empty.
Probably the problem don't come from KautoSaveFile but QFileDevice or
QIODevice on windows.
The fix consist to open the file just before write on it and close it
just after.
* writeToFile on a other Thread to improve this for windows performance (Simon).
* macOS :
* Add Fusion style and fix tilesets tab bar size
* Enable mouse wheel on tilesets tab bar with command keyboard, thanks Giovanni.
(removed by Qt upstream) https://codereview.qt-project.org/gitweb?p=qt/qtbase.git;a=commitdiff;h=ea47d152b35158ba07a55d009f57df0e4c2a048f;hp=08cc9b9991ae9ab51bed5b857b6257401401ff6f
* Add Snap packages :
* thanks Max for help.
https://snapcraft.io/qelectrotech
See: https://qelectrotech.org/forum/viewtopic.php?id=1426
* Use kde-neon extension
The kde-neon extension reduces our boilerplate in snapcraft.yaml
and reduces the snap size considerably, as we do not need to ship
any library that is already included in the kde-frameworks-5-core18
snap itself.
This limits qelectrotech builds to the architectures supported
by kde-frameworks-5-core18, i.e. only amd64.
https://git.tuxfamily.org/qet/qet.git/log/?qt=grep&q=snap
* Add Flatpak packages :
* thanks Mathieu for help.
See: https://github.com/qelectrotech/qelectrotech-source-mirror/pull/18
* remove the file name extension added
automatically to the saved files even if the user sets an unknown file
extension for the program.
https://git.tuxfamily.org/qet/qet.git/log/?qt=grep&q=flatpak
====== ChangeLog from 0.6 to 0.7 ======
* Element editor :
* Element informations (manufacturer, reference etc...) can be created directly from the element editor. For that go to the widget "Element Property"
* It is no longer required to have a text field, for save the edited element.
* Improve the behavior with the arrow keys (depending to the current selection (nothing / one / several).
* Context menu display only enabled actions.
* Added new feature -> alignment.
* Alignment of text field can be edited.
* Added two new actions in context menu for insert or remove point of a selected polygon.
* Rectangle can have rounded corner.
* Polyline: finish the creation of polyline with the last point at the same position of the first point, close the polyline.
* Diagram editor :
* Conductors can now be drawn with two colors.
* Improve High-DPI support on Windows and Linux plateform.
* The code for the resize handles has been almost completely redesigned.
* Dissociate fonts policy and size for independent text item and for summarry pages (foliolist), added a 2 button in config page for open Qfontdialog widget and choose policy for independent text item.
* Add in config page a Qfontdialog widget for change dynamic text size, font family, orientation angle and text length, by default.
* Basic shape add new CustomDashLine style with Dash Pattern (<< 10 << 10 );
* It is now possible to add a text field to an element directly from the diagram.
* Element text item with are now converted to dynamic element text item.
* Element editor, part text can't be edited directly.
* User can export / import the configuration of the texts and texts group of an element.
* Context menu display only enabled actions.
* Added new action in the context menu, multiple paste, check box for autonum the pasted element.
* Multipaste -> improve the conductor autonum, conductors are numerated from top to bottom, and left to right.
* Text of conductor can be placed at top/bottom/left/right of conductor, and text never overlaps the conductor.
* Function for search conductor at the same potential.
When the search function is searching in a terminal element, they search only for the first terminal found, no matter if the terminal element have more than two terminals.
So the list of conductors at the same potential is missing some conductors.
This commit fix it, now the search function search for every terminals of a terminal element.
* When remove an element with several conductors connected to the same terminal, the electrical potential is partially or totally destroyed.
This commit fix it : When element is removed one or several conductors are created (if needed) to conserve the electrical potential.
* Added new feature -> alignment.
* Alignment of text field can be edited.
* Added new context menu action "group the selected texts".
* Widget used to edit text item group can edit the pos of the group.
* Element text item group can now be framed.
* Added two new actions in context menu for insert or remove point of a selected polygon.
* QETshapeItem rectangle can have rounded corner.
* Add in config the possibility to start the numbering of the columns of titleblocks at 0.
* Add new function Search and replace widget Crtl +F
* Diagram properties, Element properties, Independent text item can be changed (and mass changed) through the search and replace widget.
* Added 4 tools for edit the depth (Z value) of items.
* Element panel : elements can be searched by their name but also with by all their informations.
* New free selection style.
* Diagram editor : dock used to edit the shape item, can now edit several items in the same time.
* Dynamic element text item : The font of the dynamic texts can be individually be setted.
* Adding or revoming diagram set project to modified
* When user cleanning an project, set project to modified
* Add a shortcut "Ctrl+Shift+P" to quickly open the dialog used for create the auto numbering rules.
* Add missing StatusTip of some QAction
* When user add a polygon, a message in statusBar show how to finish this shape
* Polyline: finish the creation of polyline with the last point at the same position of the first point, close the polyline.
* Plug-in : Add StatusTip instruction for install and launching DXF plugin depending on the operating system
* when plugin qet-tb-generator"generate terminal blocks and connectors" isn't installed show an QMessageBox instruction now depending on the operating system for install it
Add in QMessageBox url encoding/decoding for easy download packages
* Elementspanelwidget: adds keyboard shortcuts to quickly move up, down,or move the targeted folio to the beginning of the project
F3 MoveUp
F4 MoveDown
F5 MoveUpTop
* Title block editor :
Added new title block variables %projectpath, %projectfilename, %projecttitle, previous-folio-num and next-folio-num
"%saveddate, %savedtime, %savedfilename and %savedfilepath" they variables should be updated after file save or save as dialog is confirmed,
before file is saved.
* NameList widget : add a combo box for easily paste texts, like the variables for title block.
* The font of the dynamic text field can be edited.
* The font of the static text field can be edited.
* The color of the static text field can be edited.
* Improve for new qet_tb_generator plug-in : added the full path of the current project as
an argument when calling the plug-in from QET if a project is open.
If not a file dialog is showed to select the QET project.
* QET create a backup file, use to restore the project when a crash occur.
* Use KAutoSaveFile for the backup system, instead of home made function.
* Use of QSAveFile instead a QFile.
* User can enable and edit autosave timer.
* let user define the file system path of the common,custom elements collections, and custom title blocks template.
* QetGraphicsItem, remove the function applyRotation and rotateBy, and use instead the native function of QGraphicsItem : setRotation
* Conductor is an herited class of QGraphicsObject, instead of QObject and QGraphicsPathItem
* Clean (and reduce the size) the class QETDiagramEditor, mostly by replacing the connection syntax "signal -> slot" by "signal -> lambda".
* Replace deprecated QMatrix by QTransform.
* DXF export : fix some double items in dxf file.
* DXF export : add some colors for basic shapes inside dxf.
* Bug fix :
* Fix compilation warning (clang and gcc).
* Fix element text item alignment work well when text rotation != 0.
* Fix crash when the properties of a element text item group
* Fix crash occurred by the conductor and shape "ghost".
* Fix element text alignment work also when font size change.
* fix :
1- When open a .qet by double click on it, QET ask user for open the backup file, of this .qet.
2- On windows, if user open a project from the recent files menu, when close QET the file is deleted user lose her work.
clear the element texts when paste an element with the option "Do not store the labels of items in the copy paste" enabled.
* elements can't be moved up and left, when there rotation are different than 0.
* minor fix : slave link dialog doesn't display the good label, when the label of master element is build from formula.
* Fix : in some condition, dynamic text are not at the same position when open a project.
* On windows when user drag and drop an element from the common elements collection to the custom elements collection,
the element file stay in read only mode, and so user can't save the element
* Improvement : minimize the unwanted gap of the top right folio of the view (see https://qelectrotech.org/forum/viewtopic.php?pid=9379#p9379)
* Fix: bug 168
* Fix : when create multiple conductors with the free hand selection, the checking of existing potentiel don't search trought a folio report.
* Fix: DXF export.
* Minor fix : remove from the element information the html hexadecimal and decimal characters of line feed and carriage return.
* fix : in the diagram editor, when we select several shapes at the same time, the properties widget only apply the change to one shape.
* Bug fix : when user load a project which contains summary pages, project was marked modified (summary was created on the fly and moved from the end on second
position), now the project is no longer marked as amended when user have one or multiples summary pages when loading this project.
* Static text of element are now exported to dxf
* Fix Static text size of element exported to dxf
* Improvement : minimize the unwanted gap of the top right folio of the view
* Fix : when create multiple conductors with the free hand selection, the checking of existing potentiel don't search trought a folio report.
* Don't display gui when qet is launched with specific argument
====== ChangeLog from 0.5 to 0.6 ======
In the official collection, there are now 4106 elements, and 539 categoris for a total of 4645 files
* Improved performance, added multithreading to speed up the loading of items when launching QET.
* RAM consumption has been considerably reduced.
* New "collections" panel.
* Automatic numberings (autonum), Variables and prefix.
* Folio generator
* Management Policy
* New thickness properties for conductors.
* The thickness of the lines of all basic shapes (lines, rectangles, ellipses, polygons) can be changed from 0,2px to 50,0px.
* The color of lines and fillings of basic shapes can be choosed from a color palette or set with a html color code.
* Added Copy/paste from another project.
* Online documentation and links to download the latest packages of the version under development for Windows and MacOS are available directly from the software.
* Resetting the layout of the summary pages.
* In the panel left split the view into a several docks.
* High-DPI support (Qt 5.6.0)
* new python plugin to generate terminal block.
* Windows packages are now created on a Debian operating system using cross-compilation and targeted to make executable binary files for these operating systems.
This technical evolution allows a significant time saving during the creation of the packages. And we can provide also in the same time Windows XP and Vista packages by cross-compil with Qt 5.7.1 environment
* The Mac OS X executable binary files are created on a virtual environment, moved compiler to latest LLVM clan version, improved dmg packages with added Info.plist.
* An annoying memory leak has been found and solved. Afters hours of use, some users noticed that the RAM consumption growed steadily, up to 10GB or more. This problem is now solved.
====== ChangeLog from 0.4 to 0.5 ======
In the official collection, there are now 2625 elements, and 418 catégoris for a total of 3043 files.
* Port to Qt 5 framework
* New QSettings native format for config files.
* In the diagram editor, the grid is not displayed by default outside the diagram, the minimum zoom is blocked. A button allows you to un-validate this operation.
* It is now possible to put the tittle block on the right vertical mode.
* The default tittle block can be defined for the next folios of the project.
* The summary now takes the font set in the QElectroTech.conf
* The floating dock is now operational, variables, actions are taken into account on the fly.
* A transformation tool transforms quickly and finely each primitive by handles.
* Add UUID tag for element XML.
* The database enables faster loading a large number of managing symbols in tables changes pixmaps collections, it no longer compares the modification date of the files but their use UUID attributes to update the cache .
* In terms of basic shapes, the transform tool works directly on vectors, it replaces the reduction tool / enlargement that has just been deleted as unnecessary.
* Improve Undo command by QPropertyUndoCommand class.
====== ChangeLog from 0.3 to 0.4 ======
In the official collection, there are now 2298 elements, and 376 catégoris for a total of 2674 files.
* We have removed the flag '-fno-ipa-sra "This settled the compilation problems on Mac OS X and FreeBSD clang.
* The official collection has been redesigned, through the work of Nuri a new structure is in place.
* A menu has been added, allowing you to change the application language.
* we added a summary creation tool.
* Added button "export the nomenclature" transforms data from diagrams to CSV file for spreadsheet.
Arun wrote a detailed manual in English.
* New tools have been added, they can create mechanical connections and draw cabinets, desks, junction boxes, or areas on the schematic (line tool, rectangle, ellipse, polygon type: respect for style dashes).
* An aid in positioning cross, drawing, was added.
* The locked state images and basic forms (basic shapes) is now stored in the project.
* The "control" during the movement of an element, text field disables snapping to the grid, for free positioning.
It is now possible to choose the background folios in white or gray.
* Add supports trackpad gestures (multitouch).
The dates of the cartridges are now using the short system date and date format according to the language detected setting in the OS.
We take advantage of the transition to standard C ++ 11, and a big cleanup in the code was done.
* The undo action or redo the undo stack are now animated graphically.
When the action save, save as, the status bar displays the name and path of the backup job.
Qet is now able to come to load a style sheet (stylesheet) directly from the conf directory.
* A DXF export has been added, the entire project folios can be exported in this format.
* Added reports folio, Cross references.
* Added a variable font size for text of conductors.
* Added new properties to all conductors at the same potential, even through referrals.
* When several conductors have the same value potential equalization, it is not useful to display on all conductors.
* Added button to activates the automatic connection of the conductors of the element when moving it.
* Numbering rules are now available for the entire project.
Qet detects the Windows version and applies the appropriate graphic style, depending on the version of Windows.
====== ChangeLog from 0.3 rc to 0.3 ======
First, the collection of symbols has made a big step forward, with about 1560 new elements.
There are now symbols for pneumatics, hydraulics, process, solar, cold, etc. Considerable effort has been done to organize the collection in a better way.
We hope that the new organisation is clearer for all. We would like to thank all the contributors who send us symbols.
=====-Element Editor: =====
Considerable work has be done to replace the manual defining zone of the symbol, aka hotspot.And fix bugs, It is now automatic. You do not have to care about it anymore.
Primary colors have been added for the drawing shapes.
A contextual menu (right click) has been added. So, you can now work more quickly with symbols. It is also more user-friendly.
====== ChangeLog from v0.3 rc ======
=====-Element Editor: =====
* Replacing checkboxes with lists of colors.
* Removed the manual hotspot, it is now automatic and you do not have to worry.
Officially Collection: a large classification work on the structure was realized. It should be clear to everyone.
The collection is enriched with 1711 items in 286 categories (ie 1997 files)
=====-Schema Editor:=====
* Added import image, image rotation, image resizing and saving the file in the project.
(Double click on the image called a widget and cursor that reduce or enlarge the selected image.)
NB: Following the "edit image" entry will also be added in the right click menu.
* F5 keyboard shortcut can recharge symbol collections.
Some bugs have been resolved, and the translation status continues to grow.
======ChangeLog from v0.3 beta ======
Two more items for the changelog:
* In the official collection, there are now 1672 elements and 256 categoris for a total of 1928 files. In version 0.3 alpha, there were 1465 elements and 233 categories, while version 0.22 had153 elements and 51 categories.
*Progress in the translation (see http://qelectrotech.org/wiki/doc/translation/stats for current state)
* Functions (edit element and find in panel) have been moved to the context
Here is the changelog, for version 0.3 beta:
* Functions (edit element and find in panel) have been moved to the context menu, that can be accessed with right click. This is more user friendly.
* Refresh of categories when an element is moved.
* DateNow button added in the "Diagram property" dialog.
* Dotted lines can now been added between conductors.
* Rich text can be added to the diagram text fields.
[screenshot]
* HTML WYSIWYG editor for rich text: bold, italic, underlined, font size from 6 to 72 pixels, font colour, etc.
* You can change between the two modes(Selection mode <-> View mode) with the scroll button.
* Symbol editor: focus on the new value for language, languages sorted in alphabetical order.
* Added a widget that reflects the loading of a big project.
* Automated numbering of conductors according to your rules. See note from Joshua http://qelectrotech.org/wiki/doc/autonum
* Added a dialog to automatically rotate the text if the associated conductor is vertical or horizontal. Parameters are saved in qelectrotech.conf
* Added basic colours on the tools for lines and for the filling of the primitives, and also for the style line and point in the element editor.
* Added several protection to prevent from saving an element if one of its primitive is beyond the hotspot.
====== ChangeLog from 0.22 to 0.3a ======
===== Application =====
Elements collection: QElectroTech now provides 1465 elements within 233 categories (0.22 provided 153 elements within 51 categories). Most elements are related to electricity though some relate to chillers, solar, hydraulic and pneumatic engineering.
A new kind of collections appeared to store title block templates; as for elements, there is a distinction between common (system-wide) templates and custom (user-wide) templates.
Translations:
English, Spanish, French, Portuguese and Czech translations have been maintained.
Russian translations have been removed because they are not maintained anymore.
Polish, German, Italian, Arabic and Croatian translations have been added.
Following translation to Arabic, some work was done to improve Right-To-Left languages support.
Elements names are fully translated to English, French, Czech and Polish.
Main windows: added a “What's this?” action.
QElectroTech now handles *.titleblock files.
===== Diagram editor =====
It is now possible to move and rotate all texts on a diagram : element texts, conductor texts and independent texts.
When moving a text related to an electrical element, this element is highlighted.
Texts related to a conductor cannot be moved too far away from it.
It is now possible to create diagrams with more than 100 rows/columns.
Elements panel:
During a drag and drop operation, the hovered item is now expanded after a short time not moving the mouse.
Items are now expanded/collapsed by a double click.
Common, custom and embedded collections of title block templates are displayed within the elements panel.
Elements previews and names are now cached into a SQLite database stored in the user configuration directory, thus speeding up the elements panel (re)loading
The elements panel now displays the folio index before each diagram title.
UI consistency: renamed “Import element” to “Open an element file”, separated this action from those related to the current selection, and ensured elements-related actions are disabled when selecting a project/diagram/title block template.
Freshly integrated elements are now highlighted in the elements panel this behaviour can be disabled though.
When clearing the search field, the panel state is restored to its previous state.
Title blocks are now rendered using templates:
For each diagram, users can choose the template to be used in the diagram properties.
They may also drag and drop it from the elements panel to the diagram.
Title block templates are always integrated within the parent project.
Fixed a bug in the print preview dialog.
Added a F2 shortcut for the widget “Edit the color of the given conductor”.
As elements, diagrams now have a “version” attribute for compatibility purposes.
Better handling of file opening for documents saved with newer versions of QElectroTech.
Diagram loading: removed an optimization that could lead to conductors not being loaded when several terminals share the same coordinates.
Users may now enter visualisation mode by pressing Ctrl and Shift.
Printing: when printing diagrams with no title block, use the space left by the title block.
Added a few status and “What's this?” tips.
Got rid of the green icon used for projects, changed a few other icons.
===== Element editor =====
Both static and dynamic texts can now be rotated
Added “dotted” line style
Added white color for texts
Newly added parts are placed above existing ones.
===== Title block template editor =====
A third kind of editor was implemented so users can create their own title block templates:
It allows users to customize the layout and content of cells that constitute the title block.
Cells can be merged and splitted.
Their width can be fixed, relative to the total width or relative to the remaining widths.
Their height is a simple fixed length.
They contain either a logo (be it in SVG or a usual bitmap format) or some text.
The text value is optionally preceded by a label.
As other texts within QElectroTech, labels and texts can be translated to other languages.
Texts and labels may contain variables (e.g. %company-name); these variables are replaced by real world values once the template is applied to a diagram.
Those real-world values can be set among the diagram properties.
====== Changelog 0.11 -> 0.2 ======
À partir de la version 0.2, QElectroTech est disponible en français, anglais, mais aussi :
* en espagnol, grâce aux traductions de Youssef ;
* en russe, grâce aux traductions de Yuriy ;
* en portugais, grâce aux traductions de José.
L'application utilise désormais le thème d'icônes Oxygen, réalisé par Nuno Pinheiro pour le projet KDE.
===== Notion de fichier projet =====
Un fichier .qet peut désormais contenir zéro, un ou plusieurs schémas électriques. Les éléments composant ces schémas sont embarqués dans le fichier projet au moment où ils sont posés sur un schéma. Le panel d'éléments affiche donc désormais :
* les projets ouverts, avec, sous chaque projet :
* les schémas de ce projet,
* la collection embarquée du projet (catégories et éléments utilisés dans les schémas)
* la collection commune fournie par QET,
* et la collection personnelle de l'utilisateur.
===== Éditeur de schémas =====
* Il est désormais possible de déplacer et copier les catégories et éléments par simple glisser-déposer (drag'n drop) dans le panel d'éléments.
* La collection embarquée est manipulable au même titre que la collection utilisateur. Les éléments inutilisés dans le projet apparaissent sur fond rouge et un dialogue permet de les purger rapidement.
* Chaque projet embarque également (au niveau de ses propriétés) les paramétrages par défaut pour les nouveaux schémas, cartouches et conducteurs.
* Il est possible de changer l'ordre des schémas dans le projet en déplaçant les onglets qui les représente. Dans le champ "Folio" des cartouches, on peut se référer à la position du schéma courant ou au nombre total de schémas dans le projet en écrivant respectivement %id et %total.
* Lors du chargement d'un fichier .qet, si des éléments ne sont pas trouvés, ils sont remplacés par un élément "fantôme", ce qui évite de perdre certaines informations lors de l'enregistrement du fichier.
* Le rendu avec un zoom réduit a été amélioré.
* Enfin, le logiciel gère l'ouverture en lecture seule d'un fichier projet.
==== Impression et export ====
À partir de la version 0.2, QElectroTech :
* propose d'utiliser une imprimante réelle ou bien de générer un document PDF ou PostScript, et ce sous Windows comme sous X11.
* génère un aperçu avant l'impression d'un projet. Cet aperçu permet de choisir les options d'impression mais également les schémas à imprimer ou non.
À noter toutefois une limitation pour les impressions PDF/PS sous Windows : le dialogue de mise en page, permettant de spécifier le format du papier ainsi que ses marges, n'est pas disponible.
Le dialogue "Exporter" (pour générer un fichier image d'un schéma) a également été refait dans l'optique d'un export simultané de tous les schémas du projet.
===== Éditeur d'éléments =====
* Lorsque l'on dessine une ligne dans l'éditeur d'éléments, il est possible de choisir un embout différent pour chaque extrémité, comme par exemple une flèche, un cercle, un carré ou, tout simplement, un bout de ligne normal.
* La forme "Rectangle" a été ajoutée.
* On peut enregistrer un élément en désignant un fichier (= comportement en 0.11) ou bien en choisissant un élément cible dans une liste reprenant l'arborescence du panel d'éléments.
* Si l'on maintient la touche Shift lorsque l'on ajoute une partie (droite, cercle, texte, ...), l'outil en cours est conservé après le dessin. Sinon l'éditeur repasse sur l'outil de sélection.
* La grille a été améliorée : sa densité varie en fonction du zoom ; les points correspondant à ceux de la grille de l'éditeur de schémas sont mis en valeur.
* L'accrochage à la grille (aka "snap to grid", également connu sous le nom de grille magnétique ou encore grille aimantée) a été ajouté. Le dessin s'y accroche désormais avec une précision de 1px. On peut travailler en coordonnées libres en maintenant la touche Ctrl enfoncée durant le dessin.
* Le copier-coller a été implémenté : il est possible de coller :
* avec le bouton du milieu de la souris
* en choisissant une "zone de collage" sur l'élément (Ctrl+Shift+V)
* directement (Ctrl+V) : les parties collées sont placées à côté des parties copiées ; si on recolle les parties, elles sont collées encore un cran à côté, et ce de manière incrémentale.
* Des contrôles sont désormais effectués à l'enregistrement : présence de bornes, respect du cadre, etc.
* Uniformisation des menus par rapport à l'éditeur de schémas
====== Changelog 0.1 -> 0.11 ======
===== Fonctionnalités et interface =====
* L'application est désormais capable d'ouvrir un fichier élément passe en paramètre
* L'application se lance désormais une seule fois par utilisateur
* Lors de l'ouverture d'un fichier en dehors de l'application alors que QET est déjà démarré celui-ci essaye de s'afficher ou d'attirer l'attention de l'utilisateur.
* L'application vérifie que ce fichier n'est pas déjà ouvert dans tous les éditeurs de schémas / éléments.
* Ajout de fichiers permettant d'automatiser les associations de fichiers sous Windows (.bat et .reg) et X11 (.desktop et .xml)
* Ajout de menus "Récemment ouverts" pour accéder aux fichiers récents dans les éditeurs de schémas et éléments.
* Ajout d'un splash screen
* La hauteur du schéma est désormais gérée via un système de lignes, dont le nombre et la hauteur sont ajustables.
* Il est également possible d'afficher ou non les en-têtes des lignes et/ou des colonnes.
* Ajout d'une option --lang-dir
* Ajout d'une description dans le dialogue des options d'impression
* Ajout de pages de manuel Unix (`man') en anglais et en français
===== Corrections de bugs =====
* Bug #12 : QET provoquait une erreur de segmentation dès son démarrage dans un environnement sans systray
* Bug #14 : il manquait un / dans le chemin proposé lors de l'impression vers un PDF
* Bug #15 : Mauvais positionnement des champs de texte sur le schéma
* Bug #16 : Mauvaise gestion des modifications du texte d'un conducteur
* La classe DiagramView écrivait sur la sortie d'erreur sans fin de ligne
* L'option --config-dir était mal prise en compte
* Après fermeture d'un schema, le menu Fenêtres n'était pas correctement mis à jour
* Les textes des éléments, des conducteurs, du cartouche ainsi que les textes indépendants utilisent désormais tous la même police.
* Remise à niveau de l'impression suite au passage à Qt 4.4
===== Code et détails techniques =====
* Corrections pour que QET compile avec gcc-4.3
* Les classes Conductor et Element héritent désormais de QObject (dépendance sur Qt 4.4)
* Affinage du constructeur de la classe QETApp
* Moins d'avertissements à la compilation (testé avec gcc 4.3)
* Moins d'inclusions non pertinentes
## Version 0.100
_Compiled from provided commit logs and contributor notes._
+91 -2
View File
@@ -2,13 +2,102 @@
## [Unreleased](https://github.com/qelectrotech/qelectrotech-source-mirror/tree/HEAD)
[Full Changelog](https://github.com/qelectrotech/qelectrotech-source-mirror/compare/0.9...HEAD)
[Full Changelog](https://github.com/qelectrotech/qelectrotech-source-mirror/compare/nightly...HEAD)
**Closed issues:**
- “Exclude from auto-numbering” and “Potential isolation” tick boxes fault [\#482](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/482)
- Add ability to change appearance of multiple lines at once [\#476](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/476)
- Save As dialog asks for "element name" but actually requires a file name [\#469](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/469)
- Some icons are illegible with dark theme [\#466](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/466)
- \[BUG\] Properties to all conductors [\#460](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/460)
- Internal links in PDF export [\#417](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/417)
- Apple silicon download is not working [\#400](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/400)
- Add missing title block variables to 'folio properties' window [\#271](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/271)
- Bug: Saving a read-only project doesn't clear the read-only state [\#217](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/217)
**Merged pull requests:**
- PartText: keep text position stable across save/reopen on font-size change \(\#158\) [\#501](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/501) ([ispyisail](https://github.com/ispyisail))
- Clear read-only state when a project is saved to a writable file [\#497](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/497) ([ispyisail](https://github.com/ispyisail))
- Fix regional system locale loading the wrong translation \(pt\_BR/nl\_BE/nl\_NL\) [\#496](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/496) ([ispyisail](https://github.com/ispyisail))
- Folio properties: auto-add a title block's custom variables [\#495](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/495) ([ispyisail](https://github.com/ispyisail))
- Element editor Save As: label the field as a file name, not 'element name' [\#494](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/494) ([ispyisail](https://github.com/ispyisail))
- CLI: add --set-titleblock, and fix headless backup crash [\#493](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/493) ([ispyisail](https://github.com/ispyisail))
- Update qet\_zh.ts [\#491](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/491) ([zi-mozhuang](https://github.com/zi-mozhuang))
- CLI: clickable cross-reference hyperlinks in PDF export [\#490](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/490) ([ispyisail](https://github.com/ispyisail))
- CLI: add verification & data-export tools \(info, BOM, nets, links, check-elements, resave\) [\#489](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/489) ([ispyisail](https://github.com/ispyisail))
- Issues 482 [\#488](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/488) ([Kellermorph](https://github.com/Kellermorph))
- Revert "Update-UI-Chinese-translation" [\#486](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/486) ([scorpio810](https://github.com/scorpio810))
- CLI export: don't draw the editor grid in PDF/PNG/SVG output [\#485](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/485) ([ispyisail](https://github.com/ispyisail))
- Update-UI-Chinese-translation [\#484](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/484) ([zi-mozhuang](https://github.com/zi-mozhuang))
- Add headless command-line export \(PDF / PNG / SVG / cable-list / wire-list\) [\#483](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/483) ([ispyisail](https://github.com/ispyisail))
- fix possible crashes in crossrefitem [\#479](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/479) ([ChuckNr11](https://github.com/ChuckNr11))
- Fix: Dynamic element text shifting/jumping when duplicating diagrams [\#477](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/477) ([Kellermorph](https://github.com/Kellermorph))
- Update German translations for duplicate diagram feature [\#475](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/475) ([Kellermorph](https://github.com/Kellermorph))
- Feat: Add ability to duplicate diagrams/folios with all metadata and … [\#473](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/473) ([Kellermorph](https://github.com/Kellermorph))
- Feature: Allow excluding specific elements from BOM \(Nomenclature\) [\#472](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/472) ([Kellermorph](https://github.com/Kellermorph))
- Potential Isolation option for terminals [\#471](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/471) ([Kellermorph](https://github.com/Kellermorph))
- Fix: Wiring list filter and dynamic text timing [\#470](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/470) ([Kellermorph](https://github.com/Kellermorph))
- New element: Line definition [\#464](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/464) ([Kellermorph](https://github.com/Kellermorph))
- Turkish Lang Update [\#463](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/463) ([scorpio810](https://github.com/scorpio810))
- follow up: wiring list [\#462](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/462) ([Kellermorph](https://github.com/Kellermorph))
- Fix and Improve Multi-selection for Diagram Operations [\#459](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/459) ([Kellermorph](https://github.com/Kellermorph))
## [nightly](https://github.com/qelectrotech/qelectrotech-source-mirror/tree/nightly) (2026-05-10)
[Full Changelog](https://github.com/qelectrotech/qelectrotech-source-mirror/compare/0.100...nightly)
**Closed issues:**
- Flatpak runtimes outdated [\#446](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/446)
- Move Flemish man pages from man/be/ to man/nl\_BE/ [\#439](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/439)
- you could share an PR to fix it? [\#438](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/438)
- Diacritics in some filenames can possibly lead to the problems during packaging [\#437](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/437)
- שרטוט חשמל [\#435](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/435)
- Feature request: add circuit simulation [\#432](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/432)
- No usable sources archive for version 0.100 [\#418](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/418)
- New release ? [\#411](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/411)
- options moving when opening "file", "edition" menus [\#299](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/299)
**Merged pull requests:**
- Try to add Windows build CI workflow [\#457](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/457) ([scorpio810](https://github.com/scorpio810))
- Fixed: Prevented the selection in the project tree from jumping to the last page when saving. [\#456](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/456) ([Kellermorph](https://github.com/Kellermorph))
- Fix Thumbnail in Makrotree [\#455](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/455) ([Kellermorph](https://github.com/Kellermorph))
- Update German translation for macro feature [\#454](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/454) ([Kellermorph](https://github.com/Kellermorph))
- Fix losing Focus on moving diagram position with keyboard [\#452](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/452) ([ChuckNr11](https://github.com/ChuckNr11))
- Draft: Feature - Introduce User Templates Collection and Dedicated UI Tab [\#451](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/451) ([Kellermorph](https://github.com/Kellermorph))
- Update translation [\#450](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/450) ([Kellermorph](https://github.com/Kellermorph))
- Automatic Terminal Numbering Tool [\#449](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/449) ([Kellermorph](https://github.com/Kellermorph))
- Supplement to pull request \#444 by Kellermorph [\#448](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/448) ([ChuckNr11](https://github.com/ChuckNr11))
- Add RAM-based wiring list export [\#447](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/447) ([Kellermorph](https://github.com/Kellermorph))
- Follow-up: Address review comments for slave limit feature [\#444](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/444) ([Kellermorph](https://github.com/Kellermorph))
- Feature: Auto-select active diagram in the elements panel tree [\#443](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/443) ([Kellermorph](https://github.com/Kellermorph))
- Revert "Feature: Implement max\_slaves limit for Master elements" [\#442](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/442) ([scorpio810](https://github.com/scorpio810))
- Feature: Implement max\_slaves limit for Master elements [\#441](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/441) ([Kellermorph](https://github.com/Kellermorph))
- Update qet\_cs.ts [\#434](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/434) ([pafri](https://github.com/pafri))
- Update QCH Help file [\#433](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/433) ([Int-Circuit](https://github.com/Int-Circuit))
- Create Korean man page for QElectroTech [\#431](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/431) ([Kyle-Code-CA](https://github.com/Kyle-Code-CA))
- Update ELEMENTS.LICENSE [\#430](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/430) ([Kyle-Code-CA](https://github.com/Kyle-Code-CA))
- Update CREDIT [\#429](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/429) ([Kyle-Code-CA](https://github.com/Kyle-Code-CA))
- Add Korean comments to QElectroTech XML file [\#428](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/428) ([Kyle-Code-CA](https://github.com/Kyle-Code-CA))
- Add Spanish and Korean summaries to appdata [\#427](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/427) ([Kyle-Code-CA](https://github.com/Kyle-Code-CA))
- Add Korean translations for comments and generic names [\#426](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/426) ([Kyle-Code-CA](https://github.com/Kyle-Code-CA))
- Restore copyright and license information in QET64.nsi [\#425](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/425) ([Kyle-Code-CA](https://github.com/Kyle-Code-CA))
- Add Korean language strings to lang\_extra.nsh [\#424](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/424) ([Kyle-Code-CA](https://github.com/Kyle-Code-CA))
- Add Korean translation author to aboutqetdialog [\#423](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/423) ([Kyle-Code-CA](https://github.com/Kyle-Code-CA))
- Add Korean language support in xml element collection [\#422](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/422) ([Kyle-Code-CA](https://github.com/Kyle-Code-CA))
- Add Korean translation \(ko\) translated by jkh [\#419](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/419) ([Kyle-Code-CA](https://github.com/Kyle-Code-CA))
## [0.100](https://github.com/qelectrotech/qelectrotech-source-mirror/tree/0.100) (2026-01-25)
[Full Changelog](https://github.com/qelectrotech/qelectrotech-source-mirror/compare/0.9...0.100)
**Closed issues:**
- error in doxygen action code [\#414](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/414)
- "NoName" is automatically inserted into empty text cells in title block [\#407](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/407)
- Apple silicon download is not working [\#400](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/400)
- Apple silicon download is not working [\#394](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/394)
- Differenciating connector for proper labeling [\#390](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/390)
- Non-perpendicular connections [\#368](https://github.com/qelectrotech/qelectrotech-source-mirror/issues/368)
+1087
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -1,3 +1,18 @@
[Qt6 / CMake]
Building the Qt6 line with CMake additionally requires the private
development headers of QtGui. On Debian/Ubuntu these live in a separate
package that is NOT pulled in by qt6-base-dev:
apt install qt6-base-private-dev
(other distributions: install your Qt 6 "private headers" development
package, e.g. qt6-qtbase-private-devel on Fedora)
QET needs the private QtGui API (QPdfEngine) for clickable hyperlinks in
the PDF export. Without the package, find_package(Qt6 ... GuiPrivate)
succeeds but CMake later fails at generate time with:
"Imported target Qt6::GuiPrivate includes non-existent path".
[ca]
Dependències:
libQt5 (paquets libqt5*)
+159
View File
@@ -0,0 +1,159 @@
#!/usr/bin/env python3
"""
generate-page.py — Generates gh-pages/index.html for QElectroTech nightly builds.
Called from windows-msi.yml deploy-pages job.
Environment variables required:
DATE, SHORT, REPO, SHA, RUN_URL, RUN_NUMBER,
INSTALLER_URL, PORTABLE_URL, MSI_URL (optional)
Optional (Qt6 experimental track — omitted entirely if empty):
INSTALLER_QT6_URL, PORTABLE_QT6_URL, MSI_QT6_URL
"""
import os
date = os.environ.get("DATE", "")
short = os.environ.get("SHORT", "")
repo = os.environ.get("REPO", "")
sha = os.environ.get("SHA", "")
run_url = os.environ.get("RUN_URL", "")
run_number = os.environ.get("RUN_NUMBER", "")
installer_url = os.environ.get("INSTALLER_URL", "")
portable_url = os.environ.get("PORTABLE_URL", "")
msi_url = os.environ.get("MSI_URL", "")
installer_qt6_url = os.environ.get("INSTALLER_QT6_URL", "")
portable_qt6_url = os.environ.get("PORTABLE_QT6_URL", "")
msi_qt6_url = os.environ.get("MSI_QT6_URL", "")
msi_block = ""
if msi_url:
msi_block = f"""
<a class="btn btn-msi" href="{msi_url}">
<span class="btn-icon">&#11015;</span>
<span class="btn-text">Windows Installer .msi<small>.msi &mdash; for enterprise / GPO deployment</small></span>
</a>"""
# Qt6 experimental section — only rendered if at least one Qt6 asset exists.
qt6_block = ""
if installer_qt6_url or portable_qt6_url or msi_qt6_url:
qt6_msi_btn = ""
if msi_qt6_url:
qt6_msi_btn = f"""
<a class="btn btn-msi" href="{msi_qt6_url}">
<span class="btn-icon">&#11015;</span>
<span class="btn-text">Windows Installer .msi (Qt6)<small>.msi &mdash; experimental, for enterprise / GPO deployment</small></span>
</a>"""
qt6_installer_btn = ""
if installer_qt6_url:
qt6_installer_btn = f"""
<a class="btn btn-primary" href="{installer_qt6_url}">
<span class="btn-icon">&#11015;</span>
<span class="btn-text">Windows Installer (Qt6)<small>.exe &mdash; experimental, includes all dependencies</small></span>
</a>"""
qt6_portable_btn = ""
if portable_qt6_url:
qt6_portable_btn = f"""
<a class="btn btn-secondary" href="{portable_qt6_url}">
<span class="btn-icon">&#128230;</span>
<span class="btn-text">Windows Portable (Qt6)<small>.zip &mdash; experimental, no installation required</small></span>
</a>"""
qt6_block = f"""
<div class="card">
<h2>&#129514; Windows &mdash; x86_64 &mdash; Qt6 track</h2>
<div class="warning">
&#9888;&#65039; <strong>Experimental.</strong> These Qt6-based installer, portable and MSI
builds are new and not as thoroughly tested as the Qt5 track above. Expect rough
edges; please report issues and mention &quot;Qt6&quot; explicitly.
</div>
<div class="downloads">
{qt6_installer_btn}
{qt6_msi_btn}
{qt6_portable_btn}
</div>
</div>"""
html = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>QElectroTech &ndash; Nightly Builds</title>
<style>
*,*::before,*::after{{box-sizing:border-box;margin:0;padding:0}}
body{{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f0f4f8;color:#2d3748;min-height:100vh}}
header{{background:linear-gradient(135deg,#1a365d 0%,#2b6cb0 100%);color:white;padding:48px 24px 40px;text-align:center}}
header h1{{font-size:2.2em;letter-spacing:-0.5px;margin-bottom:8px}}
header p{{opacity:.8;font-size:1.05em}}
main{{max-width:680px;margin:40px auto;padding:0 20px 60px}}
.card{{background:white;border-radius:12px;padding:28px;margin-bottom:24px;box-shadow:0 2px 12px rgba(0,0,0,.08)}}
.card h2{{font-size:1em;text-transform:uppercase;letter-spacing:.06em;color:#718096;margin-bottom:16px}}
.meta{{font-size:.875em;color:#4a5568;line-height:1.8;margin-bottom:20px}}
.meta a{{color:#2b6cb0;text-decoration:none}}
.meta a:hover{{text-decoration:underline}}
.badge{{display:inline-block;background:#ebf8ff;color:#2b6cb0;border-radius:4px;font-size:.8em;font-weight:600;padding:2px 8px;margin-left:6px;vertical-align:middle}}
.warning{{background:#fffbeb;border-left:4px solid #f6ad55;border-radius:4px;padding:12px 16px;font-size:.875em;color:#744210;margin-bottom:24px;line-height:1.5}}
.warning a{{color:#c05621}}
.downloads{{display:flex;flex-direction:column;gap:12px}}
.btn{{display:flex;align-items:center;gap:12px;padding:14px 20px;border-radius:8px;font-size:.95em;font-weight:600;text-decoration:none;transition:transform .1s,box-shadow .1s}}
.btn:hover{{transform:translateY(-1px);box-shadow:0 4px 12px rgba(0,0,0,.15)}}
.btn-primary{{background:#2b6cb0;color:white}}
.btn-msi{{background:#6b46c1;color:white}}
.btn-secondary{{background:#edf2f7;color:#2d3748}}
.btn-icon{{font-size:1.3em}}
.btn-text small{{display:block;font-weight:400;font-size:.8em;opacity:.75;margin-top:1px}}
footer{{text-align:center;font-size:.8em;color:#a0aec0;padding:32px 0 0}}
footer a{{color:#718096;text-decoration:none}}
</style>
</head>
<body>
<header>
<h1>&#9889; QElectroTech</h1>
<p>Nightly Windows Builds</p>
</header>
<main>
<div class="card">
<h2>Build info</h2>
<div class="meta">
&#128197; &nbsp;<strong>{date}</strong><br>
&#128256; &nbsp;Commit <a href="https://github.com/{repo}/commit/{sha}"><code>{short}</code></a><br>
&#128295; &nbsp;<a href="{run_url}">CI Run #{run_number}</a>
<span class="badge">nightly</span>
</div>
<div class="warning">
&#9888;&#65039; This is a development version; it introduces new features you want,
but may cause bugs that have not yet been identified yet in <code>master</code>.
For production use, download a <a href="https://github.com/{repo}/releases">stable release</a>.
</div>
</div>
<div class="card">
<h2>&#127993; Windows &mdash; x86_64 &mdash; Qt5 track</h2>
<div class="downloads">
<a class="btn btn-primary" href="{installer_url}">
<span class="btn-icon">&#11015;</span>
<span class="btn-text">Windows Installer<small>.exe &mdash; recommended, includes all dependencies</small></span>
</a>
{msi_block}
<a class="btn btn-secondary" href="{portable_url}">
<span class="btn-icon">&#128230;</span>
<span class="btn-text">Windows Portable<small>.zip &mdash; no installation required, extract and run &quot;Lancer QET.bat&quot;</small></span>
</a>
<a class="btn btn-secondary" href="https://github.com/{repo}/releases/tag/nightly">
<span class="btn-icon">&#128230;</span>
<span class="btn-text">All nightly files on GitHub<small>Release page with checksums</small></span>
</a>
</div>
</div>
{qt6_block}
</main>
<footer>
Auto-generated by GitHub Actions &nbsp;&middot;&nbsp;
<a href="https://github.com/{repo}">Source on GitHub</a> &nbsp;&middot;&nbsp;
<a href="https://qelectrotech.org">qelectrotech.org</a>
</footer>
</body>
</html>"""
os.makedirs("gh-pages", exist_ok=True)
with open("gh-pages/index.html", "w", encoding="utf-8") as f:
f.write(html)
print("index.html written OK")
+11 -1
View File
@@ -53,6 +53,7 @@ parts:
qet-tb-generator:
plugin: python
source: https://github.com/raulroda/qet_tb_generator-plugin.git
source-tag: v1.31
python-packages: [PySimpleGUI]
stage-packages:
- python3-lxml
@@ -76,7 +77,16 @@ parts:
after: [kde-sdk-setup]
plugin: nil
source: .
stage-packages: [ git, sqlite3, xdg-user-dirs ]
stage-packages:
- git
- sqlite3
- xdg-user-dirs
# libxcb-cursor0 was added as a hard dependency of the Qt5 xcb platform
# plugin in Qt 5.15.x. The kf5-5-110-qt-5-15-11-core22 content snap does
# not bundle it, so dlopen() of libqxcb.so fails on Ubuntu 24.04 hosts
# with "found but could not load" (issue #373). Staging the 22.04 build
# of this library satisfies the dep without changing base or Qt version.
- libxcb-cursor0
build-packages:
- git
- libsqlite3-dev
+1 -1
View File
@@ -10,5 +10,5 @@ rem "C:/Users/<USER>/AppData/Local/QElectroTech/QElectroTech"
rem Sans l'option --data-dir, les données utilisateur (elements, titleblocks,
rem log, ...) de QElectroTech sont stockées dans
rem "C:/Users/<USER>/AppData/Roaming/QElectroTech/QElectroTech"
set command=bin\qelectrotech.exe --common-elements-dir=elements/ --common-tbt-dir=titleblocks/ --lang-dir=lang/ -style windowsvista %*
set command=bin\qelectrotech.exe --common-elements-dir=elements/ --common-tbt-dir=titleblocks/ --lang-dir=lang/ -platform windows:fontengine=freetype %*
@start %command%
+169
View File
@@ -0,0 +1,169 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
QElectroTech.wxs - WiX v7 installer definition
Generates a self-contained x64 MSI for QElectroTech (MSYS2/UCRT64 build).
Variables passed from the wix build command line:
-d FilesDir=<absolute path to artifact\files>
-d Version=<numeric version e.g. 0.100.1.0>
-d ProductVersion=<display version e.g. 0.100.1-r8770-abc1234_x86_64-win64>
-d LicenseRtf=<absolute path to License.rtf>
-->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
<?ifndef QtPlatformArgs ?>
<?define QtPlatformArgs="" ?>
<?endif?>
<Package
Name="QElectroTech"
Manufacturer="QElectroTech Team"
Version="$(var.Version)"
ProductCode="$(var.ProductCode)"
UpgradeCode="A1B2C3D4-E5F6-7890-ABCD-EF1234567890"
Language="1033"
Codepage="1252"
InstallerVersion="500"
Scope="perMachine">
<!-- Embed all cabinet files inside the MSI (self-contained installer) -->
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
<!-- In-place upgrade: automatically uninstalls the previous version -->
<MajorUpgrade
DowngradeErrorMessage="A newer version of QElectroTech is already installed."
AllowSameVersionUpgrades="yes"
Schedule="afterInstallInitialize" />
<!-- Installation directory -->
<StandardDirectory Id="ProgramFiles64Folder">
<Directory Id="INSTALLDIR" Name="QElectroTech" />
</StandardDirectory>
<!-- ============================================================
All application files harvested in one pass from files\**
(Lancer QET.bat has been removed from the artifact before
this build — see windows-msi.yml step "Remove Lancer QET.bat")
============================================================ -->
<ComponentGroup Id="CG_AllFiles" Directory="INSTALLDIR">
<Files Include="$(var.FilesDir)\**" />
</ComponentGroup>
<!-- ============================================================
Desktop + Start Menu shortcuts
Point directly to qelectrotech.exe with all required arguments.
No .bat wrapper needed.
============================================================ -->
<ComponentGroup Id="CG_Shortcuts" Directory="INSTALLDIR">
<Component Id="C_ShortcutDesktop" Guid="F1A2B3C4-D5E6-7890-5678-012345678901">
<Shortcut Id="DesktopShortcut"
Directory="DesktopFolder"
Name="QElectroTech"
Target="[INSTALLDIR]bin\qelectrotech.exe"
Arguments="--common-elements-dir=&quot;[INSTALLDIR]elements/&quot; --common-tbt-dir=&quot;[INSTALLDIR]titleblocks/&quot; --lang-dir=&quot;[INSTALLDIR]lang/&quot; $(var.QtPlatformArgs)"
Icon="qet.ico"
WorkingDirectory="INSTALLDIR" />
<RegistryValue Root="HKCU"
Key="Software\QElectroTech"
Name="DesktopShortcut"
Type="integer" Value="1"
KeyPath="yes" />
</Component>
<Component Id="C_ShortcutStartMenu" Guid="A2B3C4D5-E6F7-8901-6789-123456789012">
<Shortcut Id="StartMenuShortcut"
Directory="ProgramMenuFolder"
Name="QElectroTech"
Target="[INSTALLDIR]bin\qelectrotech.exe"
Arguments="--common-elements-dir=&quot;[INSTALLDIR]elements/&quot; --common-tbt-dir=&quot;[INSTALLDIR]titleblocks/&quot; --lang-dir=&quot;[INSTALLDIR]lang/&quot; $(var.QtPlatformArgs)"
Icon="qet.ico"
WorkingDirectory="INSTALLDIR" />
<RegistryValue Root="HKCU"
Key="Software\QElectroTech"
Name="StartMenuShortcut"
Type="integer" Value="1"
KeyPath="yes" />
</Component>
</ComponentGroup>
<!-- ============================================================
.qet file association
============================================================ -->
<ComponentGroup Id="CG_FileAssoc" Directory="INSTALLDIR">
<Component Id="C_FileAssoc" Guid="B3C4D5E6-F7A8-9012-7890-234567890123">
<RegistryValue Root="HKCR" Key=".qet" Type="string" Value="QElectroTech.Document" KeyPath="yes" />
<RegistryValue Root="HKCR" Key="QElectroTech.Document" Type="string" Value="QElectroTech Project" />
<RegistryValue Root="HKCR" Key="QElectroTech.Document\DefaultIcon" Type="string"
Value="[INSTALLDIR]ico\qelectrotech.ico" />
<!-- Pass the same arguments as the shortcuts above: opening a .qet from
Explorer sets the working directory to the document's folder, so a
bare command line leaves QET unable to locate its data. Without the
lang-dir argument the UI falls back to the source language, which
is French. See issue #554. -->
<RegistryValue Root="HKCR" Key="QElectroTech.Document\shell\open\command" Type="string"
Value="&quot;[INSTALLDIR]bin\qelectrotech.exe&quot; --common-elements-dir=&quot;[INSTALLDIR]elements/&quot; --common-tbt-dir=&quot;[INSTALLDIR]titleblocks/&quot; --lang-dir=&quot;[INSTALLDIR]lang/&quot; $(var.QtPlatformArgs) &quot;%1&quot;" />
</Component>
</ComponentGroup>
<!-- ============================================================
Icon for shortcuts
============================================================ -->
<Icon Id="qet.ico" SourceFile="$(var.FilesDir)\ico\qelectrotech.ico" />
<!-- ============================================================
CustomAction: set elements\ subtree read-only after install
Pattern used: two-step SetProperty + Exec
1. CA_ResolveElementsPath (immediate, SetProperty):
copies the runtime value of INSTALLDIR into the property
ELEMENTS_READONLY_CMD, building the full powershell command.
2. CA_SetElementsReadOnly (deferred, Execute="deferred"):
runs the command stored in ELEMENTS_READONLY_CMD.
This is the correct WiX v4/v7 pattern for passing a
directory path (resolved at runtime) to a deferred CA.
============================================================ -->
<!-- Step 1: build the powershell command with the resolved INSTALLDIR -->
<CustomAction Id="CA_ResolveElementsPath"
Property="CA_SetElementsReadOnly"
Value="powershell.exe -NonInteractive -NoProfile -WindowStyle Hidden -Command &quot;Get-ChildItem -LiteralPath '[INSTALLDIR]elements' -Recurse -File | ForEach-Object { `$_.IsReadOnly = `$true }&quot;" />
<!-- Step 2: deferred execution of the powershell command -->
<CustomAction Id="CA_SetElementsReadOnly"
BinaryRef="Wix4UtilCA_X86"
DllEntry="WixQuietExec"
Execute="deferred"
Impersonate="no"
Return="ignore" />
<InstallExecuteSequence>
<Custom Action="CA_ResolveElementsPath" After="InstallFiles" Condition="NOT Installed AND NOT REMOVE" />
<Custom Action="CA_SetElementsReadOnly" After="CA_ResolveElementsPath" Condition="NOT Installed AND NOT REMOVE" />
</InstallExecuteSequence>
<!-- ============================================================
Main feature (everything included)
============================================================ -->
<Feature Id="ProductFeature" Title="QElectroTech" Level="1">
<ComponentGroupRef Id="CG_AllFiles" />
<ComponentGroupRef Id="CG_Shortcuts" />
<ComponentGroupRef Id="CG_FileAssoc" />
</Feature>
<!-- WixUI_Minimal with QElectroTech GPL-2 license -->
<ui:WixUI Id="WixUI_Minimal" />
<!-- WixVariable without ui: namespace, at Package level -->
<WixVariable Id="WixUILicenseRtf" Value="$(var.LicenseRtf)" />
<!-- Properties shown in Programs and Features -->
<Property Id="ARPPRODUCTICON" Value="qet.ico" />
<Property Id="ARPHELPLINK" Value="https://qelectrotech.org" />
<Property Id="ARPURLINFOABOUT" Value="https://qelectrotech.org" />
<Property Id="ARPCOMMENTS" Value="Free electrical schematic editor" />
</Package>
</Wix>
+63
View File
@@ -0,0 +1,63 @@
# Configuration for git-cliff, tuned to mimic github_changelog_generator's output style
# Doc: https://git-cliff.org/docs/configuration
[changelog]
header = """
# Changelog\n
All notable changes to QElectroTech are documented here.\n
"""
# Format proche de github_changelog_generator : titre de version avec lien de comparaison + date
body = """
{% if version %}\
## [{{ version }}]\
{%- if previous.version %}({{ previous.version }}...{{ version }}){% endif %} \
- {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [Unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper_first }}
{% for commit in commits | unique(attribute="message") %}
- {{ commit.message | trim | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}](../../commit/{{ commit.id }}))\
{% endfor %}
{% endfor %}\n
"""
footer = ""
trim = true
[git]
conventional_commits = false
filter_unconventional = false
split_commits = false
protect_breaking_commits = false
filter_commits = false
tag_pattern = "^[0-9]+\\.[0-9]+(\\.[0-9]+)?$"
skip_tags = "nightly"
ignore_tags = "nightly"
topo_order = false
sort_commits = "newest"
# Ne garder que la première ligne du message (résumé), le corps détaillé
# est retiré ici plutôt qu'au moment de l'affichage, pour que la
# déduplication par message fonctionne correctement.
commit_preprocessors = [
{ pattern = "(?s)\n.*", replace = "" },
]
# Groupement par mots-clés (FR + EN), puisque l'historique QET
# ne suit pas Conventional Commits strictement.
commit_parsers = [
{ message = "^[Mm]erge", skip = true },
{ message = "^(?i)(fix|corrig|répar|bug)", group = "🐛 Bug Fixes" },
{ message = "^(?i)(feat|ajout|add|nouvelle|nouveau)", group = "🚀 Features" },
{ message = "^(?i)(doc|traduc|translation)", group = "📝 Documentation" },
{ message = "^(?i)(refactor|clean|nettoy|optimi)", group = "♻️ Refactoring / Cleanup" },
{ message = "^(?i)(element|librairie|library)", group = "📦 Elements / Libraries" },
{ message = ".*", group = "🔧 Other Changes" },
]
[remote.github]
owner = "qelectrotech"
repo = "qelectrotech-source-mirror"
+14
View File
@@ -33,3 +33,17 @@ add_definitions(-DQT_MESSAGELOGCONTEXT)
# Build with KF5
option(BUILD_WITH_KF5 "Build with KF5" ON)
# Precompiled headers for the Qt umbrella headers.
#
# Off by default and intended for local development only. Building QET is
# dominated by re-parsing Qt's headers: a 214-line .cpp expands to ~198,000
# preprocessed lines, and compiling one translation unit costs ~4.1 s of which
# only ~0.35 s is optimisation (-O0 instead of -O3 saves 8%). A PCH caches the
# parsed header state and takes that ~4.1 s down to ~1.2 s.
#
# It is deliberately NOT on by default: a PCH satisfies includes that a source
# file forgot to make itself, so code written with it enabled can fail to
# compile for everyone else. Leaving it off keeps CI and contributors on the
# strict behaviour, and only developers who opt in trade that for the speed.
option(QET_ENABLE_PCH "Use precompiled headers (developer build speed; may mask missing #includes)" OFF)
+1 -1
View File
@@ -16,7 +16,7 @@
message(" - fetch_kdeaddons")
if(DEFINED BUILD_WITH_KF5)
if(BUILD_WITH_KF5)
Include(FetchContent)
option(BUILD_KF5 "Build KF5 libraries, use system ones otherwise" YES)
+1 -1
View File
@@ -25,7 +25,7 @@ if(BUILD_PUGIXML)
FetchContent_Declare(
pugixml
GIT_REPOSITORY https://github.com/zeux/pugixml.git
GIT_TAG v1.11.4)
GIT_TAG v1.15)
FetchContent_MakeAvailable(pugixml)
else()
+4 -1
View File
@@ -58,7 +58,10 @@ if(WIN32)
set(QET_BINARY_PATH "./")
set(QET_COMMON_COLLECTION_PATH "elements/")
set(QET_COMMON_TBT_PATH "titleblocks/")
set(QET_LANG_PATH "l10n/")
# "lang/" and not "l10n/": that is where every Windows packaging actually
# puts the .qm files (see build-aux/windows/QElectroTech.wxs and the
# windows-build workflow), and what the shortcuts pass as --lang-dir.
set(QET_LANG_PATH "lang/")
set(QET_LICENSE_PATH "./")
# Liste des ressources Windows
#RC_FILE = qelectrotech.rc
+93 -1
View File
@@ -16,6 +16,13 @@
message(" - qet_compilation_vars")
# Note: GuiPrivate is intentionally NOT in this list. Qt6's CMake config only
# creates the Qt::GuiPrivate target when "GuiPrivate" is explicitly requested
# as a component, but Qt5 has no Qt5GuiPrivate package at all (the target is
# created implicitly with Gui), so requesting it as a component breaks the
# whole Qt5 configure. It is requested separately, guarded by
# QT_VERSION_MAJOR, after the main find_package.
# (Needed for QPdfEngine::drawHyperlink, the PDF internal links.)
set(QET_COMPONENTS
LinguistTools
PrintSupport
@@ -29,6 +36,7 @@ set(QET_COMPONENTS
set(QET_PRIVATE_LIBRARIES
Qt::PrintSupport
Qt::Gui
Qt::GuiPrivate # Required for QPdfEngine::drawHyperlink (PDF internal links)
Qt::Xml
Qt::Svg
Qt::Sql
@@ -106,6 +114,48 @@ set(QET_RES_FILES
${QET_DIR}/sources/ui/configpage/generalconfigurationpage.ui
)
set(QET_SRC_FILES
${QET_DIR}/sources/cli_export.cpp
${QET_DIR}/sources/cli_export.h
${QET_DIR}/sources/logging/crashhandler.cpp
${QET_DIR}/sources/logging/crashhandler.h
${QET_DIR}/sources/logging/eventloopwatchdog.cpp
${QET_DIR}/sources/logging/eventloopwatchdog.h
${QET_DIR}/sources/logging/logring.cpp
${QET_DIR}/sources/logging/logring.h
${QET_DIR}/sources/logging/qetlogger.cpp
${QET_DIR}/sources/logging/qetlogger.h
${QET_DIR}/sources/logging/ui/diagnosticsreportdialog.cpp
${QET_DIR}/sources/logging/ui/diagnosticsreportdialog.h
${QET_DIR}/sources/pdf_links.cpp
${QET_DIR}/sources/pdf_links.h
${QET_DIR}/sources/import/edz/edzarchive.cpp
${QET_DIR}/sources/import/edz/edzarchive.h
${QET_DIR}/sources/import/edz/edzpart.cpp
${QET_DIR}/sources/import/edz/edzpart.h
${QET_DIR}/sources/import/edz/edzelementbuilder.cpp
${QET_DIR}/sources/import/edz/edzelementbuilder.h
${QET_DIR}/sources/import/edz/edzimporter.cpp
${QET_DIR}/sources/import/edz/edzimporter.h
${QET_DIR}/sources/import/edz/edzsevenzip.cpp
${QET_DIR}/sources/import/edz/edzsevenzip.h
${QET_DIR}/sources/import/edz/lzma/7zAlloc.c
${QET_DIR}/sources/import/edz/lzma/7zArcIn.c
${QET_DIR}/sources/import/edz/lzma/7zBuf.c
${QET_DIR}/sources/import/edz/lzma/7zCrc.c
${QET_DIR}/sources/import/edz/lzma/7zCrcOpt.c
${QET_DIR}/sources/import/edz/lzma/7zDec.c
${QET_DIR}/sources/import/edz/lzma/7zFile.c
${QET_DIR}/sources/import/edz/lzma/7zStream.c
${QET_DIR}/sources/import/edz/lzma/Bcj2.c
${QET_DIR}/sources/import/edz/lzma/Bra.c
${QET_DIR}/sources/import/edz/lzma/Bra86.c
${QET_DIR}/sources/import/edz/lzma/BraIA64.c
${QET_DIR}/sources/import/edz/lzma/CpuArch.c
${QET_DIR}/sources/import/edz/lzma/Delta.c
${QET_DIR}/sources/import/edz/lzma/Lzma2Dec.c
${QET_DIR}/sources/import/edz/lzma/LzmaDec.c
${QET_DIR}/sources/import/edz/lzma/Ppmd7.c
${QET_DIR}/sources/import/edz/lzma/Ppmd7Dec.c
${QET_DIR}/sources/borderproperties.cpp
${QET_DIR}/sources/borderproperties.h
${QET_DIR}/sources/bordertitleblock.cpp
@@ -208,6 +258,8 @@ set(QET_SRC_FILES
${QET_DIR}/sources/qtextorientationwidget.h
${QET_DIR}/sources/recentfiles.cpp
${QET_DIR}/sources/recentfiles.h
${QET_DIR}/sources/shortcutmanager.cpp
${QET_DIR}/sources/shortcutmanager.h
${QET_DIR}/sources/titleblockcell.cpp
${QET_DIR}/sources/titleblockcell.h
${QET_DIR}/sources/titleblockproperties.cpp
@@ -319,6 +371,8 @@ set(QET_SRC_FILES
${QET_DIR}/sources/editor/graphicspart/partpolygon.h
${QET_DIR}/sources/editor/graphicspart/partrectangle.cpp
${QET_DIR}/sources/editor/graphicspart/partrectangle.h
${QET_DIR}/sources/editor/graphicspart/partplctable.cpp
${QET_DIR}/sources/editor/graphicspart/partplctable.h
${QET_DIR}/sources/editor/graphicspart/partterminal.cpp
${QET_DIR}/sources/editor/graphicspart/partterminal.h
${QET_DIR}/sources/editor/graphicspart/parttext.cpp
@@ -398,6 +452,8 @@ set(QET_SRC_FILES
${QET_DIR}/sources/project/projectpropertieshandler.cpp
${QET_DIR}/sources/project/projectpropertieshandler.h
${QET_DIR}/sources/project/projectusagetracker.cpp
${QET_DIR}/sources/project/projectusagetracker.h
${QET_DIR}/sources/properties/elementdata.cpp
${QET_DIR}/sources/properties/elementdata.h
@@ -484,7 +540,6 @@ set(QET_SRC_FILES
${QET_DIR}/sources/richtext/richtexteditor.cpp
${QET_DIR}/sources/richtext/richtexteditor_p.h
${QET_DIR}/sources/richtext/ui_addlinkdialog.h
${QET_DIR}/sources/SearchAndReplace/searchandreplaceworker.cpp
${QET_DIR}/sources/SearchAndReplace/searchandreplaceworker.h
@@ -499,6 +554,8 @@ set(QET_SRC_FILES
${QET_DIR}/sources/SearchAndReplace/ui/replacefoliowidget.h
${QET_DIR}/sources/SearchAndReplace/ui/searchandreplacewidget.cpp
${QET_DIR}/sources/SearchAndReplace/ui/searchandreplacewidget.h
${QET_DIR}/sources/svg/qetsvg.cpp
${QET_DIR}/sources/svg/qetsvg.h
${QET_DIR}/sources/titleblock/dimension.cpp
${QET_DIR}/sources/titleblock/dimension.h
@@ -609,6 +666,8 @@ set(QET_SRC_FILES
${QET_DIR}/sources/ui/borderpropertieswidget.h
${QET_DIR}/sources/ui/compositetexteditdialog.cpp
${QET_DIR}/sources/ui/compositetexteditdialog.h
${QET_DIR}/sources/ui/contactgroupselectiondialog.cpp
${QET_DIR}/sources/ui/contactgroupselectiondialog.h
${QET_DIR}/sources/ui/conductorpropertiesdialog.cpp
${QET_DIR}/sources/ui/conductorpropertiesdialog.h
${QET_DIR}/sources/ui/conductorpropertieswidget.cpp
@@ -625,12 +684,16 @@ set(QET_SRC_FILES
${QET_DIR}/sources/ui/diagrampropertieseditordockwidget.h
${QET_DIR}/sources/ui/diagramselection.cpp
${QET_DIR}/sources/ui/diagramselection.h
${QET_DIR}/sources/ui/backupdialog.cpp
${QET_DIR}/sources/ui/backupdialog.h
${QET_DIR}/sources/ui/dialogwaiting.cpp
${QET_DIR}/sources/ui/dialogwaiting.h
${QET_DIR}/sources/ui/dynamicelementtextitemeditor.cpp
${QET_DIR}/sources/ui/dynamicelementtextitemeditor.h
${QET_DIR}/sources/ui/dynamicelementtextmodel.cpp
${QET_DIR}/sources/ui/dynamicelementtextmodel.h
${QET_DIR}/sources/ui/customelementinfopartwidget.cpp
${QET_DIR}/sources/ui/customelementinfopartwidget.h
${QET_DIR}/sources/ui/elementinfopartwidget.cpp
${QET_DIR}/sources/ui/elementinfopartwidget.h
${QET_DIR}/sources/ui/elementinfowidget.cpp
@@ -647,6 +710,8 @@ set(QET_SRC_FILES
${QET_DIR}/sources/ui/importelementdialog.h
${QET_DIR}/sources/ui/importelementtextpatterndialog.cpp
${QET_DIR}/sources/ui/importelementtextpatterndialog.h
${QET_DIR}/sources/ui/jumptoelementdialog.cpp
${QET_DIR}/sources/ui/jumptoelementdialog.h
${QET_DIR}/sources/ui/inditextpropertieswidget.cpp
${QET_DIR}/sources/ui/inditextpropertieswidget.h
${QET_DIR}/sources/ui/linksingleelementwidget.cpp
@@ -655,6 +720,8 @@ set(QET_SRC_FILES
${QET_DIR}/sources/ui/marginseditdialog.h
${QET_DIR}/sources/ui/masterpropertieswidget.cpp
${QET_DIR}/sources/ui/masterpropertieswidget.h
${QET_DIR}/sources/ui/plclinkwidget.cpp
${QET_DIR}/sources/ui/plclinkwidget.h
${QET_DIR}/sources/ui/multipastedialog.cpp
${QET_DIR}/sources/ui/multipastedialog.h
${QET_DIR}/sources/ui/potentialselectordialog.cpp
@@ -678,9 +745,15 @@ set(QET_SRC_FILES
${QET_DIR}/sources/ui/configpage/generalconfigurationpage.h
${QET_DIR}/sources/ui/configpage/projectconfigpages.cpp
${QET_DIR}/sources/ui/configpage/projectconfigpages.h
${QET_DIR}/sources/ui/configpage/guidespropertieswidget.cpp
${QET_DIR}/sources/ui/configpage/guidespropertieswidget.h
${QET_DIR}/sources/ui/configpage/shortcutsconfigpage.cpp
${QET_DIR}/sources/ui/configpage/shortcutsconfigpage.h
${QET_DIR}/sources/undocommand/addelementtextcommand.cpp
${QET_DIR}/sources/undocommand/addelementtextcommand.h
${QET_DIR}/sources/undocommand/adddiagramcommand.cpp
${QET_DIR}/sources/undocommand/adddiagramcommand.h
${QET_DIR}/sources/undocommand/addgraphicsobjectcommand.cpp
${QET_DIR}/sources/undocommand/addgraphicsobjectcommand.h
${QET_DIR}/sources/undocommand/changeelementdatacommand.cpp
@@ -695,6 +768,12 @@ set(QET_SRC_FILES
${QET_DIR}/sources/undocommand/itemmodelcommand.h
${QET_DIR}/sources/undocommand/linkelementcommand.cpp
${QET_DIR}/sources/undocommand/linkelementcommand.h
${QET_DIR}/sources/undocommand/movediagramcommand.cpp
${QET_DIR}/sources/undocommand/movediagramcommand.h
${QET_DIR}/sources/undocommand/removediagramcommand.cpp
${QET_DIR}/sources/undocommand/removediagramcommand.h
${QET_DIR}/sources/undocommand/setautonumcontextcommand.cpp
${QET_DIR}/sources/undocommand/setautonumcontextcommand.h
${QET_DIR}/sources/undocommand/rotateselectioncommand.cpp
${QET_DIR}/sources/undocommand/rotateselectioncommand.h
${QET_DIR}/sources/undocommand/rotatetextscommand.cpp
@@ -713,8 +792,21 @@ set(QET_SRC_FILES
${QET_DIR}/sources/xml/terminalstripitemxml.cpp
${QET_DIR}/sources/xml/terminalstripitemxml.h
${QET_DIR}/sources/xml/terminalstriplayoutpatternxml.cpp
${QET_DIR}/sources/xml/terminalstriplayoutpatternxml.h
)
if(NOT BUILD_WITH_KF5)
list(APPEND QET_SRC_FILES
${QET_DIR}/sources/ui/nokde/kautosavefile.cpp
${QET_DIR}/sources/ui/nokde/kautosavefile.h
${QET_DIR}/sources/ui/nokde/kcolorbutton.cpp
${QET_DIR}/sources/ui/nokde/kcolorbutton.h
${QET_DIR}/sources/ui/nokde/kcolorcombo.cpp
${QET_DIR}/sources/ui/nokde/kcolorcombo.h
)
endif()
set(TS_FILES
${QET_DIR}/lang/qet_ar.ts
${QET_DIR}/lang/qet_ca.ts
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7b7798233e8251baac7717d4b0f991ad5fdf23a76d9fbe864d363e3b93458560
size 529821696
BIN
View File
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

BIN
View File
Binary file not shown.
+2580 -1138
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2572 -1138
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2572 -1136
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2570 -1137
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2591 -1138
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2571 -1137
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2582 -1134
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2571 -1137
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2638 -1204
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2594 -1158
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2568 -1136
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2573 -1139
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2567 -1135
View File
File diff suppressed because it is too large Load Diff
+2568 -1136
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2568 -1134
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2569 -1135
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2571 -1137
View File
File diff suppressed because it is too large Load Diff
+2622 -1188
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2572 -1136
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2615 -1181
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2668 -1234
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2618 -1182
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2625 -1189
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2574 -1138
View File
File diff suppressed because it is too large Load Diff
+2632 -1196
View File
File diff suppressed because it is too large Load Diff
+2636 -1198
View File
File diff suppressed because it is too large Load Diff
+2634 -1198
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2570 -1136
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+3401 -1926
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+2572 -1136
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+3969 -2509
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -8,5 +8,5 @@ rem Met la collection QET en lecture seule
attrib +r elements/* /S /D
rem lance QElectroTech
set command=bin\qelectrotech.exe -platform windows:fontengine=freetype --common-elements-dir=elements/ --common-tbt-dir=titleblocks/ --lang-dir=lang/ --config-dir=conf/ --data-dir=conf/ -style plastique %*
set command=bin\qelectrotech.exe -platform windows:fontengine=freetype --common-elements-dir=elements/ --common-tbt-dir=titleblocks/ --lang-dir=lang/ --config-dir=conf/ --data-dir=conf/ %*
@start %command%
+12
View File
@@ -0,0 +1,12 @@
@echo off
rem Se rend dans le dossier qui convient
set current_dir=%~dp0
cd /d %current_dir%
rem Met la collection QET en lecture seule
attrib +r elements/* /S /D
rem lance QElectroTech
set command=bin\qelectrotech.exe --common-elements-dir=elements/ --common-tbt-dir=titleblocks/ --lang-dir=lang/ --config-dir=conf/ --data-dir=conf/ %*
@start %command%
+293 -121
View File
@@ -16,7 +16,6 @@
# along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
# Need homebrew and coreutils installed see <http://brew.sh>.
#Force MacOSX12.3.sdk
#see: https://www.downtowndougbrown.com/2023/08/how-to-create-a-qt-5-arm-intel-universal-binary-for-mac/
@@ -26,14 +25,20 @@ export DEVELOPER_DIR=/Applications/Xcode_14.01.app/Contents/Developer
APPNAME='qelectrotech'
BUNDLE=$APPNAME.app
APPBIN="$BUNDLE/Contents/MacOS/$APPNAME"
IDENTITY="Developer ID Application: Laurent TRINQUES (Y73WZ6WZ5X)"
# Emplacement du script
# Temp paths
RW_DMG="/tmp/qet_rw.dmg"
MOUNT_POINT="/tmp/qet_dmg_mount"
STAGING="/tmp/qet_dmg_staging"
# Script location
current_dir=$(dirname "$0")
# On se remet au depart
# Go back to repo root
cd "${current_dir}/../"
# Emplacement courant
# Current directory
current_dir=$(PWD)
@@ -46,11 +51,11 @@ echo "Please see the \"Deploying an Application on Qt/Mac\""
echo "page in the Qt documentation for more information."
echo
echo "This script :"
echo "\t - up date the git depot"
echo "\t - built the application bundle,"
echo "\t - update the git depot"
echo "\t - build the application bundle,"
echo "\t - copy over required Qt frameworks,"
echo "\t - copy additional files: translations, titleblocks and elements,"
echo "\t - create image disk."
echo "\t - notarize the .app, then create a signed DMG."
echo
echo "Enjoy ;-)"
echo
@@ -70,25 +75,23 @@ echo
echo "______________________________________________________________"
echo "Run GIT:"
# Fait une mise à jour
git submodule init
git submodule update
git pull --recurse-submodules
git pull
#git checkout foliolist_position
# recupere le numero de la nouvelle revision
# Get revision number and version
GITCOMMIT=$(git rev-parse --short HEAD)
A=$(git rev-list HEAD --count)
HEAD=$(($A+473))
VERSION=$(cat sources/qetversion.cpp | grep "return QVersionNumber{"| head -n 1| awk -F "{" '{ print $2 }' | awk -F "}" '{ print $1 }' | sed -e 's/,/./g' -e 's/ //g')
#VERSION=$(cat sources/qetversion.cpp | grep "return QVersionNumber{ 0, "| head -n 1| cut -c32-40| sed -e 's/,/./g' -e 's/ //g') #Find major, minor, and micro version numbers in sources/qetversion.cp
# Tarball de la dernière revision déjà créé
if [ -e "build-aux/mac-osx/${APPNAME}-$VERSION-r$HEAD-arm64.zip" ] ; then
DMG_NAME="${APPNAME}-$VERSION-r$HEAD-arm64.dmg"
DMG_PATH="build-aux/mac-osx/$DMG_NAME"
# Check if already built
if [ -e "$DMG_PATH" ] ; then
echo "There are not new updates, make disk image can"
echo "take a lot of time (5 min). Can you continu?"
echo "[y/n]"
@@ -108,28 +111,27 @@ echo
echo "______________________________________________________________"
echo "Run make install:"
# pour effacer lancienne compilation
# Remove old bundle
if [ -d $BUNDLE ] ; then
echo "Removing hold bundle..."
echo "Removing old bundle..."
rm -rf $BUNDLE
fi
if [ -e Makefile ] ; then
echo "Removing hold Makefile..."
echo "Removing old Makefile..."
rm .qmake.stash
make clean
fi
# genere le Makefile
# Generate Makefile
echo "Generating new makefile..."
qmake -spec macx-clang
qmake -spec macx-clang
# compilation
# Compile
if [ -e Makefile.Release ] ; then
START_TIME=$SECONDS
# arret du script si erreur de compilation
START_TIME=$SECONDS
testSuccessBuild () {
if [ $? -ne 0 ]; then
if [ $? -ne 0 ]; then
cleanVerionTag
ELAPSED_TIME=$(($SECONDS - $START_TIME))
echo
@@ -138,19 +140,18 @@ if [ -e Makefile.Release ] ; then
fi
}
# utilise tout les coeurs pour une compilation plus rapide
coeur=$(sysctl hw.ncpu | awk '{print $2}')
if [ $? -ne 0 ]; then
if [ $? -ne 0 ]; then
make -f Makefile.Release
testSuccessBuild
else
make -j$(($coeur + 1)) -f Makefile.Release
testSuccessBuild
fi
ELAPSED_TIME=$(($SECONDS - $START_TIME))
echo
echo "The time of compilation is $(($ELAPSED_TIME/60)) min $(($ELAPSED_TIME%60)) sec"
echo
echo "The time of compilation is $(($ELAPSED_TIME/60)) min $(($ELAPSED_TIME%60)) sec"
else
echo "ERROR: Makefile not found. This script requires the macx-clang makespec"
exit
@@ -158,8 +159,7 @@ fi
cp -R ${current_dir}/misc/Info.plist qelectrotech.app/Contents/
cp -R ${current_dir}/ico/mac_icon/*.icns qelectrotech.app/Contents/Resources/
# On rajoute le numero de version pour "cmd + i"
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION r$HEAD" "qelectrotech.app/Contents/Info.plist" # Version number
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION r$HEAD" "qelectrotech.app/Contents/Info.plist"
### copy over frameworks ############################################
@@ -168,70 +168,58 @@ echo
echo "______________________________________________________________"
echo "Copy Qt libraries and private frameworks:"
echo "Processing Mac deployment tool..."
echo "Processing Mac deployment tool..."
if [ ! -d $BUNDLE ] ; then
echo "ERROR: cannot find application bundle \"$BUNDLE\" in current directory"
exit
fi
#~/Qt/5.5/clang_64/bin/macdeployqt $BUNDLE
macdeployqt $BUNDLE
### add file missing #######################################
### add missing files ###############################################
echo
echo "______________________________________________________________"
echo "Copy file missing:"
echo "Copy missing files:"
# Dossier à ajouter
QET_ELMT_DIR="${current_dir}/elements/"
QET_TBT_DIR="${current_dir}/titleblocks/"
QET_LANG_DIR="${current_dir}/lang/"
QET_EXAMPLES_DIR="${current_dir}/examples/"
QET_FONTS_DIR="${current_dir}/fonts/"
QET_LICENSES_DIR="${current_dir}/licenses/"
# Add new folder for Qt dialog translation see
## see <https://download.tuxfamily.org/qet/Qt_lang/>.
LANG_DIR="${current_dir}/lang1/"
if [ -d "${QET_ELMT_DIR}" ]; then
echo "Copying add elements in the bundle..."
#mkdir $BUNDLE/Contents/Resources/elements
echo "Copying elements in the bundle..."
cp -R ${QET_ELMT_DIR} $BUNDLE/Contents/Resources/elements
fi
if [ -d "${QET_TBT_DIR}" ]; then
echo "Copying titleblocks in the bundle..."
#mkdir $BUNDLE/Contents/Resources/titleblocks
cp -R ${QET_TBT_DIR} $BUNDLE/Contents/Resources/titleblocks
fi
if [ -d "${QET_LANG_DIR}" ]; then
echo "Copying translations in the bundle... "
echo "Copying translations in the bundle..."
mkdir $BUNDLE/Contents/Resources/lang
cp ${current_dir}/lang/*.qm $BUNDLE/Contents/Resources/lang
fi
if [ -d "${LANG_DIR}" ]; then
echo "Copying translations in the bundle... "
cp ${current_dir}/lang1/*.qm $BUNDLE/Contents/Resources/lang
echo "Copying extra translations in the bundle..."
cp ${current_dir}/lang1/*.qm $BUNDLE/Contents/Resources/lang
fi
if [ -d "${QET_EXAMPLES_DIR}" ]; then
echo "Copying examples in the bundle... "
mkdir $BUNDLE/Contents/Resources/examples
cp ${current_dir}/examples/*.qet $BUNDLE/Contents/Resources/examples
echo "Copying examples in the bundle..."
mkdir $BUNDLE/Contents/Resources/examples
cp ${current_dir}/examples/*.qet $BUNDLE/Contents/Resources/examples
fi
if [ -d "${QET_FONTS_DIR}" ]; then
echo "Copying fonts in the bundle... "
mkdir $BUNDLE/Contents/Resources/fonts
cp ${current_dir}/fonts/*.ttf $BUNDLE/Contents/Resources/fonts
echo "Copying fonts in the bundle..."
mkdir $BUNDLE/Contents/Resources/fonts
cp ${current_dir}/fonts/*.ttf $BUNDLE/Contents/Resources/fonts
fi
if [ -d "${QET_LICENSES_DIR}" ]; then
@@ -240,98 +228,282 @@ if [ -d "${QET_LICENSES_DIR}" ]; then
cp -R -L ${QET_LICENSES_DIR} $BUNDLE/Contents/Resources/licenses
fi
codesign --force --deep --sign --timestamp -s "Developer ID Application: Laurent TRINQUES (Y73WZ6WZ5X)" --options=runtime $BUNDLE
### create zip tarball ###############################################
### Sign the bundle #################################################
# Sign in correct order: all dylibs first (including flat libs copied
# by macdeployqt from Homebrew), then frameworks, plugins, bundle last.
echo
echo "______________________________________________________________"
echo "Create zip tarball:"
echo "Code signing (dylibs -> frameworks -> plugins -> bundle):"
/usr/bin/ditto -c -k --keepParent $BUNDLE "build-aux/mac-osx/${APPNAME}-$VERSION-r$HEAD-arm64.zip"
# 1. Sign all flat .dylib files in Frameworks/
echo "-- Signing dylibs in Frameworks/..."
find "$BUNDLE/Contents/Frameworks" -name "*.dylib" | while read lib; do
echo " $(basename $lib)"
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
### notarize zip tarball ###############################################
echo -e "\033[1;31mWould you like to upload for Notarize packages "${APPNAME}"-"$VERSION"-"r$HEAD-arm64.zip", n/Y?.\033[m"
# 2. Sign .framework bundles
echo "-- Signing .framework bundles..."
find "$BUNDLE/Contents/Frameworks" -maxdepth 1 -name "*.framework" | while read fw; do
echo " $(basename $fw)"
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$fw"
done
# 3. Sign plugins
echo "-- Signing plugins..."
find "$BUNDLE/Contents/PlugIns" \( -name "*.dylib" -o -name "*.so" \) | while read lib; do
echo " $(basename $lib)"
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
# 4. Sign any dylibs in MacOS/
echo "-- Signing dylibs in MacOS/..."
find "$BUNDLE/Contents/MacOS" -name "*.dylib" | while read lib; do
echo " $(basename $lib)"
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
# 5. Sign the main executable explicitly
echo "-- Signing main executable..."
codesign --force --sign "$IDENTITY" --timestamp --options=runtime \
"$BUNDLE/Contents/MacOS/$APPNAME"
# 6. Sign the bundle itself last
echo "-- Signing bundle..."
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$BUNDLE"
# 7. Verify
echo
echo "Verifying bundle signature..."
codesign --verify --deep --strict --verbose=2 "$BUNDLE"
if [ $? -ne 0 ]; then
echo "ERROR: bundle signature verification failed, aborting."
exit 1
fi
spctl -a -vv "$BUNDLE"
echo "Bundle signature OK."
### Notarize the .app (via temporary ZIP) ###########################
echo
echo "______________________________________________________________"
echo "Create temporary ZIP for notarization:"
NOTARIZE_ZIP="/tmp/${APPNAME}-$VERSION-r$HEAD-arm64-notarize.zip"
/usr/bin/ditto -c -k --keepParent "$BUNDLE" "$NOTARIZE_ZIP"
echo -e "\033[1;31mWould you like to notarize the .app \"${APPNAME}-${VERSION}-r${HEAD}\", n/Y?\033[m"
read a
if [[ $a == "Y" || $a == "y" ]]; then
echo
echo "______________________________________________________________"
echo "Notarize zip tarball:"
xcrun notarytool submit build-aux/mac-osx/${APPNAME}-$VERSION-r$HEAD-arm64.zip --keychain-profile "org.qelectrotech" --wait
echo
echo "______________________________________________________________"
echo "Notarizing .app:"
xcrun notarytool submit "$NOTARIZE_ZIP" --keychain-profile "org.qelectrotech" --wait
if [ $? -ne 0 ]; then
echo "ERROR: notarization failed. Check the log with:"
echo " xcrun notarytool log <submission-id> --keychain-profile org.qelectrotech"
rm -f "$NOTARIZE_ZIP"
exit 1
fi
else
echo -e "\033[1;33mExit.\033[m"
echo -e "\033[1;33mExit.\033[m"
fi
### The end, process is done ##########################################
echo "Cleaning up temporary notarization ZIP..."
rm -f "$NOTARIZE_ZIP"
echo
echo "______________________________________________________________"
echo "The process of creating deployable application zip is done."
echo The disque image is in the folder \'build-aux/mac-osx\'.
# Affiche les mise à jour depuis l'ancienne revision
#if [ ! $(($HEAD - $revAv)) -eq 0 ] ; then
# echo
# echo "There are new updates. This numero of revision is $HEAD."
# svn log -l $(($HEAD - $revAv))
#else
# echo
# echo "There are not new updates. This numero of revision is $HEAD."
# fi
# echo
### Staple the .app #################################################
# La version en local n'est pas conforme à la dernière version svn
# svnversion | grep -q '[MS:]' ; if [ $? -eq 0 ] ; then
# echo Please note that the latest \local version is $(svnversion).
# echo This is not the same version as the deposit.
# echo You can use \'svn diff\' to see the differences.
# echo And use \'svn revert \<fichier\>\' to delete the difference.
# echo To go back, you can use svn update -r 360
# echo to go to revision number 360.
# echo
#fi
# Clean up disk folder
echo 'Cleaning up... '
rm "build-aux/mac-osx/${APPNAME}-$VERSION-r$HEAD-arm64.zip"
# staple the app
echo -e "\033[1;31mWould you like to staple the app MacOS packages "${APPNAME}"-"$VERSION"-"r$HEAD", n/Y?.\033[m"
echo -e "\033[1;31mWould you like to staple the .app \"${APPNAME}-${VERSION}-r${HEAD}\", n/Y?\033[m"
read a
if [[ $a == "Y" || $a == "y" ]]; then
xcrun stapler staple -v $BUNDLE
xcrun stapler staple -v "$BUNDLE"
if [ $? -ne 0 ]; then
echo "ERROR: stapling .app failed."
exit 1
fi
echo "Verifying staple on .app..."
xcrun stapler validate -v "$BUNDLE"
spctl -a -vv "$BUNDLE"
echo ".app stapled OK."
else
echo -e "\033[1;33mExit.\033[m"
echo -e "\033[1;33mExit.\033[m"
fi
### Create staging folder with Applications symlink #################
# The staging folder contains the .app and a symlink to /Applications
# so the user can drag-and-drop to install directly from the DMG.
echo
echo "______________________________________________________________"
echo "Re Create zip tarball:"
echo "Preparing DMG staging folder:"
/usr/bin/ditto -c -k --sequesterRsrc --keepParent $BUNDLE "build-aux/mac-osx/${APPNAME}-$VERSION-r$HEAD-arm64.zip"
rm -rf "$STAGING"
mkdir -p "$STAGING"
cp -R "$BUNDLE" "$STAGING/"
ln -s /Applications "$STAGING/Applications"
echo "Staging folder ready: $STAGING"
### Create writable DMG (UDRW) ######################################
# We use a writable DMG first so we can re-sign the .app inside
# after hdiutil copies it (hdiutil can invalidate Sealed Resources
# during the copy, so we must re-sign inside the mounted volume).
# Clean up disk folder
echo 'Cleaning up... '
rm -rf $BUNDLE
echo
echo "______________________________________________________________"
echo "Create writable DMG (UDRW) and re-sign .app inside:"
rm -f "$RW_DMG"
hdiutil create \
-volname "QElectroTech $VERSION" \
-srcfolder "$STAGING" \
-ov \
-format UDRW \
-fs HFS+ \
"$RW_DMG"
#rsync to TF DMG builds
echo -e "\033[1;31mWould you like to upload MacOS packages "${APPNAME}"-"$VERSION"-"r$HEAD-arm64.zip", n/Y?.\033[m"
if [ $? -ne 0 ]; then
echo "ERROR: hdiutil failed to create writable DMG."
rm -rf "$STAGING"
exit 1
fi
# Mount the writable DMG
rm -rf "$MOUNT_POINT"
mkdir -p "$MOUNT_POINT"
hdiutil attach "$RW_DMG" -mountpoint "$MOUNT_POINT" -nobrowse -noverify
if [ $? -ne 0 ]; then
echo "ERROR: failed to mount writable DMG."
rm -f "$RW_DMG"
rm -rf "$STAGING"
exit 1
fi
# Re-sign all binaries inside the mounted DMG
echo "-- Re-signing dylibs inside DMG..."
find "$MOUNT_POINT/$BUNDLE/Contents/Frameworks" -name "*.dylib" | while read lib; do
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
find "$MOUNT_POINT/$BUNDLE/Contents/Frameworks" -maxdepth 1 -name "*.framework" | while read fw; do
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$fw"
done
find "$MOUNT_POINT/$BUNDLE/Contents/PlugIns" \( -name "*.dylib" -o -name "*.so" \) | while read lib; do
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
echo "-- Re-signing main executable inside DMG..."
codesign --force --sign "$IDENTITY" --timestamp --options=runtime \
"$MOUNT_POINT/$BUNDLE/Contents/MacOS/$APPNAME"
echo "-- Re-signing bundle inside DMG..."
codesign --force --sign "$IDENTITY" --timestamp --options=runtime \
"$MOUNT_POINT/$BUNDLE"
# Verify signature inside the mounted DMG
echo "Verifying bundle signature inside DMG..."
codesign --verify --deep --strict --verbose=2 "$MOUNT_POINT/$BUNDLE"
if [ $? -ne 0 ]; then
echo "ERROR: bundle signature invalid inside DMG, aborting."
hdiutil detach "$MOUNT_POINT"
rm -f "$RW_DMG"
rm -rf "$STAGING" "$MOUNT_POINT"
exit 1
fi
echo "Bundle signature inside DMG OK."
# Detach the writable DMG
hdiutil detach "$MOUNT_POINT"
### Convert UDRW to final compressed UDZO ###########################
echo
echo "______________________________________________________________"
echo "Convert to final compressed DMG (UDZO):"
mkdir -p "build-aux/mac-osx"
rm -f "$DMG_PATH"
hdiutil convert "$RW_DMG" \
-format UDZO \
-o "$DMG_PATH"
if [ $? -ne 0 ]; then
echo "ERROR: hdiutil convert failed."
rm -f "$RW_DMG"
rm -rf "$STAGING" "$MOUNT_POINT"
exit 1
fi
rm -f "$RW_DMG"
rm -rf "$STAGING" "$MOUNT_POINT"
### Sign the final DMG ##############################################
echo "Signing final DMG..."
codesign --sign "$IDENTITY" --timestamp "$DMG_PATH"
### Notarize and staple the final DMG ###############################
echo -e "\033[1;31mWould you like to notarize the DMG \"${DMG_NAME}\", n/Y?\033[m"
read a
if [[ $a == "Y" || $a == "y" ]]; then
cp -Rf "build-aux/mac-osx/${APPNAME}-$VERSION-r$HEAD-arm64.zip" /Users/laurent/MAC_OS_X/
rsync -e ssh -av --delete-after --no-owner --no-g --chmod=g+w --progress --exclude='.DS_Store' /Users/laurent/MAC_OS_X/ server:download.qelectrotech.org/qet/builds/MAC_OS_X/arm64/
if [ $? != 0 ]; then
{
echo "RSYNC ERROR: problem syncing ${APPNAME}-$VERSION-r$HEAD-arm64.zip"
rsync -e ssh -av --delete-after --no-owner --no-g --chmod=g+w --progress --exclude='.DS_Store' /Users/laurent/MAC_OS_X/ server:download.qelectrotech.org/qet/builds/MAC_OS_X/arm64/
echo
echo "______________________________________________________________"
echo "Notarizing DMG:"
xcrun notarytool submit "$DMG_PATH" --keychain-profile "org.qelectrotech" --wait
if [ $? -ne 0 ]; then
echo "ERROR: DMG notarization failed. Check the log with:"
echo " xcrun notarytool log <submission-id> --keychain-profile org.qelectrotech"
exit 1
fi
} fi
echo "Stapling DMG..."
xcrun stapler staple "$DMG_PATH"
if [ $? -ne 0 ]; then
echo "ERROR: stapling DMG failed."
exit 1
fi
echo "DMG notarized and stapled OK."
echo "Verifying final DMG..."
spctl -a -vv "$DMG_PATH"
else
echo -e "\033[1;33mExit.\033[m"
echo -e "\033[1;33mExit.\033[m"
fi
### Clean up bundle #################################################
echo "Cleaning up bundle..."
rm -rf "$BUNDLE"
### The end #########################################################
echo
echo "______________________________________________________________"
echo "The process is done."
echo "DMG is in the folder 'build-aux/mac-osx'."
### Upload via rsync ################################################
echo -e "\033[1;31mWould you like to upload MacOS package \"${DMG_NAME}\", n/Y?\033[m"
read a
if [[ $a == "Y" || $a == "y" ]]; then
cp -Rf "$DMG_PATH" /Users/laurent/MAC_OS_X/
rsync -e ssh -av --delete-after --no-owner --no-g --chmod=g+w \
--progress --exclude='.DS_Store' \
/Users/laurent/MAC_OS_X/ \
server:download.qelectrotech.org/qet/builds/MAC_OS_X/arm64/
if [ $? != 0 ]; then
echo "RSYNC ERROR: problem syncing ${DMG_NAME}, retrying..."
rsync -e ssh -av --delete-after --no-owner --no-g --chmod=g+w \
--progress --exclude='.DS_Store' \
/Users/laurent/MAC_OS_X/ \
server:download.qelectrotech.org/qet/builds/MAC_OS_X/arm64/
fi
else
echo -e "\033[1;33mExit.\033[m"
fi
+119
View File
@@ -0,0 +1,119 @@
# publiccode.yml — QElectroTech
# Standard: https://yml.publiccode.tools/ (v0.7)
# Place at the root of the main repository (qelectrotech-source-mirror)
# Validate before submission: https://publiccode-editor.developers.italia.it/
publiccodeYmlVersion: "0.4"
name: QElectroTech
applicationSuite: QElectroTech
url: "https://github.com/qelectrotech/qelectrotech-source-mirror.git"
landingURL: "https://qelectrotech.org/"
releaseDate: "2026-01-25"
softwareVersion: "0.100"
developmentStatus: stable
softwareType: standalone/desktop
platforms:
- windows
- mac
- linux
- bsd
# NB: check/adjust against the official category list
# (https://yml.publiccode.tools/categories.html) before submission
categories:
- cad
- it-development
- engineering
description:
en:
shortDescription: >-
Free electrical diagram editor, also used for plumbing, hydraulics,
pneumatics, automotive wiring and photovoltaic schematics.
longDescription: >-
QElectroTech (QET) is a free and open source CAD/CAE application to
create complex industrial and domestic electrical diagrams. While
originally focused on electrical engineering, it is also widely used
for plumbing, geothermal, air-conditioning, layout, hydraulics,
pneumatics, home automation, automotive wiring, process diagrams
(P&ID), photovoltaic installations and many other technical domains.
The software ships with a community-maintained library of over 9000
standardized symbols. It runs identically on Windows, macOS, Linux
and BSD, and files created on one platform can be opened on another
without conversion.
QET has been developed since 2007 and community-maintained since
2013. It is notably used as a free alternative to commercial
software such as EPLAN or SEE Electrical, by hobbyists, teachers,
students, professional electricians and companies worldwide.
documentation: "https://qelectrotech.github.io/qelectrotech-doc/index.html"
apiDocumentation: ""
features:
- Multi-domain electrical and schematic diagram creation
- Library of over 9000 standardized symbols
- Multi-sheet project management with configurable title blocks
- DXF import/export (via dxf2elmt)
- Bill of materials and terminal strip table generation
- "Companion tools: QET-elementScaler (symbol scaling) and qet_tb_generator (terminal block table generation)"
- Interface translated into 25 languages
- "Cross-platform: Windows, macOS, Linux, BSD"
legal:
license: GPL-2.0-only
mainCopyrightOwner: QElectroTech community
repoOwner: QElectroTech community
maintenance:
type: community
contacts:
- name: Laurent Trinques (scorpio810)
email: scorpio@qelectrotech.org
localisation:
localisationReady: true
availableLanguages:
- en
- fr
- ar
- ca
- cs
- da
- de
- el
- es
- hr
- hu
- it
- ja
- ko
- mn
- nb
- nl
- pl
- pt
- pt-BR
- ro
- ru
- sk
- sl
- sr
- sv
- tr
- uk
- zh
dependsOn:
open:
- name: Qt
versionMin: "5.15"
- name: KF5 (KDE Frameworks)
optional: true
+1 -1
Submodule pugixml updated: 5a1892b321...27b68329de
+39 -3
View File
@@ -171,7 +171,11 @@ HEADERS += $$files(sources/*.h) \
$$files(sources/xml/*.h) \
$$files(sources/dxf/*.h) \
$$files(sources/qet_elementscaler/*.h) \
$$files(sources/svg/*.h)
$$files(sources/svg/*.h) \
$$files(sources/import/edz/*.h) \
$$files(sources/import/edz/lzma/*.h) \
$$files(sources/logging/*.h) \
$$files(sources/logging/ui/*.h)
SOURCES += $$files(sources/*.cpp) \
$$files(sources/editor/*.cpp) \
@@ -215,11 +219,20 @@ SOURCES += $$files(sources/*.cpp) \
$$files(sources/xml/*.cpp) \
$$files(sources/dxf/*.cpp) \
$$files(sources/qet_elementscaler/*.cpp) \
$$files(sources/svg/*.cpp)
$$files(sources/svg/*.cpp) \
$$files(sources/import/edz/*.cpp) \
$$files(sources/import/edz/lzma/*.c) \
$$files(sources/logging/*.cpp) \
$$files(sources/logging/ui/*.cpp)
# Needed for use promote QTreeWidget in terminalstripeditor.ui
INCLUDEPATH += sources/TerminalStrip/ui
# Needed for the EPLAN .edz importer (PR #513) and its bundled lzma/7z sources
INCLUDEPATH += sources/import/edz
INCLUDEPATH += sources/import/edz/lzma
# Liste des fichiers qui seront incorpores au binaire en tant que ressources Qt
RESOURCES += qelectrotech.qrc
@@ -230,7 +243,30 @@ RESOURCES += qelectrotech.qrc
TRANSLATIONS += lang/*.ts
# Modules Qt utilises par l'application
QT += xml svg network sql widgets printsupport concurrent KWidgetsAddons KCoreAddons
QT += xml svg network sql widgets printsupport concurrent gui-private
# KDE Frameworks 5 (KWidgetsAddons, KCoreAddons) are optional.
#
# Pass CONFIG+=no_kf5 to qmake to build against the Qt-only replacements in
# sources/ui/nokde instead. This mirrors the CMake option BUILD_WITH_KF5=OFF
# and is the easiest route on Windows/MinGW, where KF5 is awkward to obtain.
#
# qmake CONFIG+=no_kf5 qelectrotech.pro
#
# sources/ui/nokde is only added to the include path in that configuration, so
# a normal KF5 build is unaffected.
no_kf5 {
DEFINES += BUILD_WITHOUT_KF5
INCLUDEPATH += sources/ui/nokde
HEADERS += $$files(sources/ui/nokde/*.h)
SOURCES += $$files(sources/ui/nokde/*.cpp)
} else {
QT += KWidgetsAddons KCoreAddons
}
# Private Qt GUI headers (needed for QPdfEngine::drawHyperlink)
# gui-private should add this automatically, but some distros need it explicit
INCLUDEPATH += $$[QT_INSTALL_HEADERS]/QtGui/$$[QT_VERSION]/QtGui
# UI DESIGNER FILES AND GENERATION SOURCES FILES
FORMS += $$files(sources/richtext/*.ui) \
+1
View File
@@ -222,6 +222,7 @@
<file>ico/24x16/dk.png</file>
<file>ico/24x16/br.png</file>
<file>ico/22x22/grid.png</file>
<file>ico/22x22/guides.png</file>
<file>ico/22x22/terminalstrip.png</file>
<file>ico/16x16/diagram.png</file>
<file>ico/16x16/edit-clear-locationbar-rtl.png</file>
@@ -143,8 +143,12 @@ ElementsLocation ECHSFileToFile::copyElement(ElementsLocation &source, ElementsL
//On windows when user drag and drop an element from the common elements collection
//to the custom elements collection, the element file stay in read only mode, and so
//user can't save the element
#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
QNtfsPermissionCheckGuard ntfs_guard;
#else
extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
qt_ntfs_permission_lookup++;
#endif
QFile file(destination.fileSystemPath() % "/" % new_elmt_name);
if (!file.isWritable()) {
if (!file.setPermissions(file.permissions() | QFileDevice::WriteUser)) {
@@ -152,7 +156,9 @@ ElementsLocation ECHSFileToFile::copyElement(ElementsLocation &source, ElementsL
<< " in ECHSFileToFile::copyElement";
}
}
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
qt_ntfs_permission_lookup--;
#endif
#endif
return ElementsLocation (destination.fileSystemPath() % "/" % new_elmt_name);
}
@@ -469,7 +475,7 @@ bool ElementCollectionHandler::setNames(ElementsLocation &location,
root.appendChild(name_list.toXml(document));
QString filepath = location.fileSystemPath()
+ "/qet_directory";
% "/qet_directory";
if (!QET::writeXmlFile(document, filepath)) {
qDebug() << "ElementCollectionHandler::setNames : write qet-directory file failed";
return false;
@@ -25,12 +25,8 @@
#include "xmlelementcollection.h"
#include "xmlprojectelementcollectionitem.h"
#include <QFutureWatcher>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
#include <QtConcurrentMap>
#else
#include <QtConcurrentRun>
#endif
/**
@brief ElementsCollectionModel::ElementsCollectionModel
@@ -297,11 +293,8 @@ void ElementsCollectionModel::loadCollections(bool common_collection,
this, &ElementsCollectionModel::loadingFinished);
connect(watcher, &QFutureWatcher<void>::finished, watcher, &QFutureWatcher<void>::deleteLater);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
m_future = QtConcurrent::map(m_items_list_to_setUp, setUpData);
#else
qDebug() << "Help code for QT 6 or later";
#endif
watcher->setFuture(m_future);
}
@@ -538,6 +531,16 @@ QList<QETProject *> ElementsCollectionModel::project() const
*/
void ElementsCollectionModel::highlightUnusedElement()
{
//Reset only the items currently highlighted in red, so elements that
//are no longer unused lose the highlight. Scoping to the red
//Dense4Pattern avoids touching other backgrounds (e.g. the amber
//"show this dir" highlight) and avoids needless updates on big
//collections (issue #159).
for (ElementCollectionItem *eci : items())
if (eci->background().style() == Qt::Dense4Pattern &&
eci->background().color() == Qt::red)
eci->setBackground(QBrush());
QList <ElementsLocation> unused;
foreach (QETProject *project, m_project_list)
@@ -19,6 +19,7 @@
#include "../editor/ui/qetelementeditor.h"
#include "../elementscategoryeditor.h"
#include "../import/edz/edzimporter.h"
#include "../newelementwizard.h"
#include "../qetapp.h"
#include "../qetdiagrameditor.h"
@@ -32,8 +33,14 @@
#include "fileelementcollectionitem.h"
#include "xmlprojectelementcollectionitem.h"
#include <QCheckBox>
#include <QDesktopServices>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QLabel>
#include <QMenu>
#include <QPushButton>
#include <QTimer>
#include <QUrl>
#include <QVBoxLayout>
@@ -158,6 +165,8 @@ void ElementsCollectionWidget::setUpAction()
tr("Nouveau dossier"), this);
m_new_element = new QAction(QET::Icons::ElementNew,
tr("Nouvel élément"), this);
m_import_edz = new QAction(QET::Icons::ElementNew,
tr("Importer une pièce EPLAN (.edz)…"), this);
m_show_this_dir = new QAction(QET::Icons::FolderOnlyThis,
tr("Afficher uniquement ce dossier"),
this);
@@ -247,6 +256,8 @@ void ElementsCollectionWidget::setUpConnection()
this, &ElementsCollectionWidget::newDirectory);
connect(m_new_element, &QAction::triggered,
this, &ElementsCollectionWidget::newElement);
connect(m_import_edz, &QAction::triggered,
this, &ElementsCollectionWidget::importEdz);
connect(m_show_this_dir, &QAction::triggered,
this, &ElementsCollectionWidget::showThisDir);
connect(m_show_all_dir, &QAction::triggered,
@@ -329,6 +340,7 @@ void ElementsCollectionWidget::customContextMenu(const QPoint &point)
{
if (!feci->isMacrosCollection()) {
m_context_menu->addAction(m_new_element);
m_context_menu->addAction(m_import_edz);
}
m_context_menu->addAction(m_new_directory);
if (!feci->isCollectionRoot())
@@ -599,6 +611,108 @@ void ElementsCollectionWidget::newElement()
&ElementsCollectionWidget::locationWasSaved);
}
/**
@brief ElementsCollectionWidget::confirmEdzImportTerms
Show the EPLAN (.edz) import warning dialog. The user must tick the
acknowledgement checkbox before the Import button is enabled.
@return true if the user accepted the terms, false otherwise.
*/
bool ElementsCollectionWidget::confirmEdzImportTerms()
{
QDialog dialog(this);
dialog.setWindowTitle(tr("Avertissement — Importation d'un fichier EPLAN (.edz)"));
QLabel *text = new QLabel(
tr("Le format .edz peut provenir de deux sources différentes :\n"
"\n"
"• Le portail EPLAN Data Portal (dataportal.eplan.com), soumis "
"aux conditions d'utilisation de l'environnement EPLAN Cloud ;\n"
"• Le site d'un fabricant de composants (ou d'un distributeur) "
"qui met ses fichiers .edz à disposition directement, selon ses "
"propres conditions.\n"
"\n"
"QElectroTech ne peut pas déterminer automatiquement l'origine "
"du fichier que vous importez, ni les conditions qui s'y "
"appliquent.\n"
"\n"
"En important ce fichier, vous confirmez que :\n"
"\n"
"• vous connaissez son origine et êtes autorisé à l'utiliser "
"dans ce contexte, au regard des conditions applicables à cette "
"source ;\n"
"• cette importation est effectuée à vos propres risques et "
"responsabilité ;\n"
"• ni QElectroTech, ni ses mainteneurs, ni ses contributeurs ne "
"peuvent être tenus responsables d'une utilisation non conforme "
"de ces données."),
&dialog);
text->setWordWrap(true);
QCheckBox *accept_box = new QCheckBox(
tr("J'ai lu et j'accepte ces conditions."), &dialog);
QDialogButtonBox *buttons = new QDialogButtonBox(
QDialogButtonBox::Cancel, &dialog);
QPushButton *import_button = buttons->addButton(
tr("Importer"), QDialogButtonBox::AcceptRole);
import_button->setDefault(true);
import_button->setEnabled(false);
connect(accept_box, &QCheckBox::toggled,
import_button, &QPushButton::setEnabled);
connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
QVBoxLayout *layout = new QVBoxLayout(&dialog);
layout->addWidget(text);
layout->addWidget(accept_box);
layout->addWidget(buttons);
return dialog.exec() == QDialog::Accepted;
}
/**
@brief ElementsCollectionWidget::importEdz
Import an EPLAN Data Portal part (.edz) as a QET element into the directory
pointed at by the context menu.
*/
void ElementsCollectionWidget::importEdz()
{
ElementCollectionItem *eci = elementCollectionItemForIndex(
m_index_at_context_menu);
if (!eci || eci->type() != FileElementCollectionItem::Type) {
return;
}
FileElementCollectionItem *feci =
static_cast<FileElementCollectionItem*>(eci);
if (feci->isCommonCollection() || !feci->isDir()) {
return;
}
if (!confirmEdzImportTerms()) {
return;
}
const QString edz_path = QFileDialog::getOpenFileName(
this, tr("Importer une pièce EPLAN"), QString(),
tr("Pièces EPLAN (*.edz)"));
if (edz_path.isEmpty()) {
return;
}
EdzImporter importer;
if (!importer.importToDirectory(edz_path, feci->fileSystemPath())) {
QET::QetMessageBox::critical(
this, tr("Import EPLAN"),
tr("Impossible d'importer cette pièce :\n%1")
.arg(importer.errorString()));
return;
}
reload();
}
/**
@brief ElementsCollectionWidget::showThisDir
Hide all directories except the pointed dir;
@@ -830,7 +944,7 @@ void ElementsCollectionWidget::search()
}
//start the search when text have at least 3 letters.
if (text.count() < 3) {
if (text.size() < 3) {
return;
}
@@ -72,6 +72,8 @@ class ElementsCollectionWidget : public QWidget
void editDirectory();
void newDirectory();
void newElement();
bool confirmEdzImportTerms();
void importEdz();
void showThisDir();
void resetShowThisDir();
void dirProperties();
@@ -112,6 +114,7 @@ class ElementsCollectionWidget : public QWidget
*m_edit_dir,
*m_new_directory,
*m_new_element,
*m_import_edz,
*m_show_this_dir,
*m_show_all_dir,
*m_dir_propertie;
@@ -523,7 +523,9 @@ bool ElementsLocation::isCompanyCollection() const
*/
bool ElementsLocation::isCustomCollection() const
{
return fileSystemPath().startsWith(QETApp::customElementsDirN());
const QString dir = QETApp::customElementsDirN();
const QString path = fileSystemPath();
return path == dir || path.startsWith(dir + QLatin1Char('/'));
}
/**
@@ -701,11 +703,11 @@ pugi::xml_document ElementsLocation::pugiXml() const
if (!m_project)
{
#ifndef Q_OS_LINUX
if (docu.load_file(m_file_system_path.toStdString().c_str())) {
if (docu.load_file(m_file_system_path.toStdWString().c_str())) {
docu.save(m_string_stream);
}
#else
docu.load_file(m_file_system_path.toStdString().c_str());
docu.load_file(m_file_system_path.toStdWString().c_str());
#endif
}
else
@@ -802,14 +804,15 @@ bool ElementsLocation::setXml(const QDomDocument &xml_document) const
QString path_ = collectionPath(false);
QRegularExpression rx("^(.*)/(.*\\.elmt)$");
QRegularExpressionMatch match = rx.match(path_);
if (rx.exactMatch(path_))
if (match.hasMatch())
{
return project()
->embeddedElementCollection()
->addElementDefinition(
rx.cap(1),
rx.cap(2),
match.captured(1),
match.captured(2),
xml_document.documentElement());
}
else
@@ -43,6 +43,14 @@ ElementsTreeView::ElementsTreeView(QWidget *parent) :
{
// force du noir sur une alternance de blanc (comme le schema) et de gris
// clair, avec du blanc sur bleu pas trop fonce pour la selection
//
// Element icons are rendered with colors read directly from each .elmt
// file (almost always black linework, matching printed-schematic
// convention) onto a transparent background -- so this view must keep
// a light background regardless of the OS/desktop theme, or the icons
// become invisible on dark themes. QAbstractItemView paints its rows
// using the viewport's palette, not the view's own, so the palette
// must be applied to both to actually take effect under every style.
QPalette qp = palette();
qp.setColor(QPalette::Text, Qt::black);
qp.setColor(QPalette::Base, Qt::white);
@@ -50,6 +58,7 @@ ElementsTreeView::ElementsTreeView(QWidget *parent) :
qp.setColor(QPalette::Highlight, QColor("#678db2"));
qp.setColor(QPalette::HighlightedText, Qt::black);
setPalette(qp);
viewport()->setPalette(qp);
}
/**
@@ -22,7 +22,39 @@
#include "../qeticons.h"
#include "elementslocation.h"
#include <QApplication>
#include <QDir>
#include <QPainter>
#include <QPixmap>
#include <QStyle>
namespace {
/**
@return the folder icon overlaid with a small warning badge in the
bottom-right corner. Used for a directory whose qet_directory could
not be read (@see FileElementCollectionItem::m_qet_directory_unreadable),
so the problem is visible in the tree itself and not only on hover
via the tooltip. Built once: same folder icon, same badge, every time.
*/
const QIcon &unreadableFolderIcon()
{
static const QIcon icon = []() {
QPixmap pixmap = QET::Icons::Folder.pixmap(16, 16);
const QPixmap badge = QApplication::style()
->standardIcon(QStyle::SP_MessageBoxWarning)
.pixmap(9, 9);
QPainter painter(&pixmap);
painter.drawPixmap(pixmap.width() - badge.width(),
pixmap.height() - badge.height(),
badge);
painter.end();
return QIcon(pixmap);
}();
return icon;
}
}
/**
@brief FileElementCollectionItem::FileElementCollectionItem
@@ -136,18 +168,41 @@ QString FileElementCollectionItem::localName()
}
else
{
// Fall back to the raw directory name (m_path) whenever the
// translated name can't be obtained -- qet_directory missing,
// unreadable (e.g. a Windows path-encoding issue with special
// characters, see bugtracker #332), malformed, or present but
// without a usable name entry -- rather than leaving the item
// blank.
QString display_name;
bool readable = false;
QString str(fileSystemPath() % "/qet_directory");
pugi::xml_document docu;
if(docu.load_file(str.toStdString().c_str()))
if (docu.load_file(str.toStdWString().c_str()))
{
if (QString(docu.document_element().name())
== "qet-directory")
{
readable = true;
NamesList nl;
nl.fromXml(docu.document_element());
setText(nl.name());
// Deliberately no fallback argument: a non-empty one
// is returned *before* NamesList::name() reaches its
// "first available translation" step, so passing
// m_path here would replace a perfectly good name in
// some other language with the raw directory name.
// The fallback belongs after the chain, not inside it.
display_name = nl.name();
}
}
setText(display_name.isEmpty() ? m_path : display_name);
// Only a file-level failure counts: a readable qet-directory
// with no entry for the current language is not an error,
// NamesList::name() resolves that on its own. Recorded here
// and reported by setUpData(), which sets the tooltip after
// this runs.
m_qet_directory_unreadable = !readable;
}
}
else if (isElement()) {
@@ -274,7 +329,9 @@ bool FileElementCollectionItem::isCompanyCollection() const
*/
bool FileElementCollectionItem::isCustomCollection() const
{
return fileSystemPath().startsWith(QETApp::customElementsDirN());
const QString dir = QETApp::customElementsDirN();
const QString path = fileSystemPath();
return path == dir || path.startsWith(dir + QLatin1Char('/'));
}
/**
@@ -325,10 +382,44 @@ void FileElementCollectionItem::setUpData()
{ search_list.append(context.value(key).toString()); }
search_list.append(localName(loc));
setData(search_list.join(" "));
// Tooltip: show what a truncated tree label can't - the full
// localized name and the descriptive element information.
// Reuses the location/context already parsed for the search
// index above; the collection path stays as the last line
// (it used to be the whole tooltip).
QStringList tip;
tip << localName(loc);
for (const auto &key : { QStringLiteral("description"),
QStringLiteral("manufacturer"),
QStringLiteral("manufacturer_reference") })
{
const QString value = context.value(key).toString();
if (!value.isEmpty())
tip << value;
}
tip << collectionPath();
tip.removeDuplicates();
setToolTip(tip.join(QLatin1Char('\n')));
return;
}
}
setToolTip(collectionPath());
// Falling back to the raw directory name keeps the folder usable, but
// on its own it hides the fact that a file is broken: the user sees a
// plausible name and never learns there is anything to repair. Say so
// above the collection path, which stays as the last line the way the
// element tooltip above builds it.
QStringList tip;
if (isDir() && m_qet_directory_unreadable)
{
tip << QObject::tr("Le fichier « %1 » est absent ou illisible : "
"le nom traduit de ce dossier n'a pas pu être lu, "
"son nom de dossier est affiché à la place.")
.arg(fileSystemPath() % "/qet_directory");
}
tip << collectionPath();
setToolTip(tip.join(QLatin1Char('\n')));
}
/**
@@ -339,6 +430,19 @@ void FileElementCollectionItem::setUpData()
*/
void FileElementCollectionItem::setUpIcon()
{
// Must return unconditionally once an icon is set: setIcon() calls
// setData(), which emits dataChanged() regardless of whether the new
// icon differs from the old one (QIcon has no meaningful equality).
// QTreeView responds to dataChanged() by recomputing the row's size
// hint, which re-enters data() for this same index -- so without this
// guard, any repeated setIcon() here recurses until the stack
// overflows. Confirmed by crash report on PR #633.
//
// This item's m_qet_directory_unreadable is already final by the time
// this can run at all: ElementsCollectionModel only attaches itself
// to the tree view (making data() reachable) from loadingFinished(),
// which fires after the QtConcurrent::map over every item -- this one
// included -- has completed. So there is no race to work around here.
if (!icon().isNull())
return;
@@ -358,7 +462,8 @@ void FileElementCollectionItem::setUpIcon()
else
{
if (isDir()) {
setIcon(QET::Icons::Folder);
setIcon(m_qet_directory_unreadable ? unreadableFolderIcon()
: QET::Icons::Folder);
} else {
if (m_path.endsWith(".qetmak")) {
setIcon(QIcon());
@@ -64,6 +64,11 @@ class FileElementCollectionItem : public ElementCollectionItem
private:
QString m_path;
/// True when this directory's qet_directory file is missing or
/// unreadable, so setUpData() can say so in the tooltip. Recorded
/// rather than acted on in localName(), because setUpData() resets
/// the tooltip afterwards and would otherwise discard it.
bool m_qet_directory_unreadable = false;
};
#endif // FILEELEMENTCOLLECTIONITEM2_H
@@ -186,23 +186,22 @@ QDomElement XmlElementCollection::child(const QDomElement &parent_element,
if (parent_element.ownerDocument() != m_dom_document)
return QDomElement();
//Get all childs element of parent_element
QDomNodeList child_list = parent_element.childNodes();
QString tag_name(child_name.endsWith(".elmt")? "element" : "category");
QList <QDomElement> found_dom_element;
for (int i=0 ; i<child_list.length() ; i++)
/* Walk the siblings directly instead of the previous
* childNodes()+item(i) double pass: QDomNodeList::item(i) restarts
* from the first child every call, which made this loop quadratic
* in the number of children. This lookup runs several times per
* element instance while loading a project, on the "import"
* category that holds every embedded definition. */
const QString tag_name = child_name.endsWith(QLatin1String(".elmt"))
? QStringLiteral("element") : QStringLiteral("category");
for (QDomElement child_element = parent_element.firstChildElement(tag_name) ;
!child_element.isNull() ;
child_element = child_element.nextSiblingElement(tag_name))
{
QDomElement child_element = child_list.item(i).toElement();
if (child_element.tagName() == tag_name)
found_dom_element << child_element;
if (child_element.attribute(QStringLiteral("name")) == child_name)
return child_element;
}
if (found_dom_element.isEmpty()) return QDomElement();
foreach (QDomElement elmt, found_dom_element)
if (elmt.attribute("name") == child_name)
return elmt;
return QDomElement();
}
+7
View File
@@ -263,6 +263,13 @@ QString NamesList::name(const QString &fallback_name) const
QString system_language = QETApp::langFromSetting();
if (! map_names[system_language].isEmpty())
return (map_names[system_language]);
// langFromSetting() may return a full locale (e.g. "de_DE") while element
// and folder names are keyed by the 2-letter language code ("de"). Try the
// base language before falling back to English, mirroring what setLanguage()
// does for the UI translations.
const QString base_language = system_language.section('_', 0, 0);
if (base_language != system_language && ! map_names[base_language].isEmpty())
return (map_names[base_language]);
if (! map_names["en"].isEmpty()) return (map_names["en"]);
if (! fallback_name.isEmpty()) return (fallback_name);
if (map_names.count()) return (map_names.begin().value());
@@ -16,7 +16,7 @@
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "searchandreplaceworker.h"
#include "../qetproject.h"
#include "../QPropertyUndoCommand/qpropertyundocommand.h"
#include "../diagram.h"
#include "../diagramcommands.h"
@@ -1,4 +1,4 @@
/*
/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
@@ -475,7 +475,7 @@ void SearchAndReplaceWidget::setUpConenctions()
connect(ui->m_tree_widget, &QTreeWidget::customContextMenuRequested, [this](const QPoint &pos)
{
if (m_diagram_hash.keys().contains(ui->m_tree_widget->currentItem()))
if (m_diagram_hash.contains(ui->m_tree_widget->currentItem()))
{
QMenu *menu = new QMenu(ui->m_tree_widget);
menu->addAction(m_select_elements);
@@ -951,28 +951,28 @@ void SearchAndReplaceWidget::on_m_tree_widget_itemDoubleClicked(
{
Q_UNUSED(column)
if (m_diagram_hash.keys().contains(item))
if (m_diagram_hash.contains(item))
{
QPointer<Diagram> diagram = m_diagram_hash.value(item);
if(diagram) {
diagram.data()->showMe();
}
}
else if (m_element_hash.keys().contains(item))
else if (m_element_hash.contains(item))
{
QPointer<Element> elmt = m_element_hash.value(item);
if (elmt && elmt->diagram()) {
elmt.data()->diagram()->showMe();
}
}
else if (m_text_hash.keys().contains(item))
else if (m_text_hash.contains(item))
{
QPointer<IndependentTextItem> text = m_text_hash.value(item);
if (text && text->diagram()) {
text.data()->diagram()->showMe();
}
}
else if (m_conductor_hash.keys().contains(item))
else if (m_conductor_hash.contains(item))
{
QPointer<Conductor> cond = m_conductor_hash.value(item);
if (cond && cond->diagram()) {
@@ -1013,7 +1013,7 @@ void SearchAndReplaceWidget::on_m_tree_widget_currentItemChanged(
m_last_selected.data()->setSelected(false);
}
if (m_element_hash.keys().contains(current))
if (m_element_hash.contains(current))
{
QPointer<Element> elmt = m_element_hash.value(current);
if (elmt)
@@ -1022,7 +1022,7 @@ void SearchAndReplaceWidget::on_m_tree_widget_currentItemChanged(
elmt.data()->setHighlighted(true);
}
}
else if (m_text_hash.keys().contains(current))
else if (m_text_hash.contains(current))
{
QPointer<IndependentTextItem> text = m_text_hash.value(current);
if (text)
@@ -1031,7 +1031,7 @@ void SearchAndReplaceWidget::on_m_tree_widget_currentItemChanged(
m_last_selected = text;
}
}
else if (m_conductor_hash.keys().contains(current))
else if (m_conductor_hash.contains(current))
{
QPointer<Conductor> cond = m_conductor_hash.value(current);
if (cond)
@@ -1144,7 +1144,7 @@ void SearchAndReplaceWidget::on_m_replace_pb_clicked()
&& qtwi->checkState(0) == Qt::Checked)
{
if (ui->m_folio_pb->text().endsWith(tr(" [édité]")) &&
m_diagram_hash.keys().contains(qtwi))
m_diagram_hash.contains(qtwi))
{
QPointer<Diagram> d = m_diagram_hash.value(qtwi);
if (d) {
@@ -1152,7 +1152,7 @@ void SearchAndReplaceWidget::on_m_replace_pb_clicked()
}
}
else if (ui->m_element_pb->text().endsWith(tr(" [édité]")) &&
m_element_hash.keys().contains(qtwi))
m_element_hash.contains(qtwi))
{
QPointer<Element> e = m_element_hash.value(qtwi);
if (e) {
@@ -1160,7 +1160,7 @@ void SearchAndReplaceWidget::on_m_replace_pb_clicked()
}
}
else if (!ui->m_replace_le->text().isEmpty() &&
m_text_hash.keys().contains(qtwi))
m_text_hash.contains(qtwi))
{
m_worker.m_indi_text = ui->m_replace_le->text();
QPointer<IndependentTextItem> t =
@@ -1171,7 +1171,7 @@ void SearchAndReplaceWidget::on_m_replace_pb_clicked()
}
else if (ui->m_conductor_pb->text().endsWith(tr(" [édité]")) &&
m_conductor_hash.keys().contains(qtwi))
m_conductor_hash.contains(qtwi))
{
QPointer<Conductor> c = m_conductor_hash.value(qtwi);
if (c) {
@@ -1187,7 +1187,7 @@ void SearchAndReplaceWidget::on_m_replace_pb_clicked()
QList <IndependentTextItem *>tl;
QList <Conductor *>cl;
if (m_diagram_hash.keys().contains(qtwi))
if (m_diagram_hash.contains(qtwi))
{
QPointer<Diagram> d =
m_diagram_hash.value(qtwi);
@@ -1195,7 +1195,7 @@ void SearchAndReplaceWidget::on_m_replace_pb_clicked()
dl.append(d.data());
}
}
else if (m_element_hash.keys().contains(qtwi))
else if (m_element_hash.contains(qtwi))
{
QPointer<Element> e =
m_element_hash.value(qtwi);
@@ -1203,7 +1203,7 @@ void SearchAndReplaceWidget::on_m_replace_pb_clicked()
el.append(e.data());
}
}
else if (m_text_hash.keys().contains(qtwi))
else if (m_text_hash.contains(qtwi))
{
QPointer<IndependentTextItem> t =
m_text_hash.value(qtwi);
@@ -1211,7 +1211,7 @@ void SearchAndReplaceWidget::on_m_replace_pb_clicked()
tl.append(t.data());
}
}
else if (m_conductor_hash.keys().contains(qtwi))
else if (m_conductor_hash.contains(qtwi))
{
QPointer<Conductor> c =
m_conductor_hash.value(qtwi);
@@ -18,6 +18,7 @@
#include "terminalstripdrawer.h"
#include <QPainter>
#include <QHash>
namespace TerminalStripDrawer {
@@ -265,13 +266,13 @@ void TerminalStripDrawer::paint(QPainter *painter)
painter->restore();
//Draw the bridges
for (const auto &points_ : qAsConst(bridges_anchor_points))
for (const auto &points_ : std::as_const(bridges_anchor_points))
{
painter->save();
auto pen_{painter->pen()};
pen_.setWidth(2);
painter->setPen(pen_);
painter->drawPolyline(QPolygonF{points_});
painter->drawPolyline(QPolygonF(points_));
painter->restore();
}
}

Some files were not shown because too many files have changed in this diff Show More