diff --git a/code/datadefiners.h b/code/datadefiners.h
new file mode 100644
index 0000000..af5c52a
--- /dev/null
+++ b/code/datadefiners.h
@@ -0,0 +1,377 @@
+#pragma once
+
+/************************************************************************
+ MeOS - Orienteering Software
+ Copyright (C) 2009-2024 Melin Software HB
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+ Melin Software HB - software@melin.nu - www.melin.nu
+ Eksoppsvägen 16, SE-75646 UPPSALA, Sweden
+
+************************************************************************/
+
+#include "meos_util.h"
+#include "oDataContainer.h"
+
+#include "oBase.h"
+#include "Table.h""
+
+class RelativeTimeFormatter : public oDataDefiner {
+ string name;
+public:
+ RelativeTimeFormatter(const char* n) : name(n) {}
+
+ const wstring& formatData(const oBase* obj) const override {
+ int t = obj->getDCI().getInt(name);
+ if (t <= 0)
+ return makeDash(L"-");
+ return obj->getEvent()->getAbsTime(t);
+ }
+ pair setData(oBase* obj, const wstring& input, wstring& output, int inputId) const override {
+ int t = obj->getEvent()->getRelativeTime(input);
+ obj->getDI().setInt(name.c_str(), t);
+ output = formatData(obj);
+ return make_pair(0, false);
+ }
+ int addTableColumn(Table* table, const string& description, int minWidth) const override {
+ return table->addColumn(description, max(minWidth, 90), false, true);
+ }
+};
+
+class AbsoluteTimeFormatter : public oDataDefiner {
+ string name;
+public:
+ AbsoluteTimeFormatter(const char* n) : name(n) {}
+
+ const wstring& formatData(const oBase* obj) const override {
+ int t = obj->getDCI().getInt(name);
+ return formatTime(t);
+ }
+ pair setData(oBase* obj, const wstring& input, wstring& output, int inputId) const override {
+ int t = convertAbsoluteTimeMS(input);
+ if (t == NOTIME)
+ t = 0;
+ obj->getDI().setInt(name.c_str(), t);
+ output = formatData(obj);
+ return make_pair(0, false);
+ }
+ int addTableColumn(Table* table, const string& description, int minWidth) const override {
+ return table->addColumn(description, max(minWidth, 90), false, true);
+ }
+};
+
+class PayMethodFormatter : public oDataDefiner {
+ mutable vector< pair > modes;
+ mutable map setCodes;
+ mutable long rev;
+public:
+ PayMethodFormatter() : rev(-1) {}
+
+ void prepare(oEvent* oe) const override {
+ oe->getPayModes(modes);
+ for (size_t i = 0; i < modes.size(); i++) {
+ setCodes[canonizeName(modes[i].first.c_str())] = modes[i].second;
+ }
+ }
+
+ const wstring& formatData(const oBase* ob) const override {
+ if (ob->getEvent()->getRevision() != rev)
+ prepare(ob->getEvent());
+ int p = ob->getDCI().getInt("Paid");
+ if (p == 0)
+ return lang.tl("Faktura");
+ else {
+ int pm = ob->getDCI().getInt("PayMode");
+ for (size_t i = 0; i < modes.size(); i++) {
+ if (modes[i].second == pm)
+ return modes[i].first;
+ }
+ return _EmptyWString;
+ }
+ }
+
+ pair setData(oBase* ob, const wstring& input, wstring& output, int inputId) const override {
+ auto res = setCodes.find(canonizeName(input.c_str()));
+ if (res != setCodes.end()) {
+ ob->getDI().setInt("PayMode", res->second);
+ }
+ output = formatData(ob);
+ return make_pair(0, false);
+ }
+
+ int addTableColumn(Table* table, const string& description, int minWidth) const override {
+ return table->addColumn(description, max(minWidth, 90), true, true);
+ }
+};
+
+class StartGroupFormatter : public oDataDefiner {
+ mutable long rev = -1;
+ mutable map sgmap;
+ mutable wstring out;
+
+ int static getGroup(const oBase* ob) {
+ const oRunner* r = dynamic_cast(ob);
+ int sg = 0;
+ if (r)
+ sg = r->getStartGroup(false);
+ else {
+ const oClub* c = dynamic_cast(ob);
+ if (c)
+ sg = c->getStartGroup();
+ }
+ return sg;
+ }
+
+public:
+ StartGroupFormatter() {}
+
+ void prepare(oEvent* oe) const override {
+ auto& sg = oe->getStartGroups(true);
+ for (auto& g : sg) {
+ int t = g.second.firstStart;
+ sgmap[g.first] = oe->getAbsTimeHM(t);
+ }
+ }
+
+ const wstring& formatData(const oBase* ob) const override {
+ if (ob->getEvent()->getRevision() != rev)
+ prepare(ob->getEvent());
+ int sg = getGroup(ob);
+ if (sg > 0) {
+ auto res = sgmap.find(sg);
+ if (res != sgmap.end())
+ out = itow(sg) + L" (" + res->second + L")";
+ else
+ out = itow(sg) + L" (??)";
+
+ return out;
+ }
+ else
+ return _EmptyWString;
+ }
+
+ pair setData(oBase* ob, const wstring& input, wstring& output, int inputId) const override {
+ int g = inputId;
+ if (inputId <= 0 && !input.empty()) {
+ vector sIn;
+ split(input, L" ", sIn);
+ for (wstring& in : sIn) {
+ int num = _wtoi(in.c_str());
+ if (in.find_first_of(':') != input.npos) {
+ int t = ob->getEvent()->convertAbsoluteTime(input);
+ if (t > 0) {
+ for (auto& sg : ob->getEvent()->getStartGroups(false)) {
+ if (sg.second.firstStart == t) {
+ g = sg.first;
+ break;
+ }
+ }
+ }
+ }
+ else if (sgmap.count(num)) {
+ g = num;
+ break;
+ }
+ }
+ }
+ oRunner* r = dynamic_cast(ob);
+ if (r) {
+ r->setStartGroup(g);
+ }
+ else {
+ oClub* c = dynamic_cast(ob);
+ if (c)
+ c->setStartGroup(g);
+ }
+ output = formatData(ob);
+ return make_pair(0, false);
+ }
+
+ int addTableColumn(Table* table, const string& description, int minWidth) const override {
+ return table->addColumn(description, max(minWidth, 90), true, false);
+ }
+
+ // Return the desired cell type
+ CellType getCellType() const {
+ return CellType::cellSelection;
+ }
+
+ void fillInput(const oBase* obj, vector>& out, size_t& selected) const final {
+ if (obj->getEvent()->getRevision() != rev)
+ prepare(obj->getEvent());
+
+ int sg = getGroup(obj);
+
+ out.emplace_back(_EmptyWString, 0);
+ selected = 0;
+ for (auto& v : sgmap) {
+ out.emplace_back(v.second, v.first);
+
+ if (sg == v.first)
+ selected = sg;
+ }
+ }
+};
+
+
+class DataHider : public oDataDefiner {
+public:
+
+ const wstring& formatData(const oBase* obj) const override {
+ return _EmptyWString;
+ }
+ pair setData(oBase* obj, const wstring& input, wstring& output, int inputId) const override {
+ return make_pair(0, false);
+ }
+ int addTableColumn(Table* table, const string& description, int minWidth) const override {
+ return -1;
+ }
+};
+
+class DataBoolean : public oDataDefiner {
+ string attrib;
+public:
+ DataBoolean(const string& attrib) : attrib(attrib) {}
+
+ const wstring& formatData(const oBase* obj) const override {
+ int v = obj->getDCI().getInt(attrib);
+ return lang.tl(v ? "true[boolean]" : "false[boolean]");
+ }
+ pair setData(oBase* obj, const wstring& input, wstring& output, int inputId) const override {
+ bool v = compareStringIgnoreCase(L"true", input) == 0 || _wtoi64(input.c_str()) > 0;
+ if (!v) {
+ const wstring& T = lang.tl("true[boolean]");
+ v = compareStringIgnoreCase(T, input) == 0;
+ }
+ obj->getDI().setInt(attrib.c_str(), v);
+ output = formatData(obj);
+ return make_pair(0, false);
+ }
+ int addTableColumn(Table* table, const string& description, int minWidth) const override {
+ return table->addColumn(description, max(minWidth, 90), true, true);
+ }
+};
+
+class ResultModuleFormatter : public oDataDefiner {
+public:
+
+ const wstring& formatData(const oBase* obj) const override {
+ return obj->getDCI().getString("Result");
+ }
+ pair setData(oBase* obj, const wstring& input, wstring& output, int inputId) const override {
+ string tag(input.begin(), input.end());
+ dynamic_cast(*obj).setResultModule(tag);
+ output = formatData(obj);
+ return make_pair(0, false);
+ }
+ int addTableColumn(Table* table, const string& description, int minWidth) const override {
+ return table->addColumn(description, max(minWidth, 90), false, true);
+ }
+};
+
+class SplitPrintListFormatter : public oDataDefiner {
+public:
+
+ const wstring& formatData(const oBase* obj) const override {
+ wstring listId = obj->getDCI().getString("SplitPrint");
+ if (listId.empty()) {
+ return lang.tl("Standard");
+ }
+ try {
+ const MetaListContainer& lc = obj->getEvent()->getListContainer();
+ EStdListType type = lc.getCodeFromUnqiueId(gdioutput::narrow(listId));
+ const MetaList& ml = lc.getList(type);
+ return ml.getListName();
+ }
+ catch (meosException&) {
+ return _EmptyWString;
+ }
+ }
+
+ void fillInput(const oBase* obj, vector>& out, size_t& selected) const {
+ oEvent* oe = obj->getEvent();
+ oe->getListContainer().getLists(out, false, false, false, true);
+ out.insert(out.begin(), make_pair(lang.tl("Standard"), -10));
+ wstring listId = obj->getDCI().getString("SplitPrint");
+ EStdListType type = oe->getListContainer().getCodeFromUnqiueId(gdioutput::narrow(listId));
+ if (type == EStdListType::EStdNone)
+ selected = -10;
+ else {
+ for (auto& t : out) {
+ if (type == oe->getListContainer().getType(t.second)) {
+ selected = t.second;
+ break;
+ }
+ }
+ }
+ }
+
+ CellType getCellType() const final {
+ return CellType::cellSelection;
+ }
+
+ pair setData(oBase* obj, const wstring& input, wstring& output, int inputId) const override {
+ if (inputId == -10)
+ obj->getDI().setString("SplitPrint", L"");
+ else {
+ EStdListType type = obj->getEvent()->getListContainer().getType(inputId);
+ string id = obj->getEvent()->getListContainer().getUniqueId(type);
+ obj->getDI().setString("SplitPrint", gdioutput::widen(id));
+ }
+
+ output = formatData(obj);
+ return make_pair(0, false);
+ }
+
+ int addTableColumn(Table* table, const string& description, int minWidth) const override {
+ oEvent* oe = table->getEvent();
+ vector> out;
+ oe->getListContainer().getLists(out, false, false, false, true);
+
+ for (auto& t : out) {
+ minWidth = max(minWidth, t.first.size() * 6);
+ }
+
+ return table->addColumn(description, max(minWidth, 90), false, true);
+ }
+};
+
+class AnnotationFormatter : public oDataDefiner {
+ mutable int numChar = 12;
+public:
+
+ const wstring& formatData(const oBase* obj) const override {
+ const wstring& ws = obj->getDCI().getString("Annotation");
+ if (ws.empty())
+ return ws;
+ int pos = ws.find_first_of('@');
+ if (pos != wstring::npos)
+ return limitText(ws.substr(pos + 1), numChar);
+ return limitText(ws, numChar);
+ }
+
+ bool canEdit() const override {
+ return false;
+ }
+
+ pair setData(oBase* obj, const wstring& input, wstring& output, int inputId) const override {
+ return make_pair(0, false);
+ }
+
+ int addTableColumn(Table* table, const string& description, int minWidth) const override {
+ numChar = minWidth / 5;
+ return table->addColumn(description, max(minWidth, 90), false, true);
+ }
+};