Upstream moved 56 commits ahead of the last merge. Six files conflicted,
all of them files where upstream's French -> English tr() translation wave
touched lines we had also touched with the cable feature. Resolution rule
applied everywhere: take upstream's English source strings, keep our cable
additions.
Resolved:
- sources/ui/xrefpropertieswidget.ui : kept our structure (m_labels_gb
pulled out of the display group, m_slave_row, the m_font_pb "Police..."
button of the cable cross-reference) and mechanically applied upstream's
20 English string replacements on top of it.
- sources/ui/xrefpropertieswidget.cpp : our version plus upstream's 7
translated combo entries; the tr("Cable") item we add for the cable
cross-reference type stays.
- sources/autoNum/ui/numparteditorw.cpp : upstream's English part names
("number format 1", "Sheet no.", "Plant", "Location", ...) for the
number-format list, with our comment explaining why type 3 (cable) takes
the conductor kinds of variable.
- sources/ui/configpage/configpages.cpp : upstream's English tab titles
(Conductors / Elements / Sheets) plus our Cables tab.
- sources/ui/configpage/projectconfigpages.cpp : upstream's "Sheets" tab
title plus our Cables tab block (single-rule SelectAutonumW(3)).
- sources/qetdiagrameditor.cpp : union of both sides - upstream's new
"Add a generic device" action (icon, status tip, data id) and its
English menu names ("File"/"Edit"/"Project"/"Display"), together with our
cable action, the Numbering and Lists menus, the cable list actions and
the CSV export in the Project menu.
Kept French: our own cable tr() source strings stay French for now. That
is the smaller change and the translation pass can follow separately.
Verified: full build 1065/1065, ctest 91/92 (only the pre-existing
headless tst_menubarkeyboard fails: no window manager, so Alt+F and F10
cannot open a menu bar), smoke start exit 124, no conflict marker left in
the tree.
Conflicts resolved in 24 files. Notable decisions:
- ico/icon-themes.qrc: upstream's icon folder restructure kept; the 36
cable icon entries re-added under the new qet/qet-dark path scheme
(XML valid, every referenced file exists).
- Toolbar system: upstream's DiagramToolbarSettings now owns the
toolbar filling. The cable tool is registered as
diagrameditor.add_cable (generic group loop) and listed on the
"diagram" toolbar right after the auto-break conductor action,
separated by a divider, exactly like before the merge.
- Paste: the cable lines of a copy are handed to
PasteNumberingImport::push() as extra commands, so paste + cable
lines + the numbering question stay ONE undo step on both paste
paths (DiagramView::paste and DiagramEventAddPaste::commit).
- PDF links: upstream's deterministic link sorting kept together with
the cable cross-reference injector (injectLinkTo -> links list).
- Element renumbering: upstream's ElementAutoNumSchemeCommand::renumber
replaces the old formula-based body of
QETProject::renumberElementsBySchemeTitle; the cable numbering rule
API (cableAutoNum, axis priority, rule name) is kept in full.
- Auto-numbering pages: upstream's scheme management kept, the Câbles
tab added as index 4 (changeToCableTab), both entry points
changeToCable() and changeToElement() kept side by side.
- SelectAutonumW: dirty tracking and the contextEdited signal moved
into upstream's insertPartRow() row API, so single-rule mode, the
rule-removable button and "confirm without change leaves the project
alone" keep working.
- xrefpropertieswidget: upstream's offset clamping kept; the cable
branch (Texte field, Police button, hidden display options) kept;
the stack-overlapping checkbox is hidden for cables.
Verified after the merge: build green (515/515, no errors), ctest
85/86 (only the pre-existing headless tst_menubarkeyboard failure),
offscreen smoke exit 124.
- Dialog sizes and positions (dialoggeometry/...) describe this computer,
like the other window layout keys: no longer saved to a settings file,
and kept when one is loaded.
- The trackpad gestures setting (diagramview/gestures) is in the part
for toolbars, shortcuts and gestures rather than all other settings.
- Loading only the defaults for new projects now says it is done.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Save settings as... now asks which parts to save: toolbars, keyboard
shortcuts and mouse gestures; defaults for new projects (folio, title
block, wires, numbering); everything else. All three ticked writes the
same complete file as before (format 1, collection folders included).
A file with only some parts is format 2 and lists its parts. Loading it
replaces those parts and keeps every other setting. The folders of the
collections are never in such a file: they rarely exist on another
computer. A QElectroTech that only knows format 1 refuses it rather than
loading it as complete and losing every other setting.
A file holding only the defaults for new projects loads without closing
QElectroTech, since QETProject reads them again for every new project.
Which part a key belongs to is ConfigProfile::partOf(); a key it does
not list falls in "everything else", so a new setting can never be left
out of every part. The test checks that what BorderProperties writes for
the folio defaults lands in the new-project part.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cables are drawn line objects (toolbar button next to the auto
break/reconnect button, click-move-click, right click cancels), not
components. The type comes from a CSV catalog following the material
list pattern (designation, cores, core colours, editable through the
entry dialog with "Nouvelle entrée" / "Modifier l'entrée").
Each core is bound to the wire its colour label stands on, and the
cable reference is generated per core and written into the Conductor
entry via Conductor::setCableReference(label, cable uuid, slot). The
conductor entry is where it has to live because the terminal strip
plan (Klemmenplan) consumes it in the next step.
Numbering: "Cables" rule under Programmeinstellungen/Neues
Projekt/Nummerierung auto (mirrored in project properties), literal
"W" fallback without a rule, French gate dialog when no rule exists,
Nummerierung menu with the renumber dialog (preferred axis, per-folio
counter, rule and counter state written back, hand-typed names ask,
whole-project scope).
Also included: label block and under-line texts with per-text font and
alignment plus the material-list cell margins, free-core placement and
right-click removal, claim questions for wires of other cables,
multi-selection drag as one undo step, type change with confirmation,
cross-references for type "Cable" (%f-%l%c, own font, clickable links
in exported PDFs), report propagation of the cable definition,
Listes menu (table of contents, material list, terminal strip manager,
terminal generator plugin, cable list), cable list with freely
selectable columns (installation/localisation of cable, start and end,
Blatt start/end, length, used cores...), CSV export dialog with column
pick and preview in the Projekt menu, script API (addTable kind
"cable_list", exportCableList) and a unit test for the type catalog.
Every tr() source string in the code and the forms is now the English
text, and French is a translation (qet_fr.ts) like the other languages.
Generated, not hand-edited: master ef795a21e5 converted by
qet-en-source (harness 4c4ddc9).
3144 messages get an English source, 665 already had one, 0 left French; 8 shared-wording pairs disambiguated.
C++: 2904 literals in 233 files. Forms: 809 strings in 65 files. All 34 .ts files re-keyed; qet_fr.ts filled.
lupdate check: PASS. What each of 38 languages shows, old vs new: same 118266, English instead of French 38433, comment fallback 51, differences 0.
Configuration > Personnaliser... (also at the bottom of a toolbar's
right-click menu) opens one window with a tab for each page that sets
how the user works: Barres d'outils, Contenu des barres, Barre de
raccourcis, Raccourcis and Gestes de la souris, like SolidWorks'
Tools > Customize. The tabs are the same pages the configuration
dialog shows, which keeps them. OK applies every tab, Cancel none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The right-drag ring showed the shortcut bar's commands for the selection.
A new Preferences page, "Gestes de la souris", lets the user pick the
ring's commands per selection context, one per direction, and choose 4 or
8 directions. Commands are dropped from the list onto a slot of a ring
editor, or placed with a button; Delete empties a slot.
A context the user never edits keeps following the shortcut bar, and 8
directions stays the default, so nothing changes for anyone who does not
open the page. A picked list is positional: a direction left empty, or
holding a command this build lacks, stays empty instead of shifting the
others. Stored like ShortcutBarSettings: diagrameditor/gestures/<context>
id lists in QSettings, defaults not stored.
tst_gesturesettings covers the defaults, positional lists, the 4/8
setting, the overlay's sectors and empty slots, and the page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Configure QElectroTech > Toolbar contents: pick a toolbar, drag or add
commands from the list on the left, remove them with Delete, reorder,
add separators, and add, rename or delete toolbars of one's own.
Reset to defaults gives a toolbar its original contents back.
Each toolbar is stored as a list of command ids in QSettings
(diagrameditor/toolbars/<objectName>), the same pattern the shortcut
bar uses. A toolbar the user never changed stores nothing and is built
exactly as before; an id no command carries any more is skipped. The
four buttons that are widgets rather than commands (handle size, text
grid, background colour, conductor colour) get ids of their own and
can each be on one toolbar at a time. Stored scripts can go on any
toolbar.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new page in Configurer QElectroTech, "Barres d'outils", sets for
every window (folio editor, element editor, title block editor):
- the icon size: default, 16, 24, 32 or 48 px
- the buttons: icon only, text beside the icon, or text under it
- locking the toolbars so they can't be dragged out of place
OK applies it to the open windows at once. The defaults are Qt's
own, and a default value removes its key, so nothing changes for
anyone who never opens the page. A toolbar inside a panel is left
alone; only the window's own toolbars are locked.
New test tst_toolbarsettings: defaults change nothing, saved values
reach every toolbar of an open window, back to defaults removes the
keys.
Third stage of discussion #1319.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reported on #1178. Two things kept --common-elements-dir from working for
--run and the command-line export:
- main() runs both before QETApp exists, and only QETApp::parseArguments()
applied the folder options, so they were ignored. The application of
those options is now QETApp::applyDirectoryArguments(), static, and the
two headless branches call it too. They also drop the options from the
arguments they pass on: the export reads its project and output by
position, so "--export-pdf --common-elements-dir=X p.qet out.pdf" took
the option for the project ("Project not found").
- commonElementsDir() read the saved collection path before the
command-line one, so a user's own setting beat a folder given for this
run. The command line now comes first, in the GUI too.
Tested with a --run script adding a symbol that exists only in a given
folder: master fails with or without a saved collection path; this
resolves it, before or after --run, and over a saved path to another
folder. Without the option it still fails, and a saved path to the
folder still works.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELKpbGxqJd7EFiTUypBtVe
Discussion #598, reviving PR #654. The crash-recovery backup written
every 20 minutes went to a single file. If the project was already in a
bad state when a backup ran, that bad state replaced the only recovery
copy.
QETProject now writes the backups in turn to three KAutoSaveFile slots
(BackupGenerations), so one bad write only replaces the oldest
snapshot. Scope is crash recovery only; the opt-in autosave is
unchanged.
After a crash, the recovery prompt groups the snapshots by project and
offers one row per project with a list to pick the snapshot to reopen,
newest selected by default. The snapshots not picked are deleted.
Ported onto current master: writeBackup() keeps the "skip if nothing
changed" check (bugtracker #273) and offerBackupFiles() keeps its place
after the stale-file filter and before the crash report (#901).
Tested with the backup interval shortened to 4 s (test build only):
after three changes, master holds one recovery file, overwritten each
time; this branch holds three, with 4, 5 and 6 folios. After killing
QET, the prompt lists the project; picking the oldest snapshot reopens
4 folios, the default reopens 6. Twice each. ctest 34/34 with and
without KDE Frameworks; the carried-over KAutoSaveFile test passes in
the nokde build.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Revives #640, closed only to clear a review backlog. Two entries in
the Configuration menu: "Enregistrer la configuration sous..." saves
the settings to a .conf file, "Charger une configuration..." replaces
them with one. The file is always ini format, so a profile made on one
platform loads on the others.
Changes from #640:
- Window sizes, positions and dock layouts (".../geometry",
".../state") and the recent-files lists stay out of a profile and
are kept on load: they describe this computer, not a preference.
- A saved file carries a format marker; loading refuses files that
QElectroTech did not save.
- Loading closes QElectroTech and asks the user to start it again,
instead of starting a second copy: a second copy started while this
one is still running hands its arguments over and exits (main.cpp).
- The colour dialog's custom colours are reloaded from the profile
before quitting, since ~QETApp() saves them over the loaded ones.
- Both actions are registered with ShortcutManager, so they appear in
command search and can be given a shortcut.
- The copy rules live in the header-only utils/configprofile.h and
are unit tested (tst_configprofile).
Loading still replaces rather than merges: a setting the profile does
not have is removed, or switching profiles would keep settings from the
previous one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pressing S on a folio opens the element picker at the cursor with a row
of commands above it, chosen by what is selected, like the SolidWorks
shortcut bar:
nothing selected insert last element, element picker, text, line,
rectangle, terminal strip plan, paste, folio
properties
elements selected rotate, rotate texts, edit, copy, cut, delete
only conductors reset path, edit, delete
Each row is a list of ShortcutManager ids, so any registered command can
go on it and the bar carries no command list of its own. The lists are
in QSettings (diagrameditor/shortcut_bar/<context>); a context the user
has not changed follows the defaults. A disabled command keeps its
place, greyed, so a row looks the same each time. Clicking a button
closes the bar and triggers the action.
A new configuration page, "Barre de raccourcis", edits the three lists:
add, remove and reorder any diagram editor command.
To make that possible:
- ShortcutManager::action(id, owner) returns the action a given window
registered under an id, since each editor window registers its own.
- The add-item actions (text, image, shapes, terminal strip plan) are
registered as diagrameditor.add_<kind>, with no default key. They also
appear in the Shortcuts page and can now be bound.
Discussion #1033.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
(cherry picked from commit 53c1e213282b9f7226ca1c09e81703113236bb88)
Drop 1:2.5: on a grid of 10 it steps by 4, which misses 10, so texts on
two elements 10 apart could never line up. Every remaining divisor is a
whole number, which tst_textgrid checks.
The divisor list and the snapping arithmetic move to the header-only
textgrid.h so the toolbar menu, the preferences page and the test share
them. The preferences page gets the same choice under Grille + Clavier;
QETApp::textGridChanged keeps every editor's toolbar button in step.
While element texts are dragged, the status bar names the text grid and
says to release Shift and hold Ctrl for free placement -- Ctrl+Shift
together is the pan shortcut.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
The 3D mouse's pan and zoom speeds were fixed guesses, and each sample
was applied as it came, so the speed on screen depended on how often
the driver sends samples -- different for every platform and device.
Motion now goes through SpaceMouseMotion::map(), which scales each
sample by the time since the previous one, and applies the user's
settings from a new "Mouvement" section of Configuration > Souris 3D:
pan and zoom speed, a dead zone, inverting each axis, and zooming by
push/pull (as before) or by twisting the cap. The defaults keep the
previous behaviour. Zoom is now exponential in the deflection, so the
factor stays positive however hard the cap is pulled (1 + z/1000 went
negative past z = -1000) and an equal push and pull cancel out. Sub-
pixel pan is carried over between samples instead of being rounded
away. The backend now reports all six axes.
tst_spacemousemotion covers the mapping without a device and is built
whether or not QET_ENABLE_SPACEMOUSE is on. The new behaviour was also
checked end to end with tools/spnav-shim (qelectrotech-docker): twist
with a dead zone of 10 ignores push/pull and small drift, and a twist of
60 gives the same frame as a push of 50 with the defaults.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
QETApp's constructor ends with checkBackupFiles(), which opens modal
dialogs -- the "restore these files?" prompt, and the crash report offer.
Those dialogs run their own event loop, so the constructor does not return
until the user answers them.
main() still has work to do at that point. In particular this, a few lines
later:
QObject::connect(&app, &SingleApplication::receivedMessage,
&qetapp, &QETApp::receiveMessage);
While the prompts are up that connection does not exist yet. A second
instance launched during the window -- double-clicking a project, or
xdg-open, while the first copy is still asking about restore files --
hands its file names over, SingleApplication accepts and delivers them,
and nothing is listening. The message is discarded and the second process
has already exited, so the file is simply lost with no error.
Deferring checkBackupFiles() to the event loop lets the constructor return
promptly. main() finishes wiring up, and the prompts appear immediately
afterwards exactly as before.
Verified with a stale restore file present, sending a project to the
running instance while the restore prompt is displayed: before, the file
was dropped and never appeared, even after answering the prompt; after, it
opens. The restore and backup prompts still appear and still work.
Note this is only observable together with the fix for bugtracker #248 --
before that, no file passed to a running instance was opened under any
circumstances.
Add a KColorButton next to the 'Utiliser les couleurs du système'
checkbox in the Apparence settings tab:
- When the checkbox is active (default), system colors are used
and the color button is disabled
- When the checkbox is inactive, the color button becomes active
and lets the user pick any color for the entire application
- useCustomPalette() builds a full QPalette from the chosen color
with proper light/dark text contrast, button shading, and icon
theme switching
- The chosen color is persisted in QSettings as
'customapplicationcolor' and restored on next startup
Files changed:
- sources/ui/configpage/generalconfigurationpage.ui: HBoxLayout
with checkbox + KColorButton, customwidget declaration
- sources/ui/configpage/generalconfigurationpage.h: new slot
- sources/ui/configpage/generalconfigurationpage.cpp: load/save
custom color, enable/disable logic, toggled slot
- sources/qetapp.h: useCustomPalette() declaration
- sources/qetapp.cpp: useCustomPalette() implementation,
startup restore of custom color
Switching the system between light and dark while QET runs changed
the palette of every plain widget but left widgets that carry a style
sheet in the colors they were created with: the folio tab bar stayed
light in dark mode, and after a dark-to-light switch its Add folio and
chevron buttons hovered as a near-black box with the icon lost inside
it. QApplication::setPalette() does not reach a widget with a style
sheet; QStyleSheetStyle resolved its palette once, when the sheet was
applied, and keeps it. Seventeen call sites set a sheet on a widget and
five .ui files carry one, so any of them could show the stale palette.
QET::Palette::refreshStyleSheets() re-applies each such widget's own
sheet, which makes QStyleSheetStyle resolve it against the palette now
in force. QETApp::useSystemPalette() calls it after installing the
palette, so both the OS color scheme change and the "use system
colors" setting are covered.
tests/qttest/tst_qetpalette: a tab widget with the folio tab bar's
sheet is still drawn in the old colors after setPalette(), which is the
defect, and follows the palette after refreshStyleSheets(), in both
directions.
Fixes#943.
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.
Qt's QColorDialog loads custom colors from QSettings on startup but
never writes them back, so user-defined colors in the color picker
are lost when the application exits.
Save and load the 16 custom color slots explicitly via QSettings
in QETApp's constructor (after initStyle) and destructor (before
other settings are flushed). This covers every QColorDialog usage
in the application transparently.
Also fixes a QColorDialog memory leak in DiagramView.
189 of QET's 266 fixed-size icons are black line art with no dark
variant, so on a dark palette they were black on a dark toolbar, and
Fusion's disabled rendering lightened them into something more readable
than the enabled state (GitHub #466, #870; bugtracker 335 for the
element panels, which keep their own fix).
The theme "qet-dark" holds light-ink copies of the line-art icons in
ico/themes/qet-dark, generated by misc/make_icon_themes.py. Colored
icons are not copied; the theme inherits them from "qet". An icon counts
as line art when fewer than 20% of its visible pixels are saturated. The
copies keep hue and alpha and invert lightness, scaled so each icon's
darkest ink becomes (220,220,220), the dark palette's text color. The
eight SVG icons get their color replaced the same way.
QETApp::applyIconTheme() picks "qet-dark" for a dark palette and "qet"
otherwise. It runs from initIconTheme(), again from initStyle() once the
palette is final, and on the OS color scheme switch. Icons created with
QIcon::fromTheme() re-resolve on their next paint, so nothing else
changes.
With light-ink files, Fusion's own disabled rendering comes out dimmer
than enabled with no extra code.
tests/qttest/tst_qeticons: every name resolves in both themes, every
dark file has light ink, and a Fusion tool button shows its icon at 3:1
in both themes with disabled weaker than enabled.
Five things raised in review, plus tests for the parts that were only
described in prose.
clearPendingCrashDump() did not do what its comment said. It called
pendingCrashDumpFiles() again at clear time, so it deleted whatever was
in the directory then, not what had been offered. The offer sits inside a
modal dialog that stays open as long as the user reads it, and
SingleApplication keys its socket on the binary path, so a second
QElectroTech build running alongside is a separate process that can crash
and write a dump in that window. Re-listing deleted that dump unseen --
the exact failure this change exists to fix. The list is now taken once
in QETApp::checkCrashDump() and passed to both
pendingCrashDumpContents() and clearPendingCrashDump().
The ring is now written before the backtrace. backtrace() unwinds through
libgcc, which calls dl_iterate_phdr and takes the loader lock; warming it
in install() removes the allocation but not the lock. Crashing inside
dlopen() (Qt plugin loading), or on a corrupted stack, could therefore
hang or re-fault the handler at the backtrace and lose the ring with it.
Order is now header, signal, ring, backtrace, so the cheapest and most
valuable part is already on disk before anything that can block. The
class comment claimed the handler takes no locks; that was not strictly
true and now says so.
QET_CRASH_BACKTRACE comes from find_package(Backtrace) rather than
__has_include(<execinfo.h>). The header exists on FreeBSD but backtrace()
lives in libexecinfo there, so the probe compiled and the link failed.
A crash_dump.log left by a pre-#905 version is migrated into crashes/ at
startup, named from its own mtime. Otherwise upgrading stranded it: the
new code never looks at that path, so the dump from the crash that
prompted the upgrade would sit there unoffered forever.
Also from the review: dumps are capped at the 10 newest, so a crash loop
cannot fill the log directory before any dialog is shown; crashDumpDir()
no longer creates the directory as a side effect of a const getter
(ensureCrashDumpDir() does that for the callers that write); and redact()
now masks an AppImage's per-run /tmp/.mount_XXXXXX prefix, which
backtrace_symbols_fd() writes into every frame.
Two test executables, both of which were checked to fail against the
behaviour they replace:
- tst_crashhandler covers CrashHandler::formatInt(), which had no
coverage at all despite running only inside a signal handler, where
nothing can assert: zero, negatives, INT_MIN (negated through unsigned,
since -INT_MIN is UB), INT_MAX, truncation and a zero-sized buffer,
each checked against a sentinel-filled buffer so a write past the
reported length fails.
- tst_crashdumps covers the bookkeeping: ordering, empty dumps, the
exclusion of this run's own path, the cap, concatenation of every
offered dump, that clearing deletes only what was offered, and what
redact() masks. qetlogger.cpp needs exactly one symbol from the
application, QETApp::dataDir(), which the test supplies itself.
Not addressed here: the timestamp in crash_<timestamp>_<pid> is the
launch time, not the crash time -- correct as observed, and the commit
message that implied otherwise was the thing that was wrong. Resolvable
QET frames for AppImage/Flatpak/Snap/Debian need -rdynamic and archived
debug symbols, which is a packaging discussion, not this change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
QET had no icon theme: the 446 entries of the icon table and the 116
iconsets in .ui files each named a resource path, so an icon could only
ever be one file, and a variant for another palette or a vector source
had nowhere to go (GitHub #466, #690, #870). This adds the theme layout
without changing a single pixel; a dark variant comes in a follow-up.
The theme "qet" follows the freedesktop layout Qt's icon loader
understands. misc/make_icon_themes.py generates ico/icon-themes.qrc,
which aliases the existing ico/<size>/<name>.png files into
themes/qet/<size>/<name>.png, and ico/themes/qet/index.theme. No file
moves. The four table entries that paired a 16 pixel file with a 22
pixel file of another name (ConductorSettings, DiagramAdd,
DiagramDelete, DialogInformation) get the 22 pixel file aliased under
the 16 pixel name.
QETApp::initIconTheme() registers the theme before initIcons() and makes
it current on every platform, so a desktop icon theme cannot replace
QET's icons. Icons are then looked up by name: QIcon::fromTheme() in
qeticons.cpp and in the few places that built a QIcon from a resource
path directly, and theme="..." on the iconsets in .ui files, with the
resource path kept as fallback. Flags, color swatches, application and
MIME icons stay on their paths.
One entry does not go through the theme. The elements panel draws the
project root with ProjectFileGP in the 50 pixel slot it reserves for
element previews, and the name "project" also carries the 128 pixel
file the configuration dialog uses. On a 2x display Qt's loader picks
that file for a 50 pixel request and fills the slot. ProjectFileGP
loads the 16 and 22 pixel files directly, as before.
tests/qttest/tst_qeticons: every name in the theme resolves, the four
aliases resolve at 22 pixels, a Fusion tool button shows its icon at
3:1 with disabled weaker than enabled, and the project root icon stays
at 22 pixels or less when asked for 50 at 2x while the configuration
dialog still gets its 128 pixel file. The rendering helpers shared
with tst_qetpalette moved to tests/qttest/inkcontrast.h.
QETApp::checkBackupFiles() only reached checkCrashDump() when there was
nothing to recover:
if (stale_files.isEmpty()) {
checkCrashDump();
return;
}
A crash with a project open always leaves a stale KAutoSaveFile, so on the
next launch the recovery prompt won every time and the dump sat on disk
unoffered -- the report was unreachable in exactly the case it is most
wanted. Reported by ChuckNr11 as a side note in #898: "the report appeared
only once despite there being 10 or more crashes". It appears on the runs
that happen to have nothing to recover.
Discussion #644 step 5 asks that the two prompts never show at the same
time, which this keeps: the recovery prompt is answered first, then the
report. The recovery half moves into offerBackupFiles() so both paths fall
through to the same place.
Verified under Xvfb, from a real crash state (SIGABRT with a project open,
leaving both a stale file and a 5.4 KB crash_dump.log): the recovery prompt
appears, and dismissing it now brings up "Rapport de plantage" carrying the
version, git SHA, OS, Qt version and the log ring. Before this change the
report never appeared -- that half rests on the four lines above rather
than on a captured before/after, since re-creating the crash state for a
clean baseline run kept consuming it.
Not addressed: the dump is a single fixed path opened O_TRUNC, so
consecutive crashes still overwrite one another.
ctest 8/8.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Another exception: one non-converted code path in projectprintwindow.cpp to be followed-up.
One issue found in the Qt6 code path of diagramview.cpp which has been fixed.
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.
The comment sat one tab deeper than the code around it. Flagged in
review on PR #868. Whitespace only; no change to behaviour.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
QETApp::receiveMessage() called openFiles() directly. That slot runs inside
SingleApplication's socket handling: SingleApplicationPrivate::
slotDataAvailable() emits receivedMessage synchronously from the readyRead
lambda (singleapplication_p.cpp:452). openFiles() then loads a project --
seconds of work on a large one -- and openAndAddProject() puts up a modal
BackupDialog whose exec() runs a nested event loop while the socket handler
is still on the stack.
During that nested loop the secondary instance exits, the connection closes
and the QLocalSocket is deleted. When the dialog is dismissed and the stack
unwinds, QMetaObject::activate() carries on emitting on the freed sender and
the process dies.
A zero-timer returns to the event loop first, so the socket stack is fully
unwound before any project is opened.
Found by scorpio810 while testing PR #861, with a backtrace showing no QET
frame above the crash. His second suggestion, looking for a delete that
should be deleteLater(), turned out to be already satisfied at
singleapplication_p.cpp:331 -- which is why the deferred delete is not enough
on its own once a nested loop is in play.
Dismissing the dialog is the step that makes it fail: two earlier attempts to
reproduce it left the dialog open, the stack never unwound, and nothing
crashed. With the dialog dismissed it segfaults twice out of two; with this
change it survives twice out of two, opens the project as before, and ctest
stays green. Qt 6.10.2 on X11/xcb -- also checked under a headless Wayland
compositor and under Qt 5.15.18, so it is neither Wayland-specific nor a Qt6
regression.
The crash needs PR #861 to be reachable at all: without it splitWithSpaces()
returns an empty list, no project opens, and nothing enters this path.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A .qm compiled from a 0%-translated .ts (fi, no, rs, sk, sl, sr) loads
successfully but contains no messages, so setLanguage() treated the
language as loaded and never fell back to qet_en: users got the French
source strings instead of English. Treat an empty translator as not
loaded.
Also log the QET and Qt .qm files actually loaded in the startup
diagnostics (MachineInfo), to make translation reports easier to triage.
Apply the same split readSettings()/readSettingsState() pattern from
QETDiagramEditor to the other two main windows:
- QETElementEditor: split in constructor, call readSettingsState() after show()
- QETTitleBlockTemplateEditor: split readSettings(), callers call
readSettingsState() after show() (newTemplate + 2x openTitleBlockTemplate)
- Remove destructive settings.remove() guards that would delete saved
state on every Qt6 launch when restoreState() fails before show()
Co-authored-by: ispyisail
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.