Commit Graph

124 Commits

Author SHA1 Message Date
ispyisail 28acbadaf0 Merge remote-tracking branch 'upstream/master' into pr983-rebase-check
# Conflicts:
#	sources/dataBase/projectdatabase.cpp
#	tests/qttest/CMakeLists.txt
2026-09-23 13:16:48 +12:00
ispyisail fa213d90d9 Add Ctrl+D: duplicate the selection, offset by a configured grid step (#991)
Split from #913's second suggestion. There was no shortcut for the common
"duplicate with offset" convention; the nearest existing feature,
"Collage multiple", is a different workflow (a dialog for repeating a
paste in a grid pattern, not a one-shot duplicate).

Ctrl+D copies the selection and places it immediately, offset by a
configured spacing and direction -- no interactive follow-the-cursor
step, unlike Ctrl+V. The first press (or after the setting is explicitly
reopened) shows DuplicateOffsetDialog: spacing in grid steps, direction
up/down/left/right. Every later press reuses whatever was confirmed then,
silently, so a row of copies is one key held down and tapped, not a
dialog every time -- unattended, repeatable stamping is the actual point
of a duplicate shortcut, which a dialog or an interactive placement step
on every press would defeat. A separate "Configurer la duplication..."
entry reopens the dialog on demand to change the setting later. Cancel
leaves the diagram untouched -- verified, not assumed: qet_diff against
the saved file shows 0 added.

Chaining ("keep tapping to lay out a row") needs no special handling:
QET already reselects whatever a paste just added
(PasteDiagramCommand::redo()), so the next Ctrl+D naturally continues
from the copy just placed rather than the original.

The offset is applied by hand rather than by asking paste()/fromXml() to
place the copy at a target position. Both of those feed the position
through Diagram::snapToGrid(), which reads
QApplication::keyboardModifiers() and rounds to the nearest PIXEL instead
of the grid whenever Ctrl is held -- and Ctrl is always held here, this
action's own shortcut being Ctrl+D. Measured before settling on this:
routing the offset through paste() first produced copies off-grid on both
axes, by an amount that tracked the selection's own bounding-box geometry
rather than being a fixed error -- caught by qet-mcp's qet_elements
against the saved file, not by eye. fromXml() is instead called with no
position argument at all (leaves every item at its source coordinates,
landing the copy on top of the originals -- (0,0) is not a position, this
is "keep the source coordinates"), and the offset is added directly with
setPos(). A plain addition cannot be off by a rounding rule that never
runs.

Conductors are not in the hand-translated set: fromXml() itself does not
reposition them either -- they load after elements are already in their
final place and take their geometry from their terminals, which have
already moved with the elements that own them. Verified this holds: drew
a conductor by hand between two elements (drag, not click-click),
selected both, Ctrl+D, and the new conductor correctly joins the two new
elements via qet_conductors -- not the originals, not a mix.

Verified end-to-end on a built binary via qet-mcp, not by eye:

  before          L2 (303,207)  L9 (512,196)     -- deliberately off-grid
  spacing=2, down    (303,227)     (512,216)      -- +0,+20 exactly
  same again, 2nd    (303,247)     (512,236)      -- +0,+20 again, chained

Both elements land exactly the configured offset from their immediate
source regardless of the selection's own alignment. Qt 6.10.2, ctest
11/11.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 11:30:30 +12:00
Laurent Trinques 6fc7e4a090 Merge pull request #965 from elevatormind/no-sqlite
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m54s
Remove SQLite library dependency while retaining project db export
2026-09-22 18:51:00 +02:00
ispyisail f777be05b4 Enforce read-only SQL with SQLite, not with a first-word check
projectDataBase::isReadOnlySelect() decides whether a query only reads
by looking at its first keyword and rejecting internal semicolons.
SQLite has allowed a CTE prefix in front of a data-modifying statement
since 3.8.3, so

    WITH x AS (SELECT 1) DELETE FROM element

begins with WITH, contains no semicolon, passes the check, and deletes
every row. UPDATE and INSERT go through the same way.

This is not only reachable from the custom-query box. ProjectDBModel::
fromXml() reads a <graphics_table>'s saved <query> straight out of the
.qet and fillValue() executes it, so a project file can carry the
statement. Reproduced against a build of this branch's parent, with no
scripting and no CLI flag beyond the export itself: a project whose
stored table query was replaced with the DELETE above exported a bill
of materials of 0 rows instead of 14, exit code 0, nothing logged. A
silently empty or -- with UPDATE -- silently altered BOM is the kind of
output someone orders parts from.

Fixed by asking SQLite about the statement it actually compiled.
sqlite3_prepare_v2() compiles without running, sqlite3_stmt_readonly()
reports on the compiled statement rather than on how it was spelled,
and the prepare tail catches a second statement structurally. The same
project now exports its 14 rows again and logs a reason for the
refusal, while an ordinary WITH ... SELECT in a project file still runs
untouched -- the fix is not "ban CTEs".

isReadOnlySelect() stays in front of it rather than being replaced:
SQLite considers ATTACH, BEGIN and several PRAGMAs read-only too, since
none of them change the contents of the database, so dropping the
statement-type allowlist would have widened what is accepted while
fixing what is executed.

The check lives in its own translation unit depending on nothing but
QString and SQLite, so tests/qttest/tst_sqlreadonly.cpp can link it
alone and exercise the security property without standing up a
QETProject: 18 assertions covering the three CTE-prefixed writes named
in the review, bare writes, trailing statements, comment-only input
(which compiles to a null statement sqlite3_stmt_readonly() must not be
handed) and a null connection (refused, not waved through). Confirmed
the suite discriminates by deliberately disabling the new check and
watching exactly the nine write-refusal assertions go red while the
accept cases stayed green.

ctest 13/13, qet-coherence-check and qet-pdflink-check clean on the
example corpus.

Reported in PR #980's review thread by @elevatormind and confirmed
against this code by @scorpio810; fixed here on its own because the
flaw is in already-released code and needs none of that branch to
reach.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 03:23:40 +12:00
Andre Rummler b76d8ce8a1 Fix: remove PUGIXML files from compilation fileset. This is redundant using the target approach and leads sometimes to failures. 2026-09-21 13:29:40 +02:00
elevatormind 78caeaec48 Merge branch 'qelectrotech:master' into no-sqlite 2026-09-21 07:34:32 +02:00
Kellermorph ebab58e4b3 Add diagram background color picker with adaptive border/titleblock
Replace the white/grey toggle (m_grey_background) with a full color
picker widget (DiagramBgColorToolButton) in the Affichage toolbar,
matching the existing ConductorColorToolButton UX:

- Preset colors: White, Off-white, Light grey, Grey, Dark grey, Black
- Recently used colors section
- "Autre couleur..." opens QColorDialog for any custom color
- "Couleur système" restores the default dark-mode inverted background

New features:
- Diagram::m_custom_background_color flag: when the user picks a
  custom color, PaletteGraphicsView skips lightness inversion so the
  chosen color is displayed as-is
- Border and titleblock text/lines automatically switch between black
  and white based on Diagram::background_color.lightness(), so a dark
  background always shows a visible light border and titleblock content
- "Couleur système" restores Qt::white + re-enables inversion

Files changed:
- New: sources/ui/diagrambgcolorbutton.h/.cpp
- sources/diagram.h/.cpp: added static m_custom_background_color flag
- sources/palettegraphicsview.cpp: skip inversion when custom bg active
- sources/bordertitleblock.cpp: adaptive border pen color
- sources/titleblocktemplate.cpp: adaptive ink color for cell borders/text
- sources/qetdiagrameditor.h/.cpp: replace toggle with new widget
- cmake/qet_compilation_vars.cmake: register new source files
2026-09-20 22:58:08 +02:00
Magnus Hellströmer 27dea3ffab refactor: use VACUUM INTO for database export
Remove the native SQLite backup API and direct SQLite library
dependency.
2026-09-20 20:49:02 +02:00
Laurent Trinques c256e2dd1a Merge pull request #958 from bhangart/fix/pin-fetchcontent-dependencies
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m37s
Fix/pin fetchcontent dependencies
2026-09-20 20:13:37 +02:00
Beat Hangartner 2e27f77b28 Added more verbose description of the difference between lightweight and annotated tags as comment. 2026-09-20 14:21:36 +02:00
Laurent Trinques 242f134e0f Revert "Revert "Draw the folio with inverted lightness on a dark palette"" 2026-09-20 13:05:13 +02:00
Laurent Trinques 75fafd5acc Revert "Draw the folio with inverted lightness on a dark palette" 2026-09-20 06:57:56 +02:00
Jeff Patterson eaf15faaa3 Move the dark canvas into PaletteGraphicsView and test it directly
The inverted painting, the rubber band replay and the changed()
receiver lived in DiagramView, which the unit tests cannot link, so the
update-flag regression was only covered through a stand-in view. They
now live in PaletteGraphicsView, a QGraphicsView subclass with no
other dependency, and DiagramView derives from it. The view tells a
subclass through paintingInverted(bool) when it renders for an
inverted display; DiagramView forwards that to the diagram. The grid
dot rule moves out of Diagram::drawBackground into
QET::Palette::gridDotColor().

tst_qetpalette now links the real class: gridDotColorSoftensInvertedDots,
paletteViewFollowsThePalette (light sheet, dark sheet at text contrast
with a red box still red and the paintingInverted calls in order, back
to light), paletteViewKeepsSceneUpdatesFlowing (three whole-scene
updates and a selection each repaint, scene set after construction),
paletteViewDrawsTheRubberBand.
2026-09-19 18:27:00 -05:00
Beat Hangartner 3f397f5f78 Explain why fetching by git tag is a supply chain risk
The pinning comment stated that a tag is mutable but not what an attacker
does with that, so the trade-off was hard to judge for anyone reviewing or
later undoing the pins. Spell out the mechanism: a tag is a name pointing
at a commit, anyone with push access upstream can force-push it elsewhere,
and FetchContent resolves it at build time, so a stolen maintainer account
or CI token makes every fresh build compile the attacker's code while
nothing changes here and the tag name still reads correctly. A commit hash
is derived from the content and cannot be moved that way.

Name the two cases where this was actually exploited: tj-actions/changed-
files in March 2025 (CVE-2025-30066), where tags v1 through v45.0.7 were
retargeted to a commit leaking CI secrets into build logs across more than
23,000 repositories, and aquasecurity/trivy-action in March 2026
(CVE-2026-33634), where 76 of 77 version tags were force-pushed to a
credential stealer for about twelve hours. Both were GitHub Actions rather
than CMake dependencies, which the comment says, because the point is the
shared mechanism of resolving a tag at build time.

Also document how to upgrade a pin, including that git ls-remote reports
the tag object for an annotated tag and the commit on the "^{}" line.

The note lives in fetch_pugixml.cmake, which fetch_kdeaddons.cmake and
fetch_singleapplication.cmake already refer to. Comments only; no build
behaviour changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 23:36:20 +02:00
Beat Hangartner 7ec13cbc1a Pin fetched dependencies to commit hashes instead of git tags
CMake fetches pugixml, SingleApplication and the three KDE Frameworks
modules by git tag. A tag is a mutable pointer that its owner can move,
so two builds of the same QElectroTech commit can silently get different
third-party sources, and a compromised upstream account can change what
every builder downloads without anything changing in this repository.
Pinning each dependency to the commit its tag currently points at closes
that, while keeping the tag name in a trailing comment so the intended
version stays readable.

No versions change. Every pinned commit is the one its tag resolves to,
checked with git ls-remote and confirmed by fetching each one and
verifying that git describe reports exactly the tag. The three KDE
modules live in separate repositories and therefore need separate
commits, so the single KF_GIT_TAG variable becomes three per-module
variables; passing -DKF_GIT_TAG=<ref> still selects one ref for all
three, unpinned, exactly as before, and KF_GIT_TAG stays defined so the
build summary in define_definitions.cmake is unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 23:05:39 +02:00
Laurent Trinques eb7abdfbc6 Merge pull request #946 from jp2images/feature-panels-follow-palette
Let the Collections and Projects panels follow the palette
2026-09-19 20:24:05 +02:00
Jeff Patterson 4e43cde063 Give icons a hover state through a proxy style
Hovering a tool button changed only its frame, and on the dark palette
Fusion's hover frame is too faint to notice, so nothing told the user
which button was under the pointer (GitHub #870, PR 3 of the plan).

QETStyle wraps the running style. For QIcon::Active it returns line-art
icons tinted in the palette's highlight color, lightened until the tint
reads at 3:1 on the Light role, which is the top of Fusion's hover
gradient. Colored icons keep their colors, using the same line-art rule
as misc/make_icon_themes.py. Every other mode goes to the base style.

Fusion also asks for QIcon::Active for the icon of a highlighted menu
item and paints it on the highlight bar, where the tint would vanish.
The generated pixmap is cached per icon and cannot tell a menu from a
toolbar, so drawControl(CE_MenuItem) hands the base style an icon whose
Active pixmap is in the HighlightedText color instead.

QETApp::initStyle() installs the proxy on every platform, keeping the
base style's object name so the Fusion checks still match.

tests/qttest/tst_qeticons: hovering changes the icon ink to the tint and
stays at 3:1 on both palettes, on a raised button and on a checked one
drawn sunken, where the frame gives no hover cue at all; a colored icon is unchanged on hover; a
highlighted menu item's icon reads at 3:1 on the highlight bar.
inkcontrast.h gains background() and ink() helpers for those checks.
2026-09-19 12:26:06 -05:00
Jeff Patterson 0395f90f75 Let the Collections and Projects panels follow the palette
Both panels forced a light palette on themselves (white rows, black
text, their own selection blue) so that element previews, which are
black line art drawn for the white sheet, would stay visible on a dark
desktop (bugtracker 335). On a dark palette the two docks were the
only white windows left.

The forced palettes are gone. Element previews are now kept as drawn,
on a transparent background, and adapted where they are shown:
ElementPreviewDelegate, installed on the collection tree, hands the
view a copy with its lightness inverted when the palette is dark
(QET::Palette::forPalette), so black ink becomes the palette's light
gray while colored icons such as folders stay as they are; the drag
pixmap is adapted the same way. A light palette shows the previews
untouched. This fixes bugtracker 335 on every dark desktop rather than
masking it with a white panel.

The preview cache stored the old white-sheet pictures; it records the
format now and drops a cache written before this change once. The
amber "show this directory" highlight sets black text so it reads on
both palettes. The Projects panel only shows icons from the icon
theme, which has a dark variant, so nothing else changes there.

tests/qttest/tst_qetpalette: the line-art rule tells ink from color;
inversion keeps hue and alpha; a preview reads at 3:1 on the Base
color of both palettes; in a tree on the dark palette the delegate
inverts a line-art icon and leaves a colored one alone.

Fixes #945.
2026-09-19 12:22:24 -05:00
ispyisail e42ebdc861 Add a conductor colour button to the Schéma toolbar (#461)
An electrician on the forum draws 400 V and 24 V circuits in the same
folio and wants the colour to be one click away
(qelectrotech.org/forum, viewtopic pid=23296). Today the quickest route
is F2, which opens a colour dialog and refuses to act unless exactly one
conductor is selected, so colouring a run means one conductor, one
dialog, at a time.

This adds a swatch button beside the auto-conductor actions. Picking a
colour does two things:

  - recolours every conductor currently selected, as ONE undo step;
  - becomes the colour of the next conductor drawn, through the
    LastUsedStyle mechanism #888 already added and Conductor's
    constructor already reads.

Either half is useful alone: with nothing selected it just sets the pen
for what comes next.

The menu lists the colours the trade names -- the three phases, neutral,
earth, and the ones used for control and extra-low-voltage circuits --
then any custom colours picked this session, then the full colour
dialog. A colour already in the standard list is not repeated under
"recently used".

Nothing is written to the project or to QSettings. That is deliberate:
it is the same session-scoped "what did I just use" idea as
LastUsedStyle, so it adds no persisted state and no file-format change.
Named presets stored per project -- what #461 actually asks for -- are a
larger feature that needs a maintainer decision first; the question is
still open on that issue since 21 June.

Verified on a virtual display against examples/Habitat-Schemas_developpes.qet,
reading colours back from the saved project rather than the screen:

  select all on folio 1, pick Rouge
      21 conductors {none:1, #ff5500:2, #ff0000:6, #00aa00:5, #0000ff:7}
      -> all 21 #ff0000
  one Ctrl+Z
      -> back to the original five-colour mix, exactly
  pick Marron with nothing selected, then draw a conductor
      -> the new conductor is #7b3f00

ctest 12/12, Qt 6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CaKympWT3owLotCpEN2CFj
2026-09-19 09:02:58 +12:00
Laurent Trinques 0a7ea032d9 Revert "bump KDE KF6 frameworks version to 6.28.0 stable release"
Close https://github.com/qelectrotech/qelectrotech-source-mirror/issues/900
This reverts commit ff3c2e0916.
2026-09-17 12:18:10 +02:00
Laurent Trinques ff3c2e0916 bump KDE KF6 frameworks version to 6.28.0 stable release 2026-09-17 06:43:19 +02:00
Andre Rummler 8606cc5328 Removing Qt5/KF5 from build system. 2026-09-17 00:07:46 +02:00
ispyisail 4300a655bc Merge remote-tracking branch 'upstream/master' into feature/162-js-scripting
# Conflicts:
#	sources/qetdiagrameditor.cpp
#	sources/qetdiagrameditor.h
2026-09-17 06:50:35 +12:00
Jeff Patterson 1c8e7ed052 Give Fusion a palette it can draw with on macOS
main.cpp has forced the Fusion style on macOS since 2019, but the palette
still came from Qt's macOS platform theme, which is built for the native
style. It hands Fusion a Window, Button and Base that are the same color,
a Dark lighter than Light, and in dark mode an Inactive ButtonText of
black. Fusion derives its frames, gradients and indicators from those
roles, so fields had no edges, the radio buttons in the text alignment
dialog vanished, and the "Handles" combo box in the diagram editor
toolbar drew its text black on a dark combo the moment the window lost
focus. Light mode had the same flatness, with Window, Button and Base
all white.

Add QET::Palette (sources/qetpalette.{h,cpp}) with a light and a dark
palette laid out the way Fusion expects, and install one from
QETApp::initStyle() on macOS when the running style is Fusion, choosing
by the platform palette's lightness and keeping the platform's accent
color when it reads at 4.5:1. On macOS the base palette is now applied
whether or not "use system colors" is checked, since the system palette
cannot be drawn by Fusion; that setting only decides whether style.css
is layered on top (#467). On Qt 6.5+ the app follows the OS light/dark
switch through QStyleHints::colorSchemeChanged.

Other platforms are untouched: Fusion is Qt's default style on Linux
desktops without a platform theme, and the palette there carries the
user's desktop colors. Making Fusion and this palette the default
everywhere is discussed in #870.

tests/qttest/tst_qetpalette checks every text role pair at WCAG 4.5:1
(3:1 disabled), that Inactive equals Active, and paints a Fusion combo
box, radio buttons, buttons and a line edit on the offscreen platform to
measure the ink against its background. Set QET_TEST_DUMP_DIR to keep
the rendered images.
2026-09-16 04:39:31 -05:00
ispyisail eba258f6cd Add JavaScript scripting: --run and "Run Script..." (bugtracker #162)
Following up on my own comments there: a deliberately small, mostly
read-only scripting surface, exposed to scripts as a single global
`qet` object (QetScriptApi) built on QJSEngine rather than an embedded
Python interpreter -- no new toolchain to package (QJSEngine ships in
every Qt SDK QET already targets, via the Qml module), no GIL, no
version pinning, automatic reflection of the QObject-derived core
classes' own methods with no hand-written binding layer.

## What a script can do

- Read the model: project title, file path, folio count/titles,
  element/conductor counts per folio.
- Trigger the same operations the --export-* CLI flags already do
  (pdf/png/svg/cables/wires/bom/wiring/nets/links/info), plus
  set-titleblock and save -- thin wrappers around CLIExport::run(),
  reusing its already-tested logic rather than duplicating it.

Deliberately NOT in this version: creating or editing diagram
geometry, undo integration, driving the GUI. All explicitly out of
scope per the discussion on #162.

## Two entry points, both built and tested

- `qelectrotech --run script.js project.qet` -- headless/CI.
- Projet > "Exécuter un script..." -- an interactive macro against the
  currently open project. Export/save calls act on the project's file
  on disk (see QetScriptApi's class comment for why), so unsaved GUI
  edits aren't visible to the script; save first if that matters.

## Optional dependency, not a hard requirement

Qt::Qml is probed the same way QtPdf already is in this codebase:
QUIET, non-fatal, behind a QET_HAS_SCRIPTING compile definition. A
build without it compiles and links identically; the CLI flag and
menu action are simply absent (main.cpp) or compile to a clear
"not available" stderr message rather than silently disappearing
(qetscripting.cpp), matching the existing QtPdf pattern rather than
introducing a new one.

One real bug caught building this, not assumed away: my first pass
conditionally excluded the new source files from QET_SRC_FILES behind
`if(QET_HAS_SCRIPTING)` inside qet_compilation_vars.cmake -- but that
file is included before QET_HAS_SCRIPTING is set in the top-level
CMakeLists.txt, so the variable didn't exist yet at that point and the
files were silently never compiled, only caught by an undefined-symbol
link error. Fixed by following the QtPdf file's own precedent:
compile the files unconditionally, guard their Qt::Qml-dependent
content internally instead.

## Verified

Qt6, build clean, ctest 6/6.

- Headless: a script reading project/folio/element/conductor counts,
  calling exportInfo() and exportPdf() against a real project --
  correct JSON, a real single-page PDF confirmed with `file`.
  Error paths: a thrown script exception reports file:line:message and
  exit 1; missing script/project arguments exit 2 (matching
  CLIExport's own usage-error convention); a missing project file is
  reported and does not hang.
- Corpus: the same read-model script run against all 24 shipped
  example projects, 0 failures.
- GUI: "Exécuter un script..." opens a real file dialog filtered to
  *.js, running the picked script against the live open project
  produced the exact expected JSON export file, and the application
  was still fully responsive afterward.

Refs #162.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 19:44:14 +12:00
ispyisail 55c2c0df9d Paste under the cursor and let it be positioned before it lands
Ctrl+V pasted in place, which put the copy exactly on top of the original.
Nothing appeared to happen: the only clue was a doubled outline, and the
copy had to be dragged off the original to be seen at all. The cursor was
ignored entirely.

Ctrl+V now starts a placement. The items appear under the cursor and follow
it until a left click or Return drops them; Escape or a right click takes
them away again. That is the same interaction as placing a new element, so
paste behaves like every other way of putting something on a folio, and the
copy lands where the user is looking.

Implemented as a DiagramEventInterface beside the existing add-element and
add-macro tools. The pasted items are the real ones from the start rather
than a preview: Diagram::fromXml creates them exactly as before, this class
moves them, and PasteDiagramCommand is pushed only once they are dropped.
PasteDiagramCommand's first redo() deliberately does not add items to the
scene -- it assumes fromXml already did -- so pushing it on commit adopts
them rather than duplicating them. One copy of the paste logic, and a
cancelled paste leaves nothing on the undo stack.

Conductors are not moved directly; they are drawn from their terminals and
follow the elements they attach to. On cancel they are removed before the
elements, so none is left in the scene holding a pointer to a freed
terminal.

Verified by counting elements in the saved file rather than by eye:
56 to start, 56 after paste-then-Escape, 57 after paste-then-drop, and 56
again after undo. Save determinism run against this build: pass, no
regressions against baseline. Tests 5/5 on Qt 6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 17:07:15 +12:00
Andre Rummler 4e9463ee8a Do not install PugiXML and Googletest static libraries. These are only needed during compile time. 2026-09-12 12:02:55 +02:00
enesgursoy6110 77f8e26ffc Address smart device BOM review feedback 2026-09-11 20:12:09 +03:00
enesgursoy6110 16dbc5c404 Add ungrouped device BOM CSV export 2026-09-11 20:11:28 +03:00
enesgursoy6110 73f7ea8f50 Add smart device metadata to element information 2026-09-11 20:11:19 +03:00
Laurent Trinques 7d4cbc30f1 cmake: add qet_ko.ts to TS_FILES
qet_ko.qm was tracked in git but qet_ko.ts was never listed in TS_FILES,
so Korean was no longer built after #751 removed the tracked .qm files.
2026-09-11 10:45:24 +02:00
Laurent Trinques c7893c8229 Merge pull request #630 from ispyisail/feature-wiring-list-export
Wiring list dialog + excluded-conductor count (discussion #503, slice 4)
2026-09-10 22:45:22 +02:00
Andre Rummler 6b577ee757 Add editable resize/rotate/skew handles for shapes and images
Shapes and images can now be resized, rotated, and skewed directly
on the canvas, not just moved. Both share one small transform
struct (rotation, then skew, then scale, anchored on a movable
pivot) and one handle widget, so a corner drag, an edge skew, or
grabbing the rotate handle behaves the same way and runs through
the same matrix math everywhere, instead of every item type
reinventing its own.

Shapes also gained a proper pen tool (bezier paths, corner/smooth/
symmetric nodes), arc support, and mirroring. Images gained
non-destructive cropping and colour-keyed transparency, both
remember their own settings, so reopening the dialog picks up
where you left off instead of starting over.

Properties dialogs for both were extended to match (position,
size, angle, skew), with undo/redo wired through for every handle
drag.

Old XML files can read easily as the transformation is only added
if needed and the old syntax is still used and understood if it is
not needed.
2026-09-05 15:48:28 +02:00
Laurent Trinques a5d9cc37e1 Try to fix macOS bundle 2026-08-29 13:10:38 +02:00
Laurent Trinques 03b552c601 Merge pull request #776 from ispyisail/remove/diagramselection-dead-code
Remove dead code: sources/ui/diagramselection.*
2026-08-23 13:50:00 +02:00
Laurent Trinques e44219778e Make QtPdf detection optional at configure time
QPdfDocument::pagePointSize() (used for PDF page import) requires
Qt >= 6.4, and the QtPdf module itself is missing entirely on some
Qt6 distributions (e.g. the Flatpak org.kde.Platform runtime), since
it ships from the qtwebengine source tree rather than Qt6 core.

CMake: probe Pdf with find_package(... QUIET) instead of REQUIRED,
mirroring the existing GuiPrivate pattern. Define QET_HAS_QTPDF
only when the module is found AND Qt >= 6.4.
Replace the ad-hoc QT_VERSION_CHECK(6, 0, 0) / (6, 4, 0) guards in
qetdiagrameditor.cpp, diagrameventaddpdf.{h,cpp} and
pdfpagesdialog.{h,cpp} with #ifdef QET_HAS_QTPDF, so version and
module-availability checks live in one place.
Fixes the Flatpak build (missing Qt6Pdf) and the Windows/Debian CI
failures (QPdfDocument::pagePointSize undeclared on Qt < 6.4). The
PDF import toolbar action is now silently unavailable wherever
QtPdf isn't usable, instead of breaking the whole build.
2026-08-23 12:05:52 +02:00
ispyisail 8d175754cd Remove dead code: sources/ui/diagramselection.*
Compiled into the binary but never instantiated -- searching the tree
for any reference outside its own three files finds nothing, and this
still holds on current master. Contains a latent bug that would be
user-visible if the widget were ever reachable
(on_tableDiagram_customContextMenuRequested compares QMenu::exec()'s
return value against one action but falls through to "select all" on
both the other action and on a plain dismiss, since exec() returns
nullptr on Escape/click-away and that's not equal to either QAction*),
which supports genuine disuse rather than temporary disconnection.

Removed the three files and their three explicit entries in
cmake/qet_compilation_vars.cmake (qelectrotech.pro globs sources/ui/*
so needs no change). Builds clean; no other file references
diagramselection.

Fixes #756.
2026-08-23 21:59:39 +12:00
Kellermorph ff25a77159 Improve PDF import: DPI selection, page preview, Qt5 compat, custom icon
- Add DPI selection (150/300/600) to the page selection dialog
- Add live page preview in the selection dialog
- Conditionally compile PDF import only for Qt6 (#if QT_VERSION)
- Add custom pdf-import icon (PDF document with + symbol)
- Register new icon in qelectrotech.qrc
- Qt5 builds: PDF import action is hidden, everything else works as before
2026-08-22 18:53:36 +02:00
Kellermorph baf95338a3 Add PDF page import as image 2026-08-22 14:27:50 +02:00
ispyisail 5b8d05fc1e Add a wiring list dialog and an excluded-conductor count
Slice 4 of discussion #503, on top of slice 3 (#629): the smallest
surface that makes wiring_list_view visible, plus the diagnostic the
view needs to be honest about what it is missing.

Projet > "Liste de câblage (base de données)" opens a read-only table of
wiring_list_view, headed by a line stating how many conductors are
listed and, when non-zero, how many were excluded and why.

Deliberately not another exporter. QET already ships a wiring-list CSV
export (Projet > Exporter le plan de câblage, and --export-cables) which
walks the project XML; measured on the same projects it produces a row
per conductor and resolves labels correctly when the project has them.
Adding a second, competing CSV would be worse, not better -- the
database path's value is what it unlocks (terminal plans, BOM joins),
not replacing that export.

projectDataBase::excludedConductorCount() counts, from the live scene,
the conductors deliberately absent from the conductor table because a
terminal has no uuid. Counted from the scene precisely because the
database is where those conductors are not. Verified: 671 on
examples/industrial.qet (which has 1794 terminals and no terminal uuids
at all, so its list is empty and now says so), 0 on a project whose
elements do carry terminal uuids.

KNOWN GAP, not fixed here and the reason this is opened for discussion
rather than merge: after a save/reload the component columns are blank
for slave elements. populateElementTable()/populateElementInfoTable()
only insert Simple|Terminal|Master|Thumbnail, so slave elements -- relay
contacts, i.e. a large share of real wire endpoints -- have no row in
element_info for the view to read a label from. Measured on a two-slave-
contact project after reload: element rows 0, element_info rows 0,
terminal rows 2, conductor rows 1; the wire is listed (slice 3's LEFT
JOIN keeps it) but both component names are empty, where the existing
CSV export shows K1 -> K2 for the same file.

Closing that gap means widening a filter shared with the nomenclature
and summary views, which would change what those existing, shipped
features contain. That is a maintainer decision, not one to take
unilaterally inside an additive slice.
2026-08-21 21:10:27 +12:00
Laurent Trinques 5f83679756 Update paths_compilation_installation.cmake 2026-08-17 18:58:11 +02:00
Laurent Trinques eb095f9a10 Update start_options.cmake restore -DQET_ALLOW_OVERRIDE_DD_OPTION 2026-08-17 06:57:18 +02:00
Laurent Trinques 610001a847 Enable project database export via CMake option 2026-08-15 21:06:08 +02:00
Laurent Trinques ddbd1d8d75 cmake: fix QET_MIME_PACKAGE_PATH escaping CMAKE_INSTALL_PREFIX
QET_MIME_PACKAGE_PATH was "../share/mime/packages/", a path relative
to CMAKE_INSTALL_PREFIX. This only worked by accident with the old
default prefix (/usr/local -> ../share resolves to /usr/share/mime,
the conventional system location regardless of app prefix).

With -DCMAKE_INSTALL_PREFIX=/usr (as used by Debian/Ubuntu packaging),
the same "../share" escapes /usr entirely, landing at /share/mime
instead of /usr/share/mime, which breaks dh_install (file not found
under usr/) and would silently install the mime package definition
outside any path desktop environments actually scan.

Drop the "../" so the mime package path stays under the install
prefix, matching standard practice (/usr/share/mime/packages or
/usr/local/share/mime/packages).
2026-08-15 16:42:10 +02:00
Laurent Trinques afb7442e8a cmake: use vendored SingleApplication submodule when available
FetchContent_Declare unconditionally tries to clone SingleApplication
from GitHub, which breaks offline builds (e.g. Debian/Ubuntu pbuilder
with FETCHCONTENT_FULLY_DISCONNECTED=ON, Launchpad PPA builds).

If the SingleApplication submodule is already checked out in the
source tree, point FETCHCONTENT_SOURCE_DIR_SINGLEAPPLICATION at it so
FetchContent skips the network step entirely and reuses the local
copy. Falls back to the existing git clone behavior otherwise, so
this is a no-op for setups that don't vendor the submodule.
2026-08-15 16:17:10 +02:00
ispyisail d988054aca Export the master-side cross-reference table to DXF
Follow-up to #740, which fixed the slave-side "(n-Xn)" cross-reference
label. The master-side item - the small table/cross drawn next to a
report or master element, listing where each of its slaves is used -
was still missing from DXF export. Measured against examples/
industrial.qet with the PDF export as an oracle (renders the whole
scene, so it shows what should be there):

                          before  after   PDF
  slave xrefs  "(n-Xn)"       41     41    41   (already fixed, #740)
  folio/position strings     358    403   403

DXF now matches the PDF exactly.

## Why this needed a different approach than #740

The slave label is a plain QGraphicsTextItem - one string, trivial to
walk and re-emit as a single DXF TEXT entity, which is what #740 did.

The master-side item (CrossRefItem) is not: it paints itself with
~600 lines of hand-written QPainter calls across three modes
(drawAsCross/drawAsContacts/drawAsPlcTable), including a header
table, contact symbols, and rules. Hand-porting that logic to emit
DXF primitives directly would mean maintaining two divergent
implementations of the same drawing that have to be kept in sync by
hand forever.

## Approach: a QPaintEngine that intercepts CrossRefItem's own paint()

DxfPaintEngine/DxfPaintDevice (sources/dxfpaintdevice.{h,cpp}) is a
QPaintEngine/QPaintDevice pair - the same mechanism QPrinter and
QSvgGenerator use to redirect QPainter output elsewhere. Constructing
a QPainter on a DxfPaintDevice and calling item->paint() on it produces
DXF entities instead of pixels, using the exact same drawing code that
already renders correctly on screen. CrossRefItem::paint() is
unmodified.

Scope is deliberately narrow - only the QPainter calls CrossRefItem's
paint() is observed to make: drawLines -> LINE, drawRects/drawPath's
fill case -> outline-only LWPOLYLINE (no HATCH support in v1 - DXF's
fill primitive is a separate, more involved entity type; documented as
a known limitation rather than attempted here), drawEllipse -> CIRCLE
or a flattened polygon for rotated ellipses, drawPath's arc case (from
drawArc/drawPie) -> chord-flattened LINE segments, drawPolygon ->
LWPOLYLINE, drawTextItem -> TEXT. drawPixmap is intentionally
unimplemented (qWarning + skip) since CrossRefItem never calls it -
this is not a general-purpose DXF paint engine, and isn't meant to be
in this PR.

CrossRefItem::paint() is protected, per the normal QGraphicsItem
contract - added a small paintForExport() wrapper rather than making
paint() itself public, or reaching around access control.

## Explicitly out of scope

QetShapeItem::toDXF() and QetGraphicsTableItem::toDXF() (both already
implemented and working) are untouched. Rewriting working exporters
onto this engine to prove an architectural point would be a large,
unrelated diff with no user-visible benefit - if that consolidation is
wanted later, it's a separate proposal once this engine has shipped
and proven out on the one item that currently has no DXF export at
all.

## Testing

Built clean on Qt5/Linux. Verified via the GUI export dialog
(Fichier > Exporter > DXF) against examples/industrial.qet, 50 folios:
export completes without error or crash, all 50 .dxf files are
structurally well-formed (balanced SECTION/ENDSEC, single EOF each),
and grepping the folio-position pattern gives the before/after/PDF
numbers above. Spot-checked several real label strings (e.g. "18-B18",
"20-A2") present as TEXT entity values in the output, not just an
artifact of the count matching.
2026-08-15 08:39:55 +12:00
Andre Rummler 92b2608bfb Comments in CMake with # and not //... always remember the language you are currently using. 2026-08-12 12:29:56 +02:00
Andre Rummler af87d28e33 Fix several parameters after Qt6 migration. 2026-08-12 12:29:56 +02:00
Andre Rummler c5d714089f Replacing KF5 missed in shell print out. 2026-08-12 12:29:56 +02:00
Andre Rummler c14d6a6dd6 In order to migrate to Qt6 all options for KF6 were added:
a) using a system provided KF6
b) downloading and compiling KF6
c) using the vendored-in re-creation of the functionality

The behaviour for both Qt5 and Qt6 is steered with the same two variables which were renamed to become version agnostic:
a) BUILD_WITH_KF=ON BUILD_KF=OFF
b) BUILD_WITH_KF=ON BUILD_KF=ON
c) BUILD_WITH_KF=OFF

The version is automatically derived from the chosen Qt major version.
2026-08-12 12:29:56 +02:00