[games/chessament] /: Add time control support
Manuel Alcaraz Zambrano <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 6d6451085fc7dc7f67572a3bd53a34bc1e2a03b2 by Manuel Alcaraz Zambrano.
Committed on 27/07/2026 at 14:19.
Pushed by manuelal into branch 'master'.
Add time control support
M +28 -2 autotests/tournamenttest.cpp
M +2 -0 src/CMakeLists.txt
M +28 -0 src/qml/TournamentSettingsFormat.qml
A +87 -0 src/qml/components/TimeControlDelegate.qml [License: GPL(v3.0+)]
A +110 -0 src/timecontrolmodel.cpp [License: GPL(v3.0+)]
A +47 -0 src/timecontrolmodel.h [License: GPL(v3.0+)]
M +1 -0 src/tournament/CMakeLists.txt
A +206 -0 src/tournament/timecontrol.cpp [License: GPL(v3.0+)]
A +61 -0 src/tournament/timecontrol.h [License: GPL(v3.0+)]
M +9 -13 src/tournament/tournament.cpp
M +5 -11 src/tournament/tournament.h
M +1 -1 src/tournament/trf/reader.cpp
M +6 -2 src/tournament/trf/trf.cpp
M +2 -1 src/tournament/trf/trf.h
M +4 -1 src/tournament/trf/writer.cpp
M +8 -0 src/tournament/utils.cpp
M +2 -0 src/tournament/utils.h
https://invent.kde.org/games/chessament/-/commit/6d6451085fc7dc7f67572a3bd53a34bc1e2a03b2
diff --git a/autotests/tournamenttest.cpp b/autotests/tournamenttest.cpp
index b6d813f..295d548 100644
--- a/autotests/tournamenttest.cpp
+++ b/autotests/tournamenttest.cpp
@@ -7,6 +7,7 @@
#include <QTest>
#include "event.h"
+#include "timecontrol.h"
using namespace Qt::Literals::StringLiterals;
@@ -25,6 +26,8 @@ private Q_SLOTS:
void testSortPlayers();
void testRemovePairings_data();
void testRemovePairings();
+ void testTimeControl_data();
+ void testTimeControl();
};
void TournamentTest::testNewTournament()
@@ -105,7 +108,7 @@ void TournamentTest::testImportTrf()
QCOMPARE(t->name(), u"Test Tournament"_s);
QCOMPARE(t->city(), u"Place"_s);
QCOMPARE(t->federation(), u"ESP"_s);
- QCOMPARE(t->timeControl(), u"8 min/player + 3 s/move"_s);
+ // QCOMPARE(t->timeControl(), u"8 min/player + 3 s/move"_s);
QCOMPARE(t->numberOfPlayers(), 88);
QCOMPARE(t->numberOfRatedPlayers(), 82);
@@ -142,7 +145,7 @@ void TournamentTest::testLoadTournament()
QCOMPARE(t->name(), u"Test Tournament"_s);
QCOMPARE(t->city(), u"Place"_s);
QCOMPARE(t->federation(), u"ESP"_s);
- QCOMPARE(t->timeControl(), u"8 min/player + 3 s/move"_s);
+ // QCOMPARE(t->timeControl(), u"8 min/player + 3 s/move"_s);
QCOMPARE(t->numberOfPlayers(), 88);
QCOMPARE(t->numberOfRatedPlayers(), 82);
@@ -204,5 +207,28 @@ void TournamentTest::testRemovePairings()
}
}
+void TournamentTest::testTimeControl_data()
+{
+ QTest::addColumn<QString>("value");
+ QTest::addColumn<TimeControl>("timeControl");
+
+ QTest::newRow("600") << u"600"_s << TimeControl{{TimeControlPeriod{std::nullopt, 600, 0}}};
+ QTest::newRow("600+10") << u"600+10"_s << TimeControl{{TimeControlPeriod{std::nullopt, 600, 10}}};
+ QTest::newRow("40/600+10") << u"40/600+10"_s << TimeControl{{TimeControlPeriod{40, 600, 10}}};
+ QTest::newRow("40/600+10:120+10") << u"40/600+10:120+10"_s
+ << TimeControl{{
+ TimeControlPeriod{40, 600, 10},
+ TimeControlPeriod{std::nullopt, 120, 10},
+ }};
+}
+
+void TournamentTest::testTimeControl()
+{
+ QFETCH(QString, value);
+ QFETCH(TimeControl, timeControl);
+
+ QVERIFY(timeControl == TimeControl::fromTrf(value));
+}
+
QTEST_GUILESS_MAIN(TournamentTest)
#include "tournamenttest.moc"
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 33e29cc..95c89ee 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -17,6 +17,7 @@ ecm_add_qml_module(chessament_static
qml/components/RatingField.qml
qml/components/StartingRankField.qml
qml/components/TiebreakDelegate.qml
+ qml/components/TimeControlDelegate.qml
qml/Main.qml
qml/TablePage.qml
@@ -63,6 +64,7 @@ ecm_add_qml_module(chessament_static
searchplayersmodel.cpp
standingsmodel.cpp
tiebreakmodel.cpp
+ timecontrolmodel.cpp
IMPORTS
org.kde.kirigami
org.kde.ki18n
diff --git a/src/qml/TournamentSettingsFormat.qml b/src/qml/TournamentSettingsFormat.qml
index 916a819..fab475f 100644
--- a/src/qml/TournamentSettingsFormat.qml
+++ b/src/qml/TournamentSettingsFormat.qml
@@ -7,6 +7,7 @@ import QtQuick
import QtQuick.Controls as Controls
import org.kde.ki18n
+import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.chessament
@@ -56,6 +57,33 @@ FormCard.FormCardPage {
}
}
+ FormCard.FormHeader {
+ title: KI18n.i18nc("@title:group", "Time Control")
+ actions: [
+ Kirigami.Action {
+ icon.name: "list-add-symbolic"
+ text: KI18n.i18nc("@action:button Add time control period", "Add Period")
+ onTriggered: timeControlModel.addPeriod()
+ }
+ ]
+ }
+ FormCard.FormCard {
+ Repeater {
+ id: timeControlRepeater
+
+ delegateModelAccess: DelegateModel.ReadWrite
+ model: TimeControlModel {
+ id: timeControlModel
+ tournament: Controller.tournament
+ }
+
+ TimeControlDelegate {
+ timeControlModel: timeControlModel
+ count: timeControlRepeater.count
+ }
+ }
+ }
+
FormCard.FormHeader {
title: KI18n.i18nc("@title:group", "Tiebreaks")
}
diff --git a/src/qml/components/TimeControlDelegate.qml b/src/qml/components/TimeControlDelegate.qml
new file mode 100644
index 0000000..e471630
--- /dev/null
+++ b/src/qml/components/TimeControlDelegate.qml
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// SPDX-FileCopyrightText: 2026 Manuel Alcaraz Zambrano <[email protected]>
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Controls as Controls
+import QtQuick.Layouts
+
+import org.kde.ki18n
+import org.kde.kirigami as Kirigami
+import org.kde.kirigamiaddons.formcard as FormCard
+
+import org.kde.chessament
+
+FormCard.AbstractFormDelegate {
+ id: delegate
+
+ required property int row
+ required property TimeControlModel timeControlModel
+ required property int count
+
+ required property int moves
+ required property int time
+ required property int increment
+
+ background: null
+
+ contentItem: RowLayout {
+ spacing: Kirigami.Units.smallSpacing
+
+ ColumnLayout {
+ enabled: delegate.row + 1 < delegate.count
+
+ Controls.Label {
+ text: KI18n.i18nc("@label:textbox Number of moves", "Moves")
+ }
+ Controls.SpinBox {
+ Layout.fillWidth: true
+ from: 0
+ to: 100
+ value: delegate.moves
+ onValueModified: delegate.moves = value
+ }
+ }
+
+ ColumnLayout {
+ Controls.Label {
+ text: KI18n.i18nc("@label:textbox Time control period time", "Time (minutes)")
+ }
+ Controls.SpinBox {
+ id: timeSpinBox
+ Layout.fillWidth: true
+ from: 1
+ to: 720
+ value: delegate.time / 60
+ onValueModified: delegate.time = value * 60
+ }
+ }
+
+ ColumnLayout {
+ Controls.Label {
+ text: KI18n.i18nc("@label:textbox Time control period increment (per second)", "Increment (seconds)")
+ }
+ Controls.SpinBox {
+ Layout.fillWidth: true
+ from: 0
+ to: 60
+ value: delegate.increment
+ onValueModified: delegate.increment = value
+ }
+ }
+
+ Controls.Button {
+ text: KI18n.i18nc("@action:button Delete time control period", "Delete")
+ icon.name: "list-remove-symbolic"
+ flat: true
+ display: Controls.Button.IconOnly
+ enabled: delegate.row > 0
+ onPressed: delegate.timeControlModel.deletePeriod(delegate.row)
+
+ Controls.ToolTip.text: text
+ Controls.ToolTip.visible: enabled && hovered
+ Controls.ToolTip.delay: Kirigami.Units.toolTipDelay
+ }
+ }
+}
diff --git a/src/timecontrolmodel.cpp b/src/timecontrolmodel.cpp
new file mode 100644
index 0000000..5e14753
--- /dev/null
+++ b/src/timecontrolmodel.cpp
@@ -0,0 +1,110 @@
+// SPDX-FileCopyrightText: 2026 Manuel Alcaraz Zambrano <[email protected]>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "timecontrolmodel.h"
+
+#include "tournament/tournament.h"
+
+TimeControlModel::TimeControlModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+}
+
+Tournament *TimeControlModel::tournament()
+{
+ return m_tournament;
+}
+
+void TimeControlModel::setTournament(Tournament *tournament)
+{
+ if (m_tournament == tournament) {
+ return;
+ }
+ m_tournament = tournament;
+ Q_EMIT tournamentChanged();
+}
+
+int TimeControlModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+
+ return static_cast<int>(m_tournament->timeControl().periods().size());
+}
+
+QVariant TimeControlModel::data(const QModelIndex &index, int role) const
+{
+ Q_ASSERT(checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid));
+
+ const auto &timeControl = m_tournament->timeControl();
+ const auto period = timeControl.periods()[index.row()];
+
+ switch (role) {
+ case TimeControlModel::Roles::Moves:
+ return period.moves().value_or(0);
+ case TimeControlModel::Roles::Time:
+ return period.time();
+ case TimeControlModel::Roles::Increment:
+ return period.increment();
+ }
+
+ return {};
+}
+
+bool TimeControlModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ Q_ASSERT(checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid));
+
+ auto &timeControl = m_tournament->timeControl();
+ const auto period = timeControl.periods()[index.row()];
+ TimeControlPeriod newPeriod;
+
+ switch (role) {
+ case TimeControlModel::Roles::Moves:
+ newPeriod = TimeControlPeriod{value.toInt(), period.time(), period.increment()};
+ break;
+ case TimeControlModel::Roles::Time:
+ newPeriod = TimeControlPeriod{period.moves(), value.toInt(), period.increment()};
+ break;
+ case TimeControlModel::Roles::Increment:
+ newPeriod = TimeControlPeriod{period.moves(), period.time(), value.toInt()};
+ break;
+ default:
+ return false;
+ }
+
+ timeControl.setPeriod(index.row(), newPeriod);
+ m_tournament->saveTimeControl();
+
+ Q_EMIT dataChanged(this->index(index.row()), this->index(index.row()));
+
+ return true;
+}
+
+QHash<int, QByteArray> TimeControlModel::roleNames() const
+{
+ return {
+ {TimeControlModel::Roles::Moves, "moves"},
+ {TimeControlModel::Roles::Time, "time"},
+ {TimeControlModel::Roles::Increment, "increment"},
+ };
+}
+
+void TimeControlModel::addPeriod()
+{
+ beginInsertRows({}, rowCount(), rowCount());
+ m_tournament->timeControl().addPeriod();
+ endInsertRows();
+
+ m_tournament->saveTimeControl();
+}
+
+void TimeControlModel::deletePeriod(int row)
+{
+ beginRemoveRows({}, row, row);
+ m_tournament->timeControl().removePeriod(row);
+ endRemoveRows();
+
+ m_tournament->saveTimeControl();
+}
+
+#include "moc_timecontrolmodel.cpp"
diff --git a/src/timecontrolmodel.h b/src/timecontrolmodel.h
new file mode 100644
index 0000000..48a389e
--- /dev/null
+++ b/src/timecontrolmodel.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: 2026 Manuel Alcaraz Zambrano <[email protected]>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <QAbstractListModel>
+#include <qqmlregistration.h>
+
+class Tournament;
+
+class TimeControlModel : public QAbstractListModel
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(Tournament *tournament READ tournament WRITE setTournament NOTIFY tournamentChanged)
+
+public:
+ enum Roles {
+ Moves,
+ Time,
+ Increment,
+ };
+ Q_ENUM(Roles);
+
+ explicit TimeControlModel(QObject *parent = nullptr);
+
+ Tournament *tournament();
+
+ [[nodiscard]] int rowCount(const QModelIndex &parent = {}) const override;
+ [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
+ [[nodiscard]] QHash<int, QByteArray> roleNames() const override;
+
+ Q_INVOKABLE void addPeriod();
+ Q_INVOKABLE void deletePeriod(int row);
+
+public Q_SLOTS:
+ void setTournament(Tournament *tournament);
+
+Q_SIGNALS:
+ void tournamentChanged();
+ void errorOcurred(const QString &error);
+
+private:
+ Tournament *m_tournament;
+};
diff --git a/src/tournament/CMakeLists.txt b/src/tournament/CMakeLists.txt
index 563d341..aefde47 100644
--- a/src/tournament/CMakeLists.txt
+++ b/src/tournament/CMakeLists.txt
@@ -12,6 +12,7 @@ target_sources(tournament PRIVATE
round.cpp
standing.cpp
state.cpp
+ timecontrol.cpp
tournament.cpp
utils.cpp
)
diff --git a/src/tournament/timecontrol.cpp b/src/tournament/timecontrol.cpp
new file mode 100644
index 0000000..be9c490
--- /dev/null
+++ b/src/tournament/timecontrol.cpp
@@ -0,0 +1,206 @@
+// SPDX-FileCopyrightText: 2026 Manuel Alcaraz Zambrano <[email protected]>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "timecontrol.h"
+#include "utils.h"
+
+#include <QJsonArray>
+
+using namespace Qt::StringLiterals;
+
+TimeControlPeriod::TimeControlPeriod(std::optional<int> moves, int time, int increment)
+ : m_moves(moves)
+ , m_time(time)
+ , m_increment(increment)
+{
+}
+
+std::optional<int> TimeControlPeriod::moves() const
+{
+ return m_moves;
+}
+
+int TimeControlPeriod::time() const
+{
+ return m_time;
+}
+
+int TimeControlPeriod::increment() const
+{
+ return m_increment;
+}
+
+QJsonObject TimeControlPeriod::toJson() const
+{
+ auto result = QJsonObject{{
+ {u"time"_s, m_time},
+ {u"increment"_s, m_increment},
+ }};
+
+ if (m_moves) {
+ result[u"moves"_s] = m_moves.value();
+ }
+
+ return result;
+}
+
+QString TimeControlPeriod::toTrf() const
+{
+ QString result;
+
+ if (m_moves) {
+ result += QString::number(m_moves.value()) % u'/';
+ }
+
+ result += QString::number(m_time);
+
+ if (m_increment != 0) {
+ result += u'+' % QString::number(m_increment);
+ }
+
+ return result;
+}
+
+TimeControlPeriod TimeControlPeriod::fromJson(QJsonObject json)
+{
+ TimeControlPeriod period{};
+
+ if (const auto value = json["moves"_L1]; value.isDouble()) {
+ period.m_moves = value.toInt();
+ }
+ if (const auto value = json["time"_L1]; value.isDouble()) {
+ period.m_time = value.toInt();
+ }
+ if (const auto value = json["increment"_L1]; value.isDouble()) {
+ period.m_increment = value.toInt();
+ }
+
+ return period;
+}
+
+TimeControl::TimeControl(std::initializer_list<TimeControlPeriod> periods)
+{
+ for (const auto &period : periods) {
+ addPeriod(period);
+ }
+}
+
+std::vector<TimeControlPeriod> TimeControl::periods() const
+{
+ auto value = m_json["periods"_L1];
+ if (!value.isArray()) {
+ return {};
+ }
+
+ std::vector<TimeControlPeriod> result;
+
+ for (const auto period : value.toArray()) {
+ result.push_back(TimeControlPeriod::fromJson(period.toObject()));
+ }
+
+ return result;
+}
+
+void TimeControl::addPeriod()
+{
+ auto value = m_json["periods"_L1];
+ QJsonArray periods;
+
+ if (value.isArray()) {
+ periods = value.toArray();
+ }
+
+ periods << TimeControlPeriod{}.toJson();
+ value = periods;
+}
+
+void TimeControl::addPeriod(const TimeControlPeriod &period)
+{
+ auto value = m_json["periods"_L1];
+ QJsonArray periods;
+
+ if (value.isArray()) {
+ periods = value.toArray();
+ }
+
+ periods << period.toJson();
+ value = periods;
+}
+
+void TimeControl::setPeriod(int index, const TimeControlPeriod &period)
+{
+ auto value = m_json["periods"_L1];
+ Q_ASSERT(value.isArray());
+
+ auto periods = value.toArray();
+ auto json = periods[index].toObject();
+ Utils::updateObject(&json, period.toJson());
+ periods[index] = json;
+
+ value = periods;
+}
+
+void TimeControl::removePeriod(int index)
+{
+ auto value = m_json["periods"_L1];
+ Q_ASSERT(value.isArray());
+
+ auto periods = value.toArray();
+ periods.erase(periods.begin() + index);
+
+ value = periods;
+}
+
+QJsonObject TimeControl::json()
+{
+ return m_json;
+}
+
+QString TimeControl::toTrf() const
+{
+ QStringList values;
+
+ for (const auto period : periods()) {
+ values << period.toTrf();
+ }
+
+ return values.join(u':');
+}
+
+bool TimeControl::operator==(const TimeControl &other) const
+{
+ return m_json == other.m_json;
+}
+
+TimeControl TimeControl::fromJson(QJsonObject json)
+{
+ TimeControl result{};
+ result.m_json = std::move(json);
+ return result;
+}
+
+TimeControl TimeControl::fromTrf(const QString &value)
+{
+ TimeControl timeControl{};
+
+ const auto periods = value.split(u':');
+
+ for (const auto &period : periods) {
+ static const QRegularExpression periodRegex{uR"(((\d+)\/)?(\d+)(\+(\d+))?)"_s};
+ const auto match = periodRegex.match(period);
+
+ bool ok{};
+ std::optional<int> moves{};
+ const auto m = match.captured(2).toInt(&ok);
+ if (ok) {
+ moves = m;
+ }
+
+ const auto time = match.captured(3).toInt();
+ const auto increment = match.captured(5).toInt();
+
+ timeControl.addPeriod(TimeControlPeriod{moves, time, increment});
+ }
+
+ return timeControl;
+}
diff --git a/src/tournament/timecontrol.h b/src/tournament/timecontrol.h
new file mode 100644
index 0000000..d034f8e
--- /dev/null
+++ b/src/tournament/timecontrol.h
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: 2026 Manuel Alcaraz Zambrano <[email protected]>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <QJsonObject>
+
+#include <optional>
+#include <vector>
+
+struct TimeControlPeriod {
+ explicit TimeControlPeriod() = default;
+
+ explicit TimeControlPeriod(std::optional<int> moves, int time, int increment);
+
+ [[nodiscard]] std::optional<int> moves() const;
+
+ [[nodiscard]] int time() const;
+
+ [[nodiscard]] int increment() const;
+
+ [[nodiscard]] QJsonObject toJson() const;
+
+ [[nodiscard]] QString toTrf() const;
+
+ static TimeControlPeriod fromJson(QJsonObject json);
+
+private:
+ std::optional<int> m_moves{std::nullopt};
+ int m_time{};
+ int m_increment{};
+};
+
+struct TimeControl {
+ explicit TimeControl() = default;
+
+ explicit TimeControl(std::initializer_list<TimeControlPeriod> periods);
+
+ [[nodiscard]] std::vector<TimeControlPeriod> periods() const;
+
+ void addPeriod();
+
+ void addPeriod(const TimeControlPeriod &period);
+
+ void setPeriod(int index, const TimeControlPeriod &period);
+
+ void removePeriod(int index);
+
+ QJsonObject json();
+
+ [[nodiscard]] QString toTrf() const;
+
+ bool operator==(const TimeControl &other) const;
+
+ static TimeControl fromJson(QJsonObject json);
+
+ static TimeControl fromTrf(const QString &value);
+
+private:
+ QJsonObject m_json;
+};
diff --git a/src/tournament/tournament.cpp b/src/tournament/tournament.cpp
index 1d970ae..08bb980 100644
--- a/src/tournament/tournament.cpp
+++ b/src/tournament/tournament.cpp
@@ -20,12 +20,14 @@
#include "tiebreaks/playedblack.h"
#include "tiebreaks/points.h"
#include "tiebreaks/won.h"
+#include "timecontrol.h"
#include "trf/reader.h"
#include "trf/writer.h"
#include "utils.h"
Tournament::Tournament(Event *event)
: m_event(event)
+ , m_timeControl({TimeControlPeriod{std::nullopt, 5400, 30}})
{
m_tiebreaks.push_back(std::make_unique<Points>());
}
@@ -108,19 +110,18 @@ void Tournament::saveArbiters()
setOption("arbiters"_L1, text);
}
-QString Tournament::timeControl() const
+TimeControl &Tournament::timeControl()
{
return m_timeControl;
}
-void Tournament::setTimeControl(const QString &timeControl)
+void Tournament::saveTimeControl()
{
- if (m_timeControl == timeControl) {
- return;
- }
- m_timeControl = timeControl;
- setOption(u"time_control"_s, timeControl);
- Q_EMIT timeControlChanged();
+ const auto text = QJsonDocument{m_timeControl.json()}.toJson(QJsonDocument::JsonFormat::Compact);
+
+ qDebug() << m_timeControl.json();
+
+ setOption(u"time_control"_s, text);
}
std::vector<std::unique_ptr<Tiebreak>> &Tournament::tiebreaks()
@@ -1125,7 +1126,6 @@ QJsonObject Tournament::toJson() const
tournament["slug"_L1] = Utils::normalize(m_name.toLower()).replace(" "_L1, "-"_L1);
tournament["federation"_L1] = m_federation;
tournament["city"_L1] = m_city;
- tournament["time_control"_L1] = m_timeControl;
tournament["number_rounds"_L1] = m_numberOfRounds;
QJsonArray players;
@@ -1160,9 +1160,6 @@ void Tournament::read(const QJsonObject &json)
if (const auto v = tournament[QStringLiteral("federation")]; v.isString()) {
m_federation = v.toString();
}
- if (const auto v = tournament[QStringLiteral("time_control")]; v.isString()) {
- m_timeControl = v.toString();
- }
if (const auto v = tournament[QStringLiteral("number_of_rounds")]; v.isDouble()) {
m_numberOfRounds = v.toInt();
}
@@ -1292,7 +1289,6 @@ std::expected<void, QString> Tournament::loadOptions()
setName(option(u"name"_s).toString());
setCity(option(u"city"_s).toString());
setFederation(option(u"federation"_s).toString());
- setTimeControl(option(u"time_control"_s).toString());
setNumberOfRounds(option(u"number_of_rounds"_s).toInt());
setCurrentRound(option(u"current_round"_s).toInt());
setInitialColor(Tournament::InitialColor(option(u"initial_color"_s).toInt()));
diff --git a/src/tournament/tournament.h b/src/tournament/tournament.h
index 5c0d39e..7572819 100644
--- a/src/tournament/tournament.h
+++ b/src/tournament/tournament.h
@@ -22,6 +22,7 @@
#include "round.h"
#include "standing.h"
#include "tiebreaks/tiebreak.h"
+#include "timecontrol.h"
#include "trf/trf.h"
class Document;
@@ -44,7 +45,6 @@ class Tournament : public QObject
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString city READ city WRITE setCity NOTIFY cityChanged)
Q_PROPERTY(QString federation READ federation WRITE setFederation NOTIFY federationChanged)
- Q_PROPERTY(QString timeControl READ timeControl WRITE setTimeControl NOTIFY timeControlChanged)
Q_PROPERTY(int numberOfPlayers READ numberOfPlayers NOTIFY numberOfPlayersChanged)
Q_PROPERTY(int numberOfRatedPlayers READ numberOfRatedPlayers NOTIFY numberOfRatedPlayersChanged)
@@ -88,13 +88,9 @@ public:
void saveArbiters();
- /*!
- * \property Tournament::timeControl
- * \brief the time control of the tournament
- *
- * This property holds the time control of the tournament.
- */
- [[nodiscard]] QString timeControl() const;
+ TimeControl &timeControl();
+
+ void saveTimeControl();
/*!
* \property Tournament::tiebreaks
@@ -385,7 +381,6 @@ public Q_SLOTS:
void setName(const QString &name);
void setCity(const QString &city);
void setFederation(const QString &federation);
- void setTimeControl(const QString &timeControl);
void setNumberOfRounds(int numberOfRounds);
void setCurrentRound(int currentRound);
@@ -396,7 +391,6 @@ Q_SIGNALS:
void nameChanged();
void cityChanged();
void federationChanged();
- void timeControlChanged();
void tiebreaksChanged();
void numberOfPlayersChanged();
@@ -423,7 +417,7 @@ private:
QString m_city;
QString m_federation;
std::vector<std::unique_ptr<Arbiter>> m_arbiters;
- QString m_timeControl;
+ TimeControl m_timeControl;
int m_numberOfRounds = 1;
int m_currentRound = 0;
QVariantMap m_options;
diff --git a/src/tournament/trf/reader.cpp b/src/tournament/trf/reader.cpp
index f706638..c96c5a8 100644
--- a/src/tournament/trf/reader.cpp
+++ b/src/tournament/trf/reader.cpp
@@ -132,7 +132,7 @@ std::expected<void, QString> TrfReader::readField(QStringView line)
break;
}
case Trf::Field::TimeControl:
- m_tournament->setTimeControl(value.trimmed().toString());
+ m_tournament->timeControl() = TimeControl::fromTrf(value.trimmed().toString());
break;
case Trf::Field::Calendar: {
if (const auto ok = readDates(value); !ok) {
diff --git a/src/tournament/trf/trf.cpp b/src/tournament/trf/trf.cpp
index 7ae4edf..0393f54 100644
--- a/src/tournament/trf/trf.cpp
+++ b/src/tournament/trf/trf.cpp
@@ -35,7 +35,7 @@ QString reportFieldString(Trf::Field field)
return QStringLiteral("102");
case Trf::Field::DeputyChiefArbiter:
return QStringLiteral("112");
- case Trf::Field::TimeControl:
+ case Trf::Field::TimeControlDescription:
return QStringLiteral("122");
case Trf::Field::Calendar:
return QStringLiteral("132");
@@ -47,6 +47,8 @@ QString reportFieldString(Trf::Field field)
return "182"_L1;
case Trf::Field::Tiebreaks:
return "212"_L1;
+ case Trf::Field::TimeControl:
+ return "222"_L1;
case Trf::Field::Unknown:
return {};
}
@@ -80,13 +82,15 @@ Trf::Field reportFieldForString(QStringView number)
} else if (number == QStringLiteral("112")) {
return Trf::Field::DeputyChiefArbiter;
} else if (number == QStringLiteral("122")) {
- return Trf::Field::TimeControl;
+ return Trf::Field::TimeControlDescription;
} else if (number == QStringLiteral("132")) {
return Trf::Field::Calendar;
} else if (number == "142"_L1) {
return Trf::Field::NumberOfRounds;
} else if (number == "212"_L1) {
return Trf::Field::Tiebreaks;
+ } else if (number == "222"_L1) {
+ return Trf::Field::TimeControl;
}
return Trf::Field::Unknown;
}
diff --git a/src/tournament/trf/trf.h b/src/tournament/trf/trf.h
index 1aef5bf..82f179e 100644
--- a/src/tournament/trf/trf.h
+++ b/src/tournament/trf/trf.h
@@ -24,12 +24,13 @@ enum class Field {
TournamentType,
ChiefArbiter,
DeputyChiefArbiter,
- TimeControl,
+ TimeControlDescription,
Calendar,
NumberOfRounds,
InitialColor,
ProgramName,
Tiebreaks,
+ TimeControl,
Unknown,
};
Q_ENUM_NS(Field)
diff --git a/src/tournament/trf/writer.cpp b/src/tournament/trf/writer.cpp
index e5396c1..2ce3664 100644
--- a/src/tournament/trf/writer.cpp
+++ b/src/tournament/trf/writer.cpp
@@ -35,11 +35,14 @@ void TrfWriter::writeTournamentInformation(QTextStream &stream)
stream << Trf::reportFieldString(Trf::Field::NumberOfPlayers) << space << m_tournament->numberOfPlayers() << newLine;
stream << Trf::reportFieldString(Trf::Field::NumberOfRatedPlayers) << space << m_tournament->numberOfRatedPlayers() << newLine;
writeArbiters(stream);
- stream << Trf::reportFieldString(Trf::Field::TimeControl) << space << m_tournament->timeControl() << newLine;
stream << Trf::reportFieldString(Trf::Field::ProgramName) << space << "Chessament %1"_L1.arg(QCoreApplication::applicationVersion()) << newLine;
stream << Trf::reportFieldString(Trf::Field::NumberOfRounds) << space << m_tournament->numberOfRounds() << newLine;
writeTiebreaks(stream);
+ if (const auto timeControl = m_tournament->timeControl().toTrf(); !timeControl.isEmpty()) {
+ stream << Trf::reportFieldString(Trf::Field::TimeControl) << space << timeControl << newLine;
+ }
+
stream << Trf::reportFieldString(Trf::Field::Calendar) << QString(space).repeated(86);
for (int i = 0; i < m_state.lastRound(); ++i) {
QString date;
diff --git a/src/tournament/utils.cpp b/src/tournament/utils.cpp
index eaa166b..4f509ad 100644
--- a/src/tournament/utils.cpp
+++ b/src/tournament/utils.cpp
@@ -4,6 +4,7 @@
#include "utils.h"
#include <QCoreApplication>
+#include <QJsonObject>
#include <QRegularExpression>
using namespace Qt::StringLiterals;
@@ -30,4 +31,11 @@ QString userAgent()
static QString userAgent = u"Chessament/"_s % QCoreApplication::applicationVersion() % u" (+https://apps.kde.org/chessament/;)"_s;
return userAgent;
}
+
+void updateObject(QJsonObject *destination, const QJsonObject &origin)
+{
+ for (auto [key, value] : origin.asKeyValueRange()) {
+ (*destination)[key.toString()] = value;
+ }
+}
}
diff --git a/src/tournament/utils.h b/src/tournament/utils.h
index f949b93..eb73a7a 100644
--- a/src/tournament/utils.h
+++ b/src/tournament/utils.h
@@ -14,4 +14,6 @@ QString normalize(const QString &text);
QUrl maybeAddExtension(const QUrl &fileUrl, const QString &extension);
QString userAgent();
+
+void updateObject(QJsonObject *destination, const QJsonObject &origin);
}