Skip to content

Claude/mode submode display opinion xb193h - #1125

Merged
ea4k merged 19 commits into
masterfrom
claude/mode-submode-display-opinion-xb193h
Aug 19, 2026
Merged

Claude/mode submode display opinion xb193h#1125
ea4k merged 19 commits into
masterfrom
claude/mode-submode-display-opinion-xb193h

Conversation

@ea4k

@ea4k ea4k commented Aug 19, 2026

Copy link
Copy Markdown
Owner

No description provided.

claude and others added 19 commits August 19, 2026 13:54
Ham operators think of KLog's submode values (FT4, FT8, PSK31...) as
the mode itself, so the log grid now labels that column "Mode". The
true ADIF parent mode column (MFSK, PSK, RTTY...) is relabeled
"Mode ADIF" to make clear it's the underlying ADIF classification
rather than a second, unfamiliar concept.
Default log view now shows only the flattened "Mode" column
(submode) out of the box; "Mode ADIF" (modeid) must be enabled
explicitly via the column selector. Aligned the dead-code fallback
default list in SetupPageLogView::init() with the real default set.
- Default column order (log grid, when no custom order is saved) now
  places modeid and submode immediately after bandid instead of
  following raw DB schema order.
- Settings field list (SetupPageLogView) is reordered the same way,
  so Mode ADIF/Mode appear right after Band there too, and its
  "submode" entry is labeled "Mode (ADIF submode)" for clarity while
  the log view itself keeps the plain "Mode" label.
- SearchWindow (DX/awards search) now shows both the real ADIF mode
  (Mode ADIF) and the flattened submode (Mode) as separate columns,
  instead of only the flattened value under "Mode". The award-status
  lookup, which relied on that flattened text, now reads it from the
  submode column instead of modeid, preserving its behaviour.
By default SearchWindow now shows only "Mode" (the flattened submode),
positioned right after Band -- where "Mode ADIF" used to sit. "Mode
ADIF" is only added, right after Band and before Mode, when the user
has "modeid" active in their log view field settings, mirroring the
main log view's own default-hidden/opt-in behaviour for that column.
modeid is the ADIF parent mode (e.g. MFSK) and is shared by every
submode of that family, so keying the log table's uniqueness
constraint on it collapsed genuinely different QSOs (e.g. FT2 vs FT4,
both MFSK) that share callsign/band/timestamp/log. The second INSERT
silently failed and was swallowed as an "expected duplicate", losing
the QSO with no warning to the user.

Adds DB migration step 0.031 (updateTo031 -> recreateTableLog) to
apply the fixed constraint to existing databases. The migration is
safe: any two rows that would collide on submode already shared the
same modeid, so they could never have coexisted under the old
constraint in the first place.

Fixes #1119
modeid is the ADIF parent mode (e.g. MFSK) and is shared by every
submode of that family, so the "This QSO seems to be duplicated"
warning and the in-memory duplicate cache could flag genuinely
different QSOs (e.g. FT2 vs FT4, both MFSK) as duplicates.

Fixed consistently on both sides of the cache: loadDupeCacheBG() now
builds it from log.submode instead of log.modeid, and every writer/
reader (isThisQSODuplicated, FileManager::processQSO,
MainWindow::actionsJustAfterAddingOneQSO) now computes the mode id via
the existing getSubModeIdFromQSO() helper instead of getIdFromModeName
on the parent mode. filemanager.cpp previously had this backed out
(see removed comment) because only some call sites had been switched
to submode while the cache itself still keyed on modeid -- this fix
moves everything over together so cache writes and reads stay
consistent.

Also simplified the unused QSO-based findDuplicateId() overload in
dataproxy_sqlite.h to call getSubModeIdFromQSO() instead of
duplicating its logic inline.

Fixes #1120
logView never had an item delegate assigned, unlike every other
relational table view in the app (logs, subdivisions, sats, world
editor setup pages). Without QSqlRelationalDelegate, editing a
relational column (band, mode ADIF, mode, country...) inline uses the
default delegate, which edits/writes the raw foreign key id instead of
offering a combobox of the related values, so the edit fails and the
view reverts to the previous value.
Added LogModeDelegate (QSqlRelationalDelegate subclass) as the log
grid's item delegate for the modeid/submode columns:

- The Mode combobox only lists the user's active submodes (mirroring
  MainQSOEntryWidget's mode combo) instead of every submode in the DB.
- The Mode ADIF combobox lists the distinct parent modes derived from
  those active submodes.
- Picking a submode updates Mode ADIF to that submode's parent.
- Picking a parent mode resets the submode to the mode-table row that
  stands for "just this mode" (submode field equal to its own name).
- The cell's current value is always included even if since disabled,
  so opening the editor never silently changes it.

LogWindow::setActiveModes() feeds the delegate the same active-modes
list already pushed to MainQSOEntryWidget/MapWindow, wired in at both
call sites in mainwindow.cpp.
…edit

LogModeDelegate's combobox now commits and closes as soon as the user
picks an item (wired to QComboBox::activated), instead of waiting for
the editor to lose focus. Without this, the Mode ADIF/Mode sync only
took effect once the user clicked elsewhere, reading as if the pick
hadn't worked.

Also removed DoubleClicked from logView's edit triggers. Once the log
grid got a working delegate, double-clicking a cell both opened the
QSO edit dialog (slotDoubleClickLog) and started inline editing on the
same click, and the two fought over the double-click -- the dialog
stopped opening. Double-click is now reserved for the edit dialog;
inline editing is still reachable via F2/Enter or typing on a selected
cell.
Added QAbstractItemView::SelectedClicked to logView's edit triggers,
so the click gesture on the log grid is: 1st click selects the row
(default), a further click on an already-selected row edits that
cell, and double-click still opens the QSO edit dialog (unaffected,
since DoubleClicked was already excluded from the edit triggers).
Replaced the built-in SelectedClicked edit trigger with manual
handling: a click on the row the previous click already landed on
arms a QTimer (system doubleClickInterval()) instead of starting the
editor immediately. slotDoubleClickLog() cancels that timer as soon as
a double-click is recognized, so double-clicking an already-selected
row reliably opens the QSO edit dialog instead of racing a cell editor
open from the first of the two clicks.

m_lastClickedRow/pending state is reset in createlogPanel() so a row
index left over from a previous log/reload can't be mistaken for
"already selected" in the freshly loaded one.
The log grid selects whole rows (needed for the bulk QSO actions), so
there was no visible way to tell which cell was "current" -- the one
arrow keys move, and the one F2/click-on-already-selected-row would
edit. LogModeDelegate now draws a border around the cell that has
QStyle::State_HasFocus, in the palette's highlight color, on top of
the normal row selection background, without changing the selection
behaviour itself.
…close

getBandsInLog/getModesInLog/getSubModesInLog (DISTINCT/GROUP BY queries
run every time the Settings dialog opens and again when it closes, via
SetupDialog::readActiveBands/readActiveModes and MainWindow's own
copies) and the duplicate-QSO lookups were full table scans: bandid,
modeid, submode and lognumber had no index anywhere in the schema.

Added DataBase::createLogIndexes(), called right after a fresh "log"
table is created and again by recreateTableLog() once "logtemp" has
been renamed back to "log". Indexing only the final "log" table (never
the short-lived "logtemp" scratch table) and always under the same
fixed name avoids two problems a naive per-call-site index would hit:
slowing down logtemp's bulk INSERT for no benefit, and colliding on
"index already exists" the next time some future migration calls
recreateTableLog() again.

Folded into updateTo031() (issue #1119), which already rebuilds the
log table on upgrade -- no separate DB version bump needed.
DXClusterAssistant::modeFamily() collapsed every mode to its ADIF
parent (e.g. MFSK) before comparing a logged QSO's mode against a
spot's mode. FT2 and FT4 are both MFSK but are operationally
different digital modes, so logging a QSO on FT4 caused
removeSpotsOfLoggedQSO() to also remove -- and alreadyWorked() to
treat as worked -- a still-workable FT2 spot for the same call/band.

modeFamily() now compares by the actual submode instead, keeping only
the one grouping that is genuinely correct: USB/LSB/SSB voice
sidebands, which fold to a common "SSB" bucket as before. An unknown
mode still returns empty, taking no part in the comparison, same as
before.

Fixes #1123
Awards::updateDXCCStatus() built dxccStatusList keyed by log.modeid
(the ADIF parent mode, e.g. MFSK), while every caller of
getQSOStatus() already computes a submode-specific mode id (currentModeShown,
mainQSOEntryWidget's mode combo, SearchWindow, DXAssistantEngine all
resolve to specific submodes like FT4). Since a submode id almost
never equals the parent id stored in EntityStatus, the direct
comparison in getQSOStatus() always failed: DXCC status showed
"needed" even when already confirmed on that exact submode.

Fixed by grouping/keying updateDXCCStatus()'s query and
extractEntityStatus() by log.submode instead of log.modeid, matching
what every caller already expects.

DXCCStatusWidget worked around the mismatch by expanding to
getModeGroupIds() (every mode sharing the same ADIF parent), which
traded "never matches" for "matches too broadly": selecting FT4 showed
the combined status of the whole MFSK family (FT2+FT4+FST4+JS8+Q65...).
Added DataProxy_SQLite::getSidebandGroupIds(), which only groups the
one family where that is actually intentional -- USB/LSB/SSB -- and
switched DXCCStatusWidget to it.

AwardsWidget's own getModeGroupIds() usage (counters, not status) is
left for #1122, since it needs the same modeid->submode fix at the SQL
level (buildModeInClause/getFieldInBand) to be worth changing.

Fixes #1121
LogWindow::saveColumnOrder()/retoreColumsOrder() persisted the log
grid's custom column order via QVariant::fromValue<QList<int>>, but
QList<int> is not a type QVariant knows how to serialize/deserialize
on its own. This produced the warning on every startup, and silently
never restored a saved custom order -- the size-mismatch fallback in
retoreColumsOrder() absorbed the failed load every time.

Switched to storing it as a QStringList of numbers, the same pattern
already used right below for ColumnWidths (a genuinely simple type
QSettings/QVariant round-trip natively, no custom registration
needed). Pre-existing, unrelated to this session's earlier changes;
found via a user startup log.
buildModeInClause() (shared by getQSOonYear, getDXCConYear,
getCQzonYear, getHowManyQSOInLog, getHowManyConfirmedQSLInLog),
Awards::getWAZWorked/getWAZConfirmed, and the modeIds-list branch of
getFieldInBand all filtered "AND modeid IN (...)" -- modeid is the
ADIF parent mode (e.g. MFSK), shared by every submode of that family,
so selecting FT4 in the Awards tab actually counted every MFSK
submode (FT2, FST4, JS8, Q65...) too. getFieldInBand's other branch
(the one taking _mode as a string) was already fixed to filter on
submode via getSubModeFilterSQL -- this finishes that migration for
the modeIds-list branch and its siblings.

AwardsWidget::showAwards()/showDXMarathon() fed all of the above from
dataProxy->getModeGroupIds(currentMode), which (independently of the
modeid/submode column choice) over-broadens by grouping on shared ADIF
parent -- the same bug already fixed for DXCCStatusWidget in #1121.
Switched both to getSidebandGroupIds(), which only groups the one
family where that's actually correct: USB/LSB/SSB.

Fixes #1122
@ea4k
ea4k merged commit b68670b into master Aug 19, 2026
3 of 9 checks passed
@ea4k
ea4k deleted the claude/mode-submode-display-opinion-xb193h branch August 19, 2026 19:20
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants