Merge pull request #634 from ispyisail/feature-autonum-number-format

Numbering parts: user-settable display format (zero mask)
This commit is contained in:
Laurent Trinques
2026-08-02 11:50:59 +02:00
committed by GitHub
8 changed files with 150 additions and 25 deletions
+46 -8
View File
@@ -39,6 +39,7 @@ namespace autonum
sequentialNumbers::sequentialNumbers(const sequentialNumbers &other) sequentialNumbers::sequentialNumbers(const sequentialNumbers &other)
{ {
unit = other.unit; unit = other.unit;
wrap = other.wrap;
unit_folio = other.unit_folio; unit_folio = other.unit_folio;
ten = other.ten; ten = other.ten;
ten_folio = other.ten_folio; ten_folio = other.ten_folio;
@@ -57,6 +58,7 @@ namespace autonum
return (*this); return (*this);
unit = other.unit; unit = other.unit;
wrap = other.wrap;
unit_folio = other.unit_folio; unit_folio = other.unit_folio;
ten = other.ten; ten = other.ten;
ten_folio = other.ten_folio; ten_folio = other.ten_folio;
@@ -70,6 +72,7 @@ namespace autonum
bool sequentialNumbers::operator==(const sequentialNumbers &other) const bool sequentialNumbers::operator==(const sequentialNumbers &other) const
{ {
if (unit == other.unit && \ if (unit == other.unit && \
wrap == other.wrap && \
unit_folio == other.unit_folio && \ unit_folio == other.unit_folio && \
ten == other.ten && \ ten == other.ten && \
ten_folio == other.ten_folio && \ ten_folio == other.ten_folio && \
@@ -107,6 +110,11 @@ namespace autonum
document, document,
"unit", "unit",
unit.join(";"))); unit.join(";")));
if (!wrap.isEmpty())
element.appendChild(QETXML::textToDomElement(
document,
"wrap",
wrap.join(";")));
if (!unit_folio.isEmpty()) if (!unit_folio.isEmpty())
element.appendChild(QETXML::textToDomElement( element.appendChild(QETXML::textToDomElement(
document, document,
@@ -156,6 +164,11 @@ namespace autonum
from = element.firstChildElement("unit"); from = element.firstChildElement("unit");
unit = from.text().split(";"); unit = from.text().split(";");
//Absent from files written before cyclic parts could be
//rendered; an empty list is the correct reading of that.
from = element.firstChildElement("wrap");
wrap = from.text().split(";");
from = element.firstChildElement("unitFolio"); from = element.firstChildElement("unitFolio");
unit_folio = from.text().split(";"); unit_folio = from.text().split(";");
@@ -179,6 +192,7 @@ namespace autonum
void sequentialNumbers::clear() void sequentialNumbers::clear()
{ {
unit.clear(); unit.clear();
wrap.clear();
unit_folio.clear(); unit_folio.clear();
ten.clear(); ten.clear();
ten_folio.clear(); ten_folio.clear();
@@ -434,7 +448,8 @@ namespace autonum
qMax( qMax(
qMax(m_seq_struct.hundred.size(), qMax(m_seq_struct.hundred.size(),
m_seq_struct.ten.size()), m_seq_struct.ten.size()),
m_seq_struct.alpha.size()) qMax(m_seq_struct.alpha.size(),
m_seq_struct.wrap.size()))
); );
for (int i=1; i<=max ; i++) for (int i=1; i<=max ; i++)
@@ -442,6 +457,9 @@ namespace autonum
if (m_assigned_label.contains("%sequ_" + QString::number(i)) && m_seq_struct.unit.size() >= i) { if (m_assigned_label.contains("%sequ_" + QString::number(i)) && m_seq_struct.unit.size() >= i) {
m_assigned_label.replace("%sequ_" + QString::number(i),m_seq_struct.unit.at(i-1)); m_assigned_label.replace("%sequ_" + QString::number(i),m_seq_struct.unit.at(i-1));
} }
if (m_assigned_label.contains("%seqw_" + QString::number(i)) && m_seq_struct.wrap.size() >= i) {
m_assigned_label.replace("%seqw_" + QString::number(i),m_seq_struct.wrap.at(i-1));
}
if (m_assigned_label.contains("%seqt_" + QString::number(i)) && m_seq_struct.ten.size() >= i) { if (m_assigned_label.contains("%seqt_" + QString::number(i)) && m_seq_struct.ten.size() >= i) {
m_assigned_label.replace("%seqt_" + QString::number(i),m_seq_struct.ten.at(i-1)); m_assigned_label.replace("%seqt_" + QString::number(i),m_seq_struct.ten.at(i-1));
} }
@@ -479,15 +497,26 @@ namespace autonum
{ {
if (context.itemAt(i).at(0) == type) if (context.itemAt(i).at(0) == type)
{ {
const QStringList item = context.itemAt(i);
//A zero-padding mask, spreadsheet style: its length is the
//minimum number of digits. It overrides the width implied
//by the part type, so "Chiffre 01" with a mask of "0000"
//pads to four. An absent mask -- which is every context
//written before the field existed -- falls through to the
//type's own width, so nothing about existing projects
//changes.
const QString mask = NumerotationContext::formatOf(item);
QString number; QString number;
if (type == "ten" || type == "tenfolio") if (type == "alpha")
number = QString("%1").arg(context.itemAt(i).at(1).toInt(), 2, 10, QChar('0'));
else if (type == "hundred" || type == "hundredfolio")
number = QString("%1").arg(context.itemAt(i).at(1).toInt(), 3, 10, QChar('0'));
else if (type == "alpha")
//Alphabetic value, not an integer -- used as-is. //Alphabetic value, not an integer -- used as-is.
number = context.itemAt(i).at(1); number = item.at(1);
else number = QString::number(context.itemAt(i).at(1).toInt()); else if (!mask.isEmpty())
number = QString("%1").arg(item.at(1).toInt(), mask.length(), 10, QChar('0'));
else if (type == "ten" || type == "tenfolio")
number = QString("%1").arg(item.at(1).toInt(), 2, 10, QChar('0'));
else if (type == "hundred" || type == "hundredfolio")
number = QString("%1").arg(item.at(1).toInt(), 3, 10, QChar('0'));
else number = QString::number(item.at(1).toInt());
list.append(number); list.append(number);
} }
} }
@@ -553,6 +582,10 @@ namespace autonum
{ {
autonum::setSequentialToList(seqStruct.unit, context,"unit"); autonum::setSequentialToList(seqStruct.unit, context,"unit");
} }
if (label.contains("%seqw_"))
{
autonum::setSequentialToList(seqStruct.wrap, context,"wrap");
}
if (label.contains("%sequf_")) if (label.contains("%sequf_"))
{ {
autonum::setSequentialToList(seqStruct.unit_folio, context,"unitfolio"); autonum::setSequentialToList(seqStruct.unit_folio, context,"unitfolio");
@@ -594,6 +627,7 @@ namespace autonum
QString value; QString value;
QString formula; QString formula;
int count_unit = 0; int count_unit = 0;
int count_wrap = 0;
int count_unitf = 0; int count_unitf = 0;
int count_ten = 0; int count_ten = 0;
int count_tenf = 0; int count_tenf = 0;
@@ -636,6 +670,10 @@ namespace autonum
count_unit++; count_unit++;
formula.append("%sequ_" + QString::number(count_unit)); formula.append("%sequ_" + QString::number(count_unit));
} }
else if (type == "wrap") {
count_wrap++;
formula.append("%seqw_" + QString::number(count_wrap));
}
else if (type == "unitfolio") { else if (type == "unitfolio") {
count_unitf++; count_unitf++;
formula.append("%sequf_" + QString::number(count_unitf)); formula.append("%sequf_" + QString::number(count_unitf));
+2
View File
@@ -47,6 +47,8 @@ namespace autonum
void clear(); void clear();
QStringList unit; QStringList unit;
/// Values of the cyclic (modulo) parts, referenced by %seqw_N.
QStringList wrap;
QStringList unit_folio; QStringList unit_folio;
QStringList ten; QStringList ten;
QStringList ten_folio; QStringList ten_folio;
+26 -4
View File
@@ -52,13 +52,18 @@ void NumerotationContext::clear ()
@param increase the increase number of value @param increase the increase number of value
@param initialvalue @param initialvalue
@param modulus wrap-and-carry modulus (0 means "not a wrapping part") @param modulus wrap-and-carry modulus (0 means "not a wrapping part")
@param format zero-padding mask, spreadsheet style: "00" pads to two
digits, "000" to three. Empty keeps the part type's natural width, so
an absent format reproduces exactly the behaviour of every context
written before this field existed.
@return true if value is append @return true if value is append
*/ */
bool NumerotationContext::addValue(const QString &type, bool NumerotationContext::addValue(const QString &type,
const QVariant &value, const QVariant &value,
const int increase, const int increase,
const int initialvalue, const int initialvalue,
const int modulus) { const int modulus,
const QString &format) {
if (!keyIsAcceptable(type) && !value.canConvert<QString>()) if (!keyIsAcceptable(type) && !value.canConvert<QString>())
return false; return false;
if (keyIsNumber(type) && !value.canConvert<int>()) if (keyIsNumber(type) && !value.canConvert<int>())
@@ -74,7 +79,9 @@ bool NumerotationContext::addValue(const QString &type,
+ "|" + "|"
+ QString::number(initialvalue) + QString::number(initialvalue)
+ "|" + "|"
+ QString::number(modulus); + QString::number(modulus)
+ "|"
+ QString(format).remove("|");
return true; return true;
} }
@@ -179,6 +186,9 @@ QDomElement NumerotationContext::toXml(QDomDocument &d, const QString& str) {
if (strl.at(0) == ("wrap") && strl.size() > 4) { if (strl.at(0) == ("wrap") && strl.size() > 4) {
part.setAttribute("modulus", strl.at(4)); part.setAttribute("modulus", strl.at(4));
} }
if (strl.size() > 5 && !strl.at(5).isEmpty()) {
part.setAttribute("format", strl.at(5));
}
num_auto.appendChild(part); num_auto.appendChild(part);
} }
return num_auto; return num_auto;
@@ -190,7 +200,7 @@ QDomElement NumerotationContext::toXml(QDomDocument &d, const QString& str) {
*/ */
void NumerotationContext::fromXml(QDomElement &e) { void NumerotationContext::fromXml(QDomElement &e) {
clear(); clear();
foreach(QDomElement qde, QET::findInDomElement(e, "part")) addValue(qde.attribute("type"), qde.attribute("value"), qde.attribute("increase").toInt(), qde.attribute("initialvalue").toInt(), qde.attribute("modulus").toInt()); foreach(QDomElement qde, QET::findInDomElement(e, "part")) addValue(qde.attribute("type"), qde.attribute("value"), qde.attribute("increase").toInt(), qde.attribute("initialvalue").toInt(), qde.attribute("modulus").toInt(), qde.attribute("format"));
} }
/** /**
@@ -206,5 +216,17 @@ void NumerotationContext::replaceValue(int index, QString content) {
QString increase = strl.at(2); QString increase = strl.at(2);
QString initvalue = strl.at(3); QString initvalue = strl.at(3);
QString modulus = strl.size() > 4 ? strl.at(4) : QStringLiteral("0"); QString modulus = strl.size() > 4 ? strl.at(4) : QStringLiteral("0");
content_[index] = type + "|" + value + "|" + increase + "|" + initvalue + "|" + modulus; QString format = strl.size() > 5 ? strl.at(5) : QString();
content_[index] = type + "|" + value + "|" + increase + "|" + initvalue + "|" + modulus + "|" + format;
}
/**
@brief NumerotationContext::formatOf
@param item : a context item as returned by itemAt()
@return the part's zero-padding mask, or an empty string when it has
none -- which every context written before the field existed will be.
*/
QString NumerotationContext::formatOf(const QStringList &item)
{
return item.size() > 5 ? item.at(5) : QString();
} }
+5 -1
View File
@@ -37,7 +37,11 @@ class NumerotationContext
const QVariant & = QVariant(1), const QVariant & = QVariant(1),
const int = 1, const int = 1,
const int = 0, const int = 0,
const int = 0); const int = 0,
const QString & = QString());
/// Zero-padding mask of a part, e.g. "00"; empty means the type's
/// own natural width. See addValue().
static QString formatOf(const QStringList &item);
QString operator[] (const int &) const; QString operator[] (const int &) const;
void operator << (const NumerotationContext &); void operator << (const NumerotationContext &);
int size() const; int size() const;
@@ -259,7 +259,7 @@ NumerotationContext NumStrategy::nextString (const NumerotationContext &nc,
{ {
QStringList strl = nc.itemAt(i); QStringList strl = nc.itemAt(i);
NumerotationContext newnc; NumerotationContext newnc;
newnc.addValue(strl.at(0), strl.at(1), strl.at(2).toInt()); newnc.addValue(strl.at(0), strl.at(1), strl.at(2).toInt(), 0, 0, NumerotationContext::formatOf(strl));
return (newnc); return (newnc);
} }
@@ -273,7 +273,7 @@ NumerotationContext NumStrategy::nextNumber (const NumerotationContext &nc,
QStringList strl = nc.itemAt(i); QStringList strl = nc.itemAt(i);
NumerotationContext newnc; NumerotationContext newnc;
QString value = QString::number( (strl.at(1).toInt()) + (strl.at(2).toInt()) ); QString value = QString::number( (strl.at(1).toInt()) + (strl.at(2).toInt()) );
newnc.addValue(strl.at(0), value, strl.at(2).toInt(), strl.at(3).toInt()); newnc.addValue(strl.at(0), value, strl.at(2).toInt(), strl.at(3).toInt(), 0, NumerotationContext::formatOf(strl));
return (newnc); return (newnc);
} }
@@ -287,7 +287,7 @@ NumerotationContext NumStrategy::previousNumber(const NumerotationContext &nc,
QStringList strl = nc.itemAt(i); QStringList strl = nc.itemAt(i);
NumerotationContext newnc; NumerotationContext newnc;
QString value = QString::number( (strl.at(1).toInt()) - (strl.at(2).toInt()) ); QString value = QString::number( (strl.at(1).toInt()) - (strl.at(2).toInt()) );
newnc.addValue(strl.at(0), value, strl.at(2).toInt(), strl.at(3).toInt()); newnc.addValue(strl.at(0), value, strl.at(2).toInt(), strl.at(3).toInt(), 0, NumerotationContext::formatOf(strl));
return (newnc); return (newnc);
} }
@@ -550,7 +550,7 @@ NumerotationContext WrapNum::next (const NumerotationContext &nc, const int i) c
int new_value = strl.at(1).toInt() + increase; int new_value = strl.at(1).toInt() + increase;
if (modulus > 0) if (modulus > 0)
new_value %= modulus; new_value %= modulus;
newnc.addValue(strl.at(0), QString::number(new_value), increase, strl.at(3).toInt(), modulus); newnc.addValue(strl.at(0), QString::number(new_value), increase, strl.at(3).toInt(), modulus, NumerotationContext::formatOf(strl));
return (newnc); return (newnc);
} }
@@ -570,7 +570,7 @@ NumerotationContext WrapNum::previous(const NumerotationContext &nc, const int i
if (new_value < 0) if (new_value < 0)
new_value += modulus; new_value += modulus;
} }
newnc.addValue(strl.at(0), QString::number(new_value), increase, strl.at(3).toInt(), modulus); newnc.addValue(strl.at(0), QString::number(new_value), increase, strl.at(3).toInt(), modulus, NumerotationContext::formatOf(strl));
return (newnc); return (newnc);
} }
@@ -706,7 +706,7 @@ NumerotationContext AlphaNum::next (const NumerotationContext &nc, const int i)
{ {
QStringList strl = nc.itemAt(i); QStringList strl = nc.itemAt(i);
NumerotationContext newnc; NumerotationContext newnc;
newnc.addValue(strl.at(0), incrementAlpha(strl.at(1)), strl.at(2).toInt()); newnc.addValue(strl.at(0), incrementAlpha(strl.at(1)), strl.at(2).toInt(), 0, 0, NumerotationContext::formatOf(strl));
return (newnc); return (newnc);
} }
@@ -718,7 +718,7 @@ NumerotationContext AlphaNum::previous(const NumerotationContext &nc, const int
{ {
QStringList strl = nc.itemAt(i); QStringList strl = nc.itemAt(i);
NumerotationContext newnc; NumerotationContext newnc;
newnc.addValue(strl.at(0), decrementAlpha(strl.at(1)), strl.at(2).toInt()); newnc.addValue(strl.at(0), decrementAlpha(strl.at(1)), strl.at(2).toInt(), 0, 0, NumerotationContext::formatOf(strl));
return (newnc); return (newnc);
} }
+41 -5
View File
@@ -18,6 +18,8 @@
#include "numparteditorw.h" #include "numparteditorw.h"
#include "ui_numparteditorw.h" #include "ui_numparteditorw.h"
#include "../numerotationcontext.h"
#include <QRegularExpressionValidator> #include <QRegularExpressionValidator>
/** /**
@@ -35,6 +37,9 @@ NumPartEditorW::NumPartEditorW(int type, QWidget *parent) :
{ {
ui -> setupUi(this); ui -> setupUi(this);
setVisibleItems(); setVisibleItems();
//The mask is a run of zeros and nothing else, so it cannot be typed
//into a state the renderer would have to reject.
ui -> format_le -> setValidator(new QRegularExpressionValidator(QRegularExpression("0*"), this));
setType(NumPartEditorW::unit, true); setType(NumPartEditorW::unit, true);
} }
@@ -99,6 +104,7 @@ NumPartEditorW::NumPartEditorW (NumerotationContext &context,
ui -> increase_spinBox -> setValue(strl.at(2).toInt()); ui -> increase_spinBox -> setValue(strl.at(2).toInt());
if (strl.at(0)=="wrap" && strl.size() > 4) if (strl.at(0)=="wrap" && strl.size() > 4)
ui -> modulus_spinBox -> setValue(strl.at(4).toInt()); ui -> modulus_spinBox -> setValue(strl.at(4).toInt());
ui -> format_le -> setText(NumerotationContext::formatOf(strl));
} }
} }
@@ -219,23 +225,30 @@ NumerotationContext NumPartEditorW::toNumContext()
type_str = "alpha"; type_str = "alpha";
break; break;
} }
const QString number_format = ui -> format_le -> text();
if (type_str == "unitfolio" if (type_str == "unitfolio"
|| type_str == "tenfolio" || type_str == "tenfolio"
|| type_str == "hundredfolio") || type_str == "hundredfolio")
nc.addValue(type_str, nc.addValue(type_str,
ui -> value_field -> displayText(), ui -> value_field -> displayText(),
ui -> increase_spinBox -> value(), ui -> increase_spinBox -> value(),
ui->value_field->displayText().toInt()); ui->value_field->displayText().toInt(),
0,
number_format);
else if (type_str == "wrap") else if (type_str == "wrap")
nc.addValue(type_str, nc.addValue(type_str,
ui -> value_field -> displayText(), ui -> value_field -> displayText(),
ui -> increase_spinBox -> value(), ui -> increase_spinBox -> value(),
0, 0,
ui -> modulus_spinBox -> value()); ui -> modulus_spinBox -> value(),
number_format);
else else
nc.addValue(type_str, nc.addValue(type_str,
ui -> value_field -> displayText(), ui -> value_field -> displayText(),
ui -> increase_spinBox -> value()); ui -> increase_spinBox -> value(),
0,
0,
number_format);
return nc; return nc;
} }
@@ -317,6 +330,14 @@ void NumPartEditorW::on_increase_spinBox_valueChanged(int) {
@brief NumPartEditorW::on_modulus_spinBox_valueChanged @brief NumPartEditorW::on_modulus_spinBox_valueChanged
emit changed when modulus_spinBox value changed emit changed when modulus_spinBox value changed
*/ */
/**
@brief NumPartEditorW::on_format_le_textEdited
emit changed when the display format is edited
*/
void NumPartEditorW::on_format_le_textEdited(const QString &) {
emit changed();
}
void NumPartEditorW::on_modulus_spinBox_valueChanged(int) { void NumPartEditorW::on_modulus_spinBox_valueChanged(int) {
if (!ui -> value_field -> text().isEmpty()) emit changed(); if (!ui -> value_field -> text().isEmpty()) emit changed();
} }
@@ -359,8 +380,6 @@ void NumPartEditorW::setType(NumPartEditorW::type t, bool fnum) {
ui -> value_field -> setValidator(intValidator); ui -> value_field -> setValidator(intValidator);
ui -> increase_spinBox -> setEnabled(true); ui -> increase_spinBox -> setEnabled(true);
ui -> increase_spinBox -> setValue(1); ui -> increase_spinBox -> setValue(1);
if (t == wrap)
ui -> modulus_spinBox -> setValue(8);
} }
//@t isn't a numeric type //@t isn't a numeric type
else if (t == string else if (t == string
@@ -414,7 +433,24 @@ void NumPartEditorW::setType(NumPartEditorW::type t, bool fnum) {
ui -> increase_spinBox -> setDisabled(true); ui -> increase_spinBox -> setDisabled(true);
} }
} }
//A modulus of 0 means "no cycle", which makes a Cyclique part behave
//exactly like a plain digit. Defaulting it used to live in the numeric
//behavior block above, which is skipped when the previous type was
//itself numeric -- so the ordinary path of turning the default
//"Chiffre 1" into a "Cyclique (modulo)" left the modulus at 0 and
//produced a wrap part that never wrapped. Kept out of that block so it
//applies whatever the part was before, and only when the current value
//is unusable, so a modulus the user chose on purpose is not clobbered.
if (t == wrap && ui -> modulus_spinBox -> value() <= 0)
ui -> modulus_spinBox -> setValue(8);
ui -> modulus_spinBox -> setEnabled(t == wrap); ui -> modulus_spinBox -> setEnabled(t == wrap);
//A padding mask only means anything for a part rendered as a number.
const bool numeric = (t == unit || t == unitfolio || t == ten
|| t == tenfolio || t == hundred || t == hundredfolio
|| t == wrap);
ui -> format_le -> setEnabled(numeric);
if (!numeric)
ui -> format_le -> clear();
type_= t; type_= t;
} }
+1
View File
@@ -66,6 +66,7 @@ class NumPartEditorW : public QWidget
void on_value_field_textEdited(); void on_value_field_textEdited();
void on_increase_spinBox_valueChanged(int); void on_increase_spinBox_valueChanged(int);
void on_modulus_spinBox_valueChanged(int); void on_modulus_spinBox_valueChanged(int);
void on_format_le_textEdited(const QString &);
void setType (NumPartEditorW::type t, bool=false); void setType (NumPartEditorW::type t, bool=false);
signals: signals:
+22
View File
@@ -126,6 +126,28 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QLineEdit" name="format_le">
<property name="enabled">
<bool>false</bool>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Format d'affichage : une suite de zéros donne le nombre minimum de chiffres (00 = 07, 000 = 007). Vide = largeur naturelle du type.</string>
</property>
<property name="placeholderText">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>