Adds a real base-26 incrementing part type to the autonumbering engine,
alongside the 14 existing NumStrategy leaves. Unlike StringNum (a fixed,
non-incrementing text segment), AlphaNum::next()/previous() carry/borrow
entirely within the part's own value -- the composition loop in
NumerotationContextCommands doesn't need to change, since (unlike #578's
wrap-and-carry) nothing here needs to signal an adjacent part.
- incrementAlpha()/decrementAlpha() implement the spreadsheet-column-name
algorithm: increment carries right-to-left on 'z'/'Z' overflow,
prepending a new leading letter if the whole value overflows (z -> aa,
az -> ba). decrement is the exact inverse, including the symmetric
shrink case (aa -> z) once every position has borrowed. A single letter
already at "a"/"A" has no representable predecessor and is clamped
rather than turned into "z" -- caught via manual testing, since the
initial implementation mutated the string in the borrow loop before
checking whether to clamp, silently discarding the original value.
- Registered in NumerotationContext::validRegExpNum() but deliberately
not in validRegExpNumber(), so addValue() doesn't force alphabetic
values through int conversion.
- New "Cyclique"-adjacent "Alphabétique" entry in numparteditorw's type
dropdown, with its own letters-only QRegularExpressionValidator; the
increase spinbox is disabled since the step is always exactly one
letter, not a configurable amount.
Also wires the new part type through to actual element/conductor labels,
which turned out to be required for the feature to do anything visible
beyond folio numbering (which applies a NumerotationContext's
represented string directly). Element and conductor numbering instead
go through a separate formula-substitution layer
(autonum::sequentialNumbers + %sequ_/%seqt_/%seqh_-style placeholders in
AssignVariables::assignSequence()) that numerotationContextToFormula()
auto-populates. Without a matching placeholder, an "alpha" part would
silently vanish from the generated formula and never reach the label,
even though the underlying counter was advancing correctly:
- sequentialNumbers gained an `alpha` QStringList member (copy ctor,
operator=, operator==, toXml/fromXml, clear()).
- numerotationContextToFormula() emits a new %seqa_N placeholder for
alpha parts, the same way %sequ_N is emitted for unit parts.
- setSequential()/setSequentialToList() populate seqStruct.alpha,
passing the raw string through as-is rather than the .toInt()-based
formatting used for the numeric part types.
- AssignVariables::assignSequence() substitutes %seqa_N from
seqStruct.alpha, mirroring the existing %sequ_N/%seqt_N/%seqh_N
substitutions.
No "alphafolio" variant was added, matching the discussion's scope (only
unit/ten/hundred have folio-anchored variants).
Verified against production code via the numbering config dialog's own
Suivant/Précédent buttons: from "a", 25 clicks reached "z"; one more
produced "aa"; 25 more reached "az"; one more produced "ba" (carry).
Reversed: "ba"->"az"->(25 clicks)->"aa"->"z" (shrink)->(25 clicks)->"a".
One more "previous" at "a" correctly stayed at "a" after the clamp fix.
Also confirmed the Formule field auto-updates to "%seqa_1" the instant
the type is switched to "Alphabétique", confirming the formula-generation
wiring works live in the UI, not just at the engine level.
Adds a configurable wrap-at-N counter type to the autonumbering engine
(NumerotationContext + NumerotationContextCommands), covering PLC/rack-style
addressing conventions like "e0.0...e0.7, e1.0...e1.7" (8 channels per
card) generally, rather than hardcoding octal specifically.
- New "wrap" part type (WrapNum, alongside the existing UnitNum/TenNum/
HundredNum strategies) stores a modulus in addition to the existing
value/increase/initialvalue fields. Its own next()/previous() only wraps
its own value modulo the configured modulus -- carrying into (or
borrowing from) the adjacent part requires visibility across parts,
which only the composition loop has.
- NumerotationContextCommands::next()/previous() gained carry()/borrow()
helpers: when a wrap part's own next() would reach/exceed its modulus
(or go below 0 on previous()), the nearest preceding numeric part is
bumped by exactly one unit, skipping non-numeric parts (e.g. a "."
string separator). Wrap parts chain correctly if adjacent (e.g. seconds
wrapping into minutes wrapping into hours).
- For the leading part of a wrap-and-carry pair to stay fixed except when
carried into (i.e. actually produce "e0.0...e0.7, e1.0..." rather than
advancing on every step under its own strategy), its own increase must
be 0. The increase spinbox's minimum was 1, which made this
configuration impossible through the UI -- lowered to 0 and documented
with a tooltip, since this wasn't obvious from the UI alone.
- NumerotationContext gained a 5th pipe-separated field (modulus) in its
serialized string form, defaulting to 0 (non-wrapping) for every
existing part type; toXml()/fromXml() persist it as a "modulus" XML
attribute the same way "initialvalue" is already persisted for
unitfolio/tenfolio/hundredfolio.
- New "Cyclique (modulo)" entry in the part-type dropdown (numparteditorw),
available for element, conductor, and folio autonumbering alike, since
all three already go through NumerotationContextCommands.
Verified in the running app via the numbering config dialog's own
Suivant/Précédent buttons (which call the production
NumerotationContextCommands::next()/previous() directly): a two-part
context (unit, increase=0 + wrap mod 8) produced exactly
e0.0→...→e0.7→e1.0→...→e1.7 on repeated "next", and the exact reverse
(with correct borrowing) on repeated "previous".