Commit Graph

8466 Commits

Author SHA1 Message Date
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
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
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
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
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 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
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
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
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
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
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