diff --git a/code/HTMLWriter.cpp b/code/HTMLWriter.cpp index 02338e1..db326a1 100644 --- a/code/HTMLWriter.cpp +++ b/code/HTMLWriter.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/HTMLWriter.h b/code/HTMLWriter.h index e85ffdd..eaecad5 100644 --- a/code/HTMLWriter.h +++ b/code/HTMLWriter.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/MeOSFeatures.cpp b/code/MeOSFeatures.cpp index 47b0747..9a5d4f1 100644 --- a/code/MeOSFeatures.cpp +++ b/code/MeOSFeatures.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/MeOSFeatures.h b/code/MeOSFeatures.h index 9f92607..fa52890 100644 --- a/code/MeOSFeatures.h +++ b/code/MeOSFeatures.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/Printer.h b/code/Printer.h index 896e768..a52d506 100644 --- a/code/Printer.h +++ b/code/Printer.h @@ -4,7 +4,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/RestService.cpp b/code/RestService.cpp index c776932..80dd277 100644 --- a/code/RestService.cpp +++ b/code/RestService.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/RestService.h b/code/RestService.h index af987f2..6f6d6c7 100644 --- a/code/RestService.h +++ b/code/RestService.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software -Copyright (C) 2009-2019 Melin Software HB +Copyright (C) 2009-2020 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 diff --git a/code/RunnerDB.cpp b/code/RunnerDB.cpp index 6e3890b..224318f 100644 --- a/code/RunnerDB.cpp +++ b/code/RunnerDB.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -1194,19 +1194,13 @@ void RunnerDB::refreshTables() { } void RunnerDB::releaseTables() { - if (runnerTable) - runnerTable->releaseOwnership(); - runnerTable = 0; - - if (clubTable) - clubTable->releaseOwnership(); - clubTable = 0; + runnerTable.reset(); + clubTable.reset(); } -Table *RunnerDB::getRunnerTB()//Table mode -{ - if (runnerTable == 0) { - Table *table=new Table(oe, 20, L"Löpardatabasen", "runnerdb"); +const shared_ptr &RunnerDB::getRunnerTB() { + if (!runnerTable) { + auto table = make_shared
(oe, 20, L"Löpardatabasen", "runnerdb"); table->addColumn("Index", 70, true, true); table->addColumn("Id", 70, true, true); @@ -1220,7 +1214,6 @@ Table *RunnerDB::getRunnerTB()//Table mode table->setTableProp(Table::CAN_INSERT|Table::CAN_DELETE|Table::CAN_PASTE); table->setClearOnHide(false); - table->addOwnership(); runnerTable = table; } int nr = 0; @@ -1284,18 +1277,17 @@ void RunnerDB::refreshRunnerTableData(Table &table) { } } -Table *RunnerDB::getClubTB()//Table mode -{ +const shared_ptr
&RunnerDB::getClubTB() { bool canEdit = !oe->isClient(); - if (clubTable == 0) { - Table *table = new Table(oe, 20, L"Klubbdatabasen", "clubdb"); + if (!clubTable) { + auto table = make_shared
(oe, 20, L"Klubbdatabasen", "clubdb"); table->addColumn("Id", 70, true, true); table->addColumn("Ändrad", 70, false); table->addColumn("Namn", 200, false); - oClub::buildTableCol(oe, table); + oClub::buildTableCol(oe, table.get()); if (canEdit) table->setTableProp(Table::CAN_DELETE|Table::CAN_INSERT|Table::CAN_PASTE); @@ -1303,7 +1295,6 @@ Table *RunnerDB::getClubTB()//Table mode table->setTableProp(0); table->setClearOnHide(false); - table->addOwnership(); clubTable = table; } @@ -1318,7 +1309,6 @@ Table *RunnerDB::getClubTB()//Table mode return clubTable; } - void oDBRunnerEntry::addTableRow(Table &table) const { bool canEdit = !oe->isClient(); @@ -1377,8 +1367,8 @@ const RunnerDBEntry &oDBRunnerEntry::getRunner() const { return db->rdb[index]; } -bool oDBRunnerEntry::inputData(int id, const wstring &input, - int inputId, wstring &output, bool noUpdate) +pair oDBRunnerEntry::inputData(int id, const wstring &input, + int inputId, wstring &output, bool noUpdate) { if (!db) throw meosException("Not initialized"); @@ -1392,7 +1382,7 @@ bool oDBRunnerEntry::inputData(int id, const wstring &input, db->nhash.clear(); db->runnerHash.clear(); db->runnerHashByClub.clear(); - return true; + break; case TID_CARD: db->rhash.remove(rd.cardNo); rd.cardNo = _wtoi(input.c_str()); @@ -1401,7 +1391,7 @@ bool oDBRunnerEntry::inputData(int id, const wstring &input, output = itow(rd.cardNo); else output = L""; - return true; + break; case TID_NATIONAL: if (input.empty()) { rd.national[0] = 0; @@ -1429,7 +1419,7 @@ bool oDBRunnerEntry::inputData(int id, const wstring &input, output = input; break; } - return false; + return make_pair(0, false); } void oDBRunnerEntry::fillInput(int id, vector< pair > &out, size_t &selected) diff --git a/code/RunnerDB.h b/code/RunnerDB.h index c6fcc4a..ec6d8bd 100644 --- a/code/RunnerDB.h +++ b/code/RunnerDB.h @@ -11,7 +11,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -161,8 +161,8 @@ class RunnerDB { private: oEvent *oe; - Table *runnerTable; - Table *clubTable; + shared_ptr
runnerTable; + shared_ptr
clubTable; static int cellEntryIndex; @@ -286,8 +286,8 @@ public: void refreshClubTableData(Table &table); void refreshTables(); - Table *getRunnerTB(); - Table *getClubTB(); + const shared_ptr
&getRunnerTB(); + const shared_ptr
&getClubTB(); void hasEnteredCompetition(__int64 extId); @@ -368,9 +368,9 @@ public: const RunnerDBEntry &getRunner() const; void addTableRow(Table &table) const; - bool inputData(int id, const wstring &input, - int inputId, wstring &output, bool noUpdate); - void fillInput(int id, vector< pair > &out, size_t &selected); + pair inputData(int id, const wstring &input, + int inputId, wstring &output, bool noUpdate) override; + void fillInput(int id, vector< pair > &out, size_t &selected) override; oDBRunnerEntry(oEvent *oe); virtual ~oDBRunnerEntry(); diff --git a/code/SportIdent.cpp b/code/SportIdent.cpp index 0e31efc..20d4d3c 100644 --- a/code/SportIdent.cpp +++ b/code/SportIdent.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/SportIdent.h b/code/SportIdent.h index fd0369d..2ec6571 100644 --- a/code/SportIdent.h +++ b/code/SportIdent.h @@ -13,7 +13,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/TabAuto.cpp b/code/TabAuto.cpp index 508f56f..c276fe3 100644 --- a/code/TabAuto.cpp +++ b/code/TabAuto.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -291,7 +291,7 @@ int TabAuto::processButton(gdioutput &gdi, const ButtonInfo &bu) gdi.setInputStatus("ExportScript", stat); gdi.setInputStatus("BrowseFile", stat); gdi.setInputStatus("BrowseScript", stat); - if (gdi.hasField("HTMLRefresh")) { + if (gdi.hasWidget("HTMLRefresh")) { gdi.setInputStatus("HTMLRefresh", stat); gdi.setInputStatus("StructuredExport", stat); } diff --git a/code/TabAuto.h b/code/TabAuto.h index d6098bd..71c968e 100644 --- a/code/TabAuto.h +++ b/code/TabAuto.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/TabBase.cpp b/code/TabBase.cpp index 0ae479a..a5d376e 100644 --- a/code/TabBase.cpp +++ b/code/TabBase.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/TabBase.h b/code/TabBase.h index 887ddcf..8dce07c 100644 --- a/code/TabBase.h +++ b/code/TabBase.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/TabClass.cpp b/code/TabClass.cpp index 0243516..2f40c2b 100644 --- a/code/TabClass.cpp +++ b/code/TabClass.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -30,6 +30,7 @@ #include #include "oEvent.h" +#include "metalist.h" #include "xmlparser.h" #include "gdioutput.h" #include "csvparser.h" @@ -37,12 +38,15 @@ #include "meos_util.h" #include "oListInfo.h" #include "TabClass.h" +#include "TabList.h" +#include "methodeditor.h" #include "ClassConfigInfo.h" #include "meosException.h" #include "gdifonts.h" #include "oEventDraw.h" #include "MeOSFeatures.h" #include "qualification_final.h" +#include "generalresult.h" extern pEvent gEvent; const char *visualDrawWindow = "visualdraw"; @@ -70,6 +74,7 @@ TabClass::TabClass(oEvent *poe):TabBase(poe) } void TabClass::clearCompetitionData() { + currentResultModuleTags.clear(); pSettings.clear(); pSavedDepth = 3600; pFirstRestart = 3600; @@ -460,15 +465,15 @@ int TabClass::multiCB(gdioutput &gdi, int type, void *data) save(gdi, false); //Clears and reloads - if (gdi.hasField("Courses")) { + if (gdi.hasWidget("Courses")) { gdi.selectItemByData("Courses", -2); gdi.disableInput("Courses"); } oe->setupRelay(*pc, newType, nstages, st); - if (gdi.hasField("MAdd")) { + if (gdi.hasWidget("MAdd")) { for (const char *s : {"MCourses", "StageCourses", "MAdd", "MRemove", "MUp", "MDown"}) { - if (gdi.hasField(s)) { + if (gdi.hasWidget(s)) { gdi.enableInput(s); } } @@ -481,7 +486,7 @@ int TabClass::multiCB(gdioutput &gdi, int type, void *data) pc->synchronize(); gdi.restore(); gdi.enableInput("MultiCourse", true); - if (gdi.hasField("Courses")) + if (gdi.hasWidget("Courses")) gdi.enableInput("Courses"); oe->adjustTeamMultiRunners(pc); } @@ -705,6 +710,18 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) tableMode=!tableMode; loadPage(gdi); } + else if (bi.id == "EditModule") { + save(gdi, true); + + size_t ix = gdi.getSelectedItem("Module").first; + if (ix < currentResultModuleTags.size()) { + TabList &tc = dynamic_cast(*gdi.getTabs().get(TListTab)); + tc.getMethodEditor().show(this, gdi); + const string &mtag = currentResultModuleTags[ix]; + tc.getMethodEditor().load(gdi, mtag, false); + gdi.refresh(); + } + } else if (bi.id=="Restart") { save(gdi, true); clearPage(gdi, true); @@ -842,56 +859,8 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) } DrawSettingsCSV::write(gdi, *oe, fn, res); - - /*csvparser writer; - writer.openOutput(fn.c_str()); - vector header; - - writer.outputRow(header); - header.emplace_back("ClassId"); - header.emplace_back("Class"); - header.emplace_back("Competitors"); - header.emplace_back("Course"); - header.emplace_back("First Control"); - - header.emplace_back("First Start"); - header.emplace_back("Interval"); - header.emplace_back("Vacant"); - - writer.outputRow(header); - vector line; - - for (size_t k = 0; kgetId())); - line.push_back(gdi.toUTF8(ci.pc->getName())); - line.push_back(itos(ci.pc->getNumRunners(1, false, false))); - pCourse crs = ci.pc->getCourse(); - pControl ctrl = nullptr; - if (crs) { - line.push_back(gdi.toUTF8(crs->getName())); - ctrl = crs->getControl(0); - } - else line.emplace_back(""); - - if (ctrl) { - line.push_back(itos(ctrl->getId())); - } - else line.emplace_back(""); - - // Save settings with class - - line.push_back(gdi.narrow(oe->getAbsTime(drawInfo.firstStart + drawInfo.baseInterval * ci.firstStart))); - line.push_back(gdi.narrow(formatTime(ci.interval * drawInfo.baseInterval))); - line.push_back(itos(ci.nVacant)); - writer.outputRow(line); - } - }*/ } else if (bi.id == "ImportDrawSettings") { - wstring fn = gdi.browseForOpen({ make_pair(lang.tl("Kalkylblad/csv"), L"*.csv") }, L"csv"); if (fn.empty()) @@ -1030,7 +999,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) prepareForDrawing(gdi); } else if (bi.id == "DrawMode") { - if (gdi.hasField("Name")) + if (gdi.hasWidget("Name")) save(gdi, false); ClassId = 0; @@ -1120,7 +1089,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) oListParam par; for (int k = 0; k < oe->getNumClasses(); k++) { - if (!gdi.hasField("PLUse" + itos(k))) + if (!gdi.hasWidget("PLUse" + itos(k))) continue; BaseInfo *biu = gdi.setText("PLUse" + itos(k), L"", false); if (biu) { @@ -1188,7 +1157,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) oe->setProperty("DrawInterlace", allowNeighbourSameCourse ? 1 : 0); int pairSize = 1; - if (gdi.hasField("PairSize")) { + if (gdi.hasWidget("PairSize")) { pairSize = gdi.getSelectedItem("PairSize").first; } @@ -1269,7 +1238,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) wstring firstStart = oe->getAbsTime(3600); wstring minInterval = L"2:00"; wstring vacances = getDefaultVacant(); - if (gdi.hasField("Vacances")) { + if (gdi.hasWidget("Vacances")) { vacances = gdi.getText("Vacances"); setDefaultVacant(vacances); } @@ -1277,7 +1246,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) int maxNumControl = 1; int pairSize = 1; - if (gdi.hasField("AllowNeighbours")) { + if (gdi.hasWidget("AllowNeighbours")) { bool allowNeighbourSameCourse = gdi.isChecked("AllowNeighbours"); oe->setProperty("DrawInterlace", allowNeighbourSameCourse ? 1 : 0); } @@ -1296,7 +1265,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) minInterval = gdi.getText("MinInterval"); vacances = gdi.getText("Vacances"); //pairwise = gdi.isChecked("Pairwise"); - if (gdi.hasField("PairSize")) { + if (gdi.hasWidget("PairSize")) { pairSize = gdi.getSelectedItem("PairSize").first; } } @@ -1480,15 +1449,15 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) oEvent::DrawMethod method = oEvent::DrawMethod(gdi.getSelectedItem("Method").first); int interval = 0; - if (gdi.hasField("Interval")) + if (gdi.hasWidget("Interval")) interval = convertAbsoluteTimeMS(gdi.getText("Interval")); int vacanses = 0; - if (gdi.hasField("Vacanses")) + if (gdi.hasWidget("Vacanses")) vacanses = gdi.getTextNo("Vacanses"); int leg = 0; - if (gdi.hasField("Leg")) { + if (gdi.hasWidget("Leg")) { leg = gdi.getSelectedItem("Leg").first; } else if (pc && pc->getParentClass() != 0) @@ -1497,7 +1466,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) wstring bib; bool doBibs = false; - if (gdi.hasField("Bib")) { + if (gdi.hasWidget("Bib")) { bib = gdi.getText("Bib"); doBibs = gdi.isChecked("HandleBibs"); } @@ -1520,23 +1489,23 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) } //bool pairwise = false; -// if (gdi.hasField("Pairwise")) +// if (gdi.hasWidget("Pairwise")) // pairwise = gdi.isChecked("Pairwise"); int pairSize = 1; - if (gdi.hasField("PairSize")) { + if (gdi.hasWidget("PairSize")) { pairSize = gdi.getSelectedItem("PairSize").first; } int maxTime = 0, restartTime = 0; double scaleFactor = 1.0; - if (gdi.hasField("TimeRestart")) + if (gdi.hasWidget("TimeRestart")) restartTime = oe->getRelativeTime(gdi.getText("TimeRestart")); - if (gdi.hasField("MaxAfter")) + if (gdi.hasWidget("MaxAfter")) maxTime = convertAbsoluteTimeMS(gdi.getText("MaxAfter")); - if (gdi.hasField("ScaleFactor")) + if (gdi.hasWidget("ScaleFactor")) scaleFactor = _wtof(gdi.getText("ScaleFactor").c_str()); if (method == oEvent::DrawMethod::Random || method == oEvent::DrawMethod::SOFT || method == oEvent::DrawMethod::MeOS) { @@ -1623,7 +1592,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) return 0; int leg = 0; - if (gdi.hasField("Leg")) { + if (gdi.hasWidget("Leg")) { leg = gdi.getSelectedItem("Leg").first; } vector spec; @@ -1836,8 +1805,23 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) oListParam par; par.selection.insert(cid); oListInfo info; - par.listCode = EStdStartList; - par.setLegNumberCoded(leg); + ClassConfigInfo cc; + if (pc->getNumDistinctRunnersMinimal() == 1) { + par.listCode = EStdStartList; + par.setLegNumberCoded(leg); + } + else { + if (pc->getClassType() == ClassType::oClassPatrol) { + par.listCode = oe->getListContainer().getType("patrolstart"); + } + else if (leg >= 0) { + par.listCode = EStdTeamStartListLeg; + par.setLegNumberCoded(leg); + } + else { + par.listCode = EStdStartList; + } + } oe->generateListInfo(par, gdi.getLineHeight(), info); oe->generateList(gdi, false, info, true); @@ -2146,6 +2130,13 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) int num = bi.data; updateSplitDistribution(gdi, num, bi.getExtraInt()); } + else if (bi.id == "Module") { + size_t ix = gdi.getSelectedItem("Module").first; + if (ix < currentResultModuleTags.size()) { + const string &mtag = currentResultModuleTags[ix]; + gdi.hideWidget("EditModule", mtag.empty()); + } + } else if (bi.id=="Courses") EditChanged=true; else if (bi.id == "BibSettings") { @@ -2496,7 +2487,7 @@ void TabClass::selectClass(gdioutput &gdi, int cid) if (cid==0) { gdi.restore("", true); gdi.disableInput("MultiCourse", true); - if (gdi.hasField("Courses")) + if (gdi.hasWidget("Courses")) gdi.enableInput("Courses"); gdi.enableEditControls(false); gdi.setText("Name", L""); @@ -2504,19 +2495,19 @@ void TabClass::selectClass(gdioutput &gdi, int cid) gdi.check("AllowQuickEntry", true); gdi.setText("NumberMaps", L""); - if (gdi.hasField("FreeStart")) + if (gdi.hasWidget("FreeStart")) gdi.check("FreeStart", false); - if (gdi.hasField("IgnoreStart")) + if (gdi.hasWidget("IgnoreStart")) gdi.check("IgnoreStart", false); - if (gdi.hasField("DirectResult")) + if (gdi.hasWidget("DirectResult")) gdi.check("DirectResult", false); - if (gdi.hasField("LockStartList")) { + if (gdi.hasWidget("LockStartList")) { gdi.check("LockStartList", false); gdi.setInputStatus("LockStartList", false); } - if (gdi.hasField("NoTiming")) + if (gdi.hasWidget("NoTiming")) gdi.check("NoTiming", false); ClassId=cid; @@ -2549,28 +2540,30 @@ void TabClass::selectClass(gdioutput &gdi, int cid) else gdi.selectItemByData("StartBlock", -1); - if (gdi.hasField("Status")) { + if (gdi.hasWidget("Status")) { vector< pair > out; size_t selected = 0; pc->getDCI().fillInput("Status", out, selected); gdi.addItem("Status", out); gdi.selectItemByData("Status", selected); + + fillResultModules(gdi, pc); } gdi.check("AllowQuickEntry", pc->getAllowQuickEntry()); - if (gdi.hasField("NoTiming")) + if (gdi.hasWidget("NoTiming")) gdi.check("NoTiming", pc->getNoTiming()); - if (gdi.hasField("FreeStart")) + if (gdi.hasWidget("FreeStart")) gdi.check("FreeStart", pc->hasFreeStart()); - if (gdi.hasField("IgnoreStart")) + if (gdi.hasWidget("IgnoreStart")) gdi.check("IgnoreStart", pc->ignoreStartPunch()); - if (gdi.hasField("DirectResult")) + if (gdi.hasWidget("DirectResult")) gdi.check("DirectResult", pc->hasDirectResult()); - if (gdi.hasField("LockStartList")) { + if (gdi.hasWidget("LockStartList")) { bool active = pc->getParentClass() != 0; gdi.setInputStatus("LockStartList", active); gdi.check("LockStartList", active && pc->lockedClassAssignment()); @@ -2581,7 +2574,7 @@ void TabClass::selectClass(gdioutput &gdi, int cid) gdi.restore("", false); gdi.enableInput("MultiCourse", false); - if (gdi.hasField("Courses")) { + if (gdi.hasWidget("Courses")) { gdi.enableInput("Courses"); pCourse pcourse = pc->getCourse(); gdi.selectItemByData("Courses", pcourse ? pcourse->getId() : -2); @@ -2611,22 +2604,22 @@ void TabClass::selectClass(gdioutput &gdi, int cid) multiCourse(gdi, pc->getNumStages()); gdi.refresh(); - if (gdi.hasField("Courses")) { + if (gdi.hasWidget("Courses")) { gdi.addItem("Courses", lang.tl("Flera banor"), -3); gdi.selectItemByData("Courses", -3); gdi.disableInput("Courses"); gdi.check("CoursePool", pc->hasCoursePool()); } - if (gdi.hasField("Unordered")) + if (gdi.hasWidget("Unordered")) gdi.check("Unordered", pc->hasUnorderedLegs()); - if (gdi.hasField("LockForking")) { + if (gdi.hasWidget("LockForking")) { gdi.check("LockForking", pc->lockedForking()); setLockForkingState(gdi, pc->hasCoursePool(), pc->lockedForking()); } - if (gdi.hasField("MCourses")) { + if (gdi.hasWidget("MCourses")) { oe->fillCourses(gdi, "MCourses", true); string strId = "StageCourses_label"; gdi.setTextTranslate(strId, getCourseLabel(pc->hasCoursePool()), true); @@ -2659,11 +2652,11 @@ void TabClass::selectClass(gdioutput &gdi, int cid) gdi.setInputStatus(string("Restart")+legno, !pc->restartIgnored(k), true); gdi.setInputStatus(string("RestartRope")+legno, !pc->restartIgnored(k), true); - if (gdi.hasField(string("Restart")+legno)) + if (gdi.hasWidget(string("Restart")+legno)) gdi.setText(string("Restart")+legno, pc->getRestartTimeS(k)); - if (gdi.hasField(string("RestartRope")+legno)) + if (gdi.hasWidget(string("RestartRope")+legno)) gdi.setText(string("RestartRope")+legno, pc->getRopeTimeS(k)); - if (gdi.hasField(string("MultiR")+legno)) + if (gdi.hasWidget(string("MultiR")+legno)) gdi.selectItemByData((string("MultiR")+legno).c_str(), pc->getLegRunner(k)); } } @@ -2671,13 +2664,13 @@ void TabClass::selectClass(gdioutput &gdi, int cid) else { gdi.restore("", true); gdi.enableInput("MultiCourse", true); - if (gdi.hasField("Courses")) { + if (gdi.hasWidget("Courses")) { gdi.enableInput("Courses"); pCourse pcourse = pc->getCourse(); gdi.selectItemByData("Courses", pcourse ? pcourse->getId() : -2); } } - if (gdi.hasField("QualificationFinal")) + if (gdi.hasWidget("QualificationFinal")) gdi.setInputStatus("QualificationFinal", pc->getParentClass() == 0); gdi.selectItemByData("Classes", cid); @@ -2966,44 +2959,44 @@ void TabClass::save(gdioutput &gdi, bool skipReload) ClassId=pc->getId(); - pc->setName(name); - if (gdi.hasField("NumberMaps")) { + pc->setName(name, true); + if (gdi.hasWidget("NumberMaps")) { pc->setNumberMaps(gdi.getTextNo("NumberMaps")); } - if (gdi.hasField("StartName")) + if (gdi.hasWidget("StartName")) pc->setStart(gdi.getText("StartName")); - if (gdi.hasField("ClassType")) + if (gdi.hasWidget("ClassType")) pc->setType(gdi.getText("ClassType")); - if (gdi.hasField("StartBlock")) + if (gdi.hasWidget("StartBlock")) pc->setBlock(gdi.getTextNo("StartBlock")); - if (gdi.hasField("Status")) { + if (gdi.hasWidget("Status")) { pc->getDI().setEnum("Status", gdi.getSelectedItem("Status").first); } - if (gdi.hasField("CoursePool")) + if (gdi.hasWidget("CoursePool")) pc->setCoursePool(gdi.isChecked("CoursePool")); - if (gdi.hasField("Unordered")) + if (gdi.hasWidget("Unordered")) pc->setUnorderedLegs(gdi.isChecked("Unordered")); - if (gdi.hasField("LockForking")) + if (gdi.hasWidget("LockForking")) pc->lockedForking(gdi.isChecked("LockForking")); pc->setAllowQuickEntry(gdi.isChecked("AllowQuickEntry")); - if (gdi.hasField("NoTiming")) + if (gdi.hasWidget("NoTiming")) pc->setNoTiming(gdi.isChecked("NoTiming")); - if (gdi.hasField("FreeStart")) + if (gdi.hasWidget("FreeStart")) pc->setFreeStart(gdi.isChecked("FreeStart")); - if (gdi.hasField("IgnoreStart")) + if (gdi.hasWidget("IgnoreStart")) pc->setIgnoreStartPunch(gdi.isChecked("IgnoreStart")); - if (gdi.hasField("DirectResult")) { + if (gdi.hasWidget("DirectResult")) { bool withDirect = gdi.isChecked("DirectResult"); if (withDirect && !pc->hasDirectResult() && !hasWarnedDirect && @@ -3017,13 +3010,13 @@ void TabClass::save(gdioutput &gdi, bool skipReload) pc->setDirectResult(withDirect); } - if (gdi.hasField("LockStartList")) { + if (gdi.hasWidget("LockStartList")) { bool locked = gdi.isChecked("LockStartList"); if (pc->getParentClass()) pc->lockedClassAssignment(locked); } - if (gdi.hasField("Courses")) { + if (gdi.hasWidget("Courses")) { int crs = gdi.getSelectedItem("Courses").first; if (crs == 0) { @@ -3039,8 +3032,15 @@ void TabClass::save(gdioutput &gdi, bool skipReload) pc->setCourse(oe->getCourse(crs)); } - if (pc->hasMultiCourse()) { + if (gdi.hasWidget("Module")) { + size_t ix = gdi.getSelectedItem("Module").first; + if (ix < currentResultModuleTags.size()) { + const string &mtag = currentResultModuleTags[ix]; + pc->setResultModule(mtag); + } + } + if (pc->hasMultiCourse()) { if (gdi.hasData("SimpleMulti")) { bool sim = gdi.isChecked("CommonStart"); if (sim) { @@ -3059,7 +3059,7 @@ void TabClass::save(gdioutput &gdi, bool skipReload) char legno[10]; sprintf_s(legno, "%d", k); - if (!gdi.hasField(string("LegType")+legno)) + if (!gdi.hasWidget(string("LegType")+legno)) continue; pc->setLegType(k, LegTypes(gdi.getSelectedItem(string("LegType")+legno).first)); @@ -3079,16 +3079,16 @@ void TabClass::save(gdioutput &gdi, bool skipReload) string key; key = string("Restart")+legno; - if (gdi.hasField(key)) + if (gdi.hasWidget(key)) pc->setRestartTime(k, gdi.getText(key)); key = string("RestartRope")+legno; - if (gdi.hasField(key)) + if (gdi.hasWidget(key)) pc->setRopeTime(k, gdi.getText(key)); key = string("MultiR")+legno; - if (gdi.hasField(key)) { + if (gdi.hasWidget(key)) { int mr = gdi.getSelectedItem(key).first; if (pc->getLegRunner(k) != mr) @@ -3143,6 +3143,14 @@ bool TabClass::loadPage(gdioutput &gdi) oe->checkDB(); oe->checkNecessaryFeatures(); gdi.selectTab(tabId); + + TabList &tc = dynamic_cast(*gdi.getTabs().get(TListTab)); + if (tc.getMethodEditor().isShown(this)) { + tc.getMethodEditor().show(this, gdi); + gdi.refresh(); + return true; + } + clearPage(gdi, false); int xp = gdi.getCX(); @@ -3153,8 +3161,7 @@ bool TabClass::loadPage(gdioutput &gdi) ClassesCB, "Välj vy", false, false).fixedCorner(); if (tableMode) { - Table *tbl = oe->getClassTB(); - gdi.addTable(tbl, xp, 30); + gdi.addTable(oClass::getTable(oe), xp, gdi.scaleLength(30)); return true; } @@ -3235,7 +3242,23 @@ bool TabClass::loadPage(gdioutput &gdi) gdi.popX(); gdi.dropLine(3); - gdi.addSelection("Status", 200, 300, 0, L"Status:"); + gdi.addSelection("Status", 100, 300, 0, L"Status:"); + vector> statusClass; + oClass::fillClassStatus(statusClass); + vector< pair > st; + for (auto &sc : statusClass) + st.emplace_back(lang.tl(sc.second), st.size()); + gdi.addItem("Status", st); + gdi.autoGrow("Status"); + + gdi.popX(); + gdi.dropLine(3); + gdi.addSelection("Module", 100, 400, ClassesCB, L"Resultatuträkning:"); + fillResultModules(gdi, nullptr); + gdi.dropLine(0.9); + gdi.addButton("EditModule", "Redigera", ClassesCB); + gdi.hideWidget("EditModule"); + gdi.dropLine(-0.9); } gdi.popX(); @@ -3454,7 +3477,7 @@ void TabClass::saveClassSettingsTable(gdioutput &gdi) { saveClassSettingsTable(gdi, modifiedFee, modifiedBib); oe->synchronize(true); - if (gdi.hasField("BibGap")) { + if (gdi.hasWidget("BibGap")) { int gap = gdi.getTextNo("BibGap"); if (oe->getBibClassGap() != gap) { oe->setBibClassGap(gap); @@ -3544,17 +3567,17 @@ void TabClass::drawDialog(gdioutput &gdi, oEvent::DrawMethod method, const oClas int pairSize = lastPairSize; - if (gdi.hasField("FirstStart")) + if (gdi.hasWidget("FirstStart")) firstStart = oe->getRelativeTime(gdi.getText("FirstStart")); else if (!lastFirstStart.empty()) firstStart = oe->getRelativeTime(lastFirstStart); - if (gdi.hasField("Interval")) + if (gdi.hasWidget("Interval")) interval = convertAbsoluteTimeMS(gdi.getText("Interval")); else if (!lastInterval.empty()) interval = convertAbsoluteTimeMS(lastInterval); - if (gdi.hasField("PairSize")) { + if (gdi.hasWidget("PairSize")) { pairSize = gdi.getSelectedItem("PairSize").first; } gdi.restore("MultiDayDraw", false); @@ -3698,10 +3721,10 @@ void TabClass::setMultiDayClass(gdioutput &gdi, bool hasMulti, oEvent::DrawMetho gdi.selectItemByData("Method", int(defaultMethod)); - if (gdi.hasField("Vacanses")) { + if (gdi.hasWidget("Vacanses")) { gdi.setInputStatus("Vacanses", !hasMulti); } - if (gdi.hasField("HandleBibs")) { + if (gdi.hasWidget("HandleBibs")) { gdi.setInputStatus("HandleBibs", !hasMulti); if (hasMulti) { @@ -3710,7 +3733,7 @@ void TabClass::setMultiDayClass(gdioutput &gdi, bool hasMulti, oEvent::DrawMetho } } - if (gdi.hasField("DoDrawBefore")) { + if (gdi.hasWidget("DoDrawBefore")) { gdi.setInputStatus("DoDrawBefore", !hasMulti); gdi.setInputStatus("DoDrawAfter", !hasMulti); } @@ -3869,7 +3892,7 @@ void TabClass::showClassSelection(gdioutput &gdi, int &bx, int &by, GUICALLBACK } void TabClass::enableLoadSettings(gdioutput &gdi) { - if (!gdi.hasField("LoadSettings")) + if (!gdi.hasWidget("LoadSettings")) return; set sel; gdi.getSelection("Classes", sel); @@ -3982,21 +4005,18 @@ void TabClass::selectCourses(gdioutput &gdi, int legNo) { } void TabClass::updateFairForking(gdioutput &gdi, pClass pc) const { - if (!gdi.hasField("FairForking")) + if (!gdi.hasWidget("FairForking")) return; vector< vector > forks; vector< vector > forksC; set< pair > unfairLegs; - + BaseInfo *bi = gdi.setText("FairForking", gdi.getText("FairForking"), false); + TextInfo &text = dynamic_cast(*bi); if (pc->checkForking(forksC, forks, unfairLegs)) { - BaseInfo *bi = gdi.setText("FairForking", gdi.getText("FairForking"), false); - TextInfo &text = dynamic_cast(*bi); text.setColor(colorGreen); gdi.setText("FairForking", lang.tl("The forking is fair."), true); } else { - BaseInfo *bi = gdi.setText("FairForking", gdi.getText("FairForking"), false); - TextInfo &text = dynamic_cast(*bi); text.setColor(colorRed); gdi.setText("FairForking", lang.tl("The forking is not fair."), true); } @@ -4215,7 +4235,7 @@ void TabClass::saveClassSettingsTable(gdioutput &gdi, set &classModifiedFee int block = gdi.getTextNo("Blck"+id); int sort = gdi.getTextNo("Sort"+id); - if (gdi.hasField("Fee" + id)) { + if (gdi.hasWidget("Fee" + id)) { int fee = oe->interpretCurrency(gdi.getText("Fee"+id)); int latefee = oe->interpretCurrency(gdi.getText("LateFee"+id)); int feered = oe->interpretCurrency(gdi.getText("RedFee"+id)); @@ -4236,7 +4256,7 @@ void TabClass::saveClassSettingsTable(gdioutput &gdi, set &classModifiedFee it->getDI().setInt("HighClassFeeRed", latefeered); } - if (gdi.hasField("Bib" + id)) { + if (gdi.hasWidget("Bib" + id)) { ListBoxInfo lbi; bool mod = false; if (gdi.getSelectedItem("Bib" + id, lbi)) { @@ -4248,7 +4268,7 @@ void TabClass::saveClassSettingsTable(gdioutput &gdi, set &classModifiedFee } modifiedBib |= mod; - if (gdi.hasField("BibTeam" + id)) { + if (gdi.hasWidget("BibTeam" + id)) { ListBoxInfo lbi_bib; if (gdi.getSelectedItem("BibTeam" + id, lbi_bib)) { if (it->getBibMode() != lbi_bib.data) @@ -4270,6 +4290,7 @@ void TabClass::saveClassSettingsTable(gdioutput &gdi, set &classModifiedFee it->setCourse(oe->getCourse(courseId)); it->getDI().setInt("SortIndex", sort); it->setAllowQuickEntry(direct); + it->synchronize(true); } } @@ -4293,7 +4314,7 @@ void TabClass::updateStartData(gdioutput &gdi, pClass pc, int leg, bool updateDe if (st == STChange) { if (typeid(sdataBase) != typeid(ListBoxInfo)) { InputInfo sdII = dynamic_cast(sdataBase); - gdi.removeControl(sdKey); + gdi.removeWidget(sdKey); gdi.addSelection(sdII.getX(), sdII.getY(), sdKey, sdII.getWidth(), 200, MultiCB); setParallelOptions(sdKey, gdi, pc, leg); } @@ -4304,7 +4325,7 @@ void TabClass::updateStartData(gdioutput &gdi, pClass pc, int leg, bool updateDe else { if (typeid(sdataBase) != typeid(InputInfo)) { ListBoxInfo sdLBI = dynamic_cast(sdataBase); - gdi.removeControl(sdKey); + gdi.removeWidget(sdKey); string val = "-"; gdi.addInput(sdLBI.getX(), sdLBI.getY(), sdKey, pc->getStartDataS(leg), 8, MultiCB); } @@ -4452,19 +4473,19 @@ void TabClass::setLockForkingState(gdioutput &gdi, const oClass &c) { } void TabClass::setLockForkingState(gdioutput &gdi, bool poolState, bool lockState) { - if (gdi.hasField("DefineForking")) + if (gdi.hasWidget("DefineForking")) gdi.setInputStatus("DefineForking", !lockState && !poolState); - if (gdi.hasField("LockForking")) + if (gdi.hasWidget("LockForking")) gdi.setInputStatus("LockForking", !poolState); int legno = 0; - while (gdi.hasField("@Course" + itos(legno))) { + while (gdi.hasWidget("@Course" + itos(legno))) { gdi.setInputStatus("@Course" + itos(legno++), !lockState || poolState); } for (string s : {"MCourses", "StageCourses", "MAdd", "MRemove"}) { - if (gdi.hasField(s)) { + if (gdi.hasWidget(s)) { gdi.setInputStatus(s, !lockState || poolState); } } @@ -4472,7 +4493,7 @@ void TabClass::setLockForkingState(gdioutput &gdi, bool poolState, bool lockStat bool moveUp = false; bool moveDown = false; - if (gdi.hasField("MCourses")) { + if (gdi.hasWidget("MCourses")) { ListBoxInfo lbi; if (gdi.getSelectedItem("StageCourses", lbi)) { if (lbi.index > 0) @@ -4483,10 +4504,10 @@ void TabClass::setLockForkingState(gdioutput &gdi, bool poolState, bool lockStat } } - if (gdi.hasField("MUp")) { + if (gdi.hasWidget("MUp")) { gdi.setInputStatus("MUp", (!lockState || poolState) && moveUp); } - if (gdi.hasField("MDown")) { + if (gdi.hasWidget("MDown")) { gdi.setInputStatus("MDown", (!lockState || poolState) && moveDown); } } @@ -4780,3 +4801,28 @@ void TabClass::setDefaultVacant(const wstring &v) { if (val >= 0 && val <= 100) oe->setProperty("VacantPercent", val); } +void TabClass::fillResultModules(gdioutput &gdi, pClass pc) { + string tag; + if (pc) + tag = pc->getResultModuleTag(); + + vector< pair > st; + vector< pair > > mol; + oe->loadGeneralResults(false, true); + oe->getGeneralResults(false, mol, true); + currentResultModuleTags.clear(); + st.emplace_back(lang.tl("Standard"), 0); + currentResultModuleTags.emplace_back(""); + int current = 0; + for (size_t k = 0; k < mol.size(); k++) { + st.emplace_back(mol[k].second.second, k+1); + if (tag == mol[k].second.first) + current = k+1; + + currentResultModuleTags.push_back(mol[k].second.first); + } + gdi.addItem("Module", st); + gdi.autoGrow("Module"); + gdi.selectItemByData("Module", current); + gdi.hideWidget("EditModule", current == 0); +} diff --git a/code/TabClass.h b/code/TabClass.h index d568516..8cf1c33 100644 --- a/code/TabClass.h +++ b/code/TabClass.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -159,6 +159,9 @@ class TabClass : wstring getDefaultVacant(); void setDefaultVacant(const wstring &val); + + vector currentResultModuleTags; + void fillResultModules(gdioutput &gdi, pClass pc); public: void clearCompetitionData(); diff --git a/code/TabClub.cpp b/code/TabClub.cpp index 5210669..718fc41 100644 --- a/code/TabClub.cpp +++ b/code/TabClub.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -132,10 +132,11 @@ int TabClub::clubCB(gdioutput &gdi, int type, void *data) pClub pc=oe->getClub(lbi.data); if (pc) { gdi.clearPage(true); - oe->calculateTeamResults(false); - oe->sortTeams(ClassStartTime, 0, true); oe->calculateResults({}, oEvent::ResultType::ClassResult); oe->sortRunners(ClassStartTime); + oe->calculateTeamResults(set({}), oEvent::ResultType::ClassResult); + oe->sortTeams(ClassStartTime, 0, true); + int pay, paid; { map ppm; @@ -581,7 +582,7 @@ int TabClub::clubCB(gdioutput &gdi, int type, void *data) else if (type == GUI_LINK) { TextInfo *ti = static_cast(data); if (ti->id == "CmpSettings") { - if (gdi.hasField("SaveSettings")) + if (gdi.hasWidget("SaveSettings")) gdi.sendCtrlMessage("SaveSettings"); TabCompetition &tc = dynamic_cast(*gdi.getTabs().get(TCmpTab)); tc.loadPage(gdi); @@ -592,7 +593,7 @@ int TabClub::clubCB(gdioutput &gdi, int type, void *data) } else if (type == GUI_CLEAR) { if (gdi.isInputChanged("")) { - if (gdi.hasField("SaveSettings")) { + if (gdi.hasWidget("SaveSettings")) { gdi.sendCtrlMessage("SaveSettings"); } } @@ -664,8 +665,7 @@ bool TabClub::loadPage(gdioutput &gdi) gdi.dropLine(2); gdi.addString("", 10, "help:29758"); gdi.dropLine(1); - Table *tbl=oe->getClubsTB(); - gdi.addTable(tbl, gdi.getCX(), gdi.getCY()); + gdi.addTable(oClub::getTable(oe), gdi.getCX(), gdi.getCY()); gdi.refresh(); return true; } diff --git a/code/TabClub.h b/code/TabClub.h index 6657102..a6bf1ce 100644 --- a/code/TabClub.h +++ b/code/TabClub.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/TabCompetition.cpp b/code/TabCompetition.cpp index 8f650ba..2ba5af1 100644 --- a/code/TabCompetition.cpp +++ b/code/TabCompetition.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -60,6 +60,7 @@ #include #include #include +#include "generalresult.h" void Setup(bool overwrite, bool overWriteall); void exportSetup(); @@ -132,11 +133,11 @@ bool TabCompetition::save(gdioutput &gdi, bool write) oe->updateStartTimes(delta); } - oe->setDate(date); + oe->setDate(date, true); oe->useLongTimes(longTimes); - oe->setName(gdi.getText("Name")); + oe->setName(gdi.getText("Name"), true); oe->setAnnotation(gdi.getText("Annotation")); - oe->setZeroTime(zt); + oe->setZeroTime(zt, true); oe->synchronize(); @@ -872,7 +873,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) gdi.disableInput("ExpandTResults", true); gdi.disableInput("SelectAll", true); gdi.disableInput("SelectNone", true); - if (gdi.hasField("ClassNewEntries")) { + if (gdi.hasWidget("ClassNewEntries")) { gdi.getSelection("ClassNewEntries", allTransfer); transferNoCompet = gdi.isChecked("TransferEconomy"); } @@ -977,7 +978,6 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) gdi.addButton("MultiEvent", "Återgå", CompetitionCB); gdi.scrollToBottom(); gdi.refresh(); - } else throw std::exception("Kunde inte lokalisera nästa etapp"); @@ -1406,7 +1406,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) ci = &events[eventIndex]; bool removeRemoved = true; - if (gdi.hasField("RemoveRemoved")) + if (gdi.hasWidget("RemoveRemoved")) removeRemoved = gdi.isChecked("RemoveRemoved"); wstring course = gdi.getText("FileName", true); @@ -1431,7 +1431,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) lastEntry = gdi.getText("LastEntryDate"); } - if (gdi.hasField("EventorImport")) { + if (gdi.hasWidget("EventorImport")) { gdi.disableInput("EventorImport"); gdi.disableInput("FileName"); gdi.disableInput("FirstStart"); @@ -1504,7 +1504,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) gdi.addString("", 1, "Skapar ny tävling"); oe->newCompetition(L"New"); oe->importXML_EntryData(gdi, tEvent, false, false, noFilter, noType); - oe->setZeroTime(formatTimeHMS(zeroTime)); + oe->setZeroTime(formatTimeHMS(zeroTime), false); oe->getDI().setDate("OrdinaryEntry", lastEntry); if (ci) { if (!ci->account.empty()) @@ -1598,7 +1598,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) } gdi.scrollToBottom(); gdi.dropLine(); - if (gdi.hasField("Cancel")) + if (gdi.hasWidget("Cancel")) gdi.disableInput("Cancel"); // Disable "cancel" above gdi.fillRight(); if (id > 0) @@ -1740,10 +1740,10 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) if (save.empty()) throw meosException("Filnamn kan inte vara tomt"); - bool individual = !gdi.hasField("ExportTeam") || gdi.isChecked("ExportTeam"); + bool individual = !gdi.hasWidget("ExportTeam") || gdi.isChecked("ExportTeam"); bool includeStage = true; - if (gdi.hasField("IncludeRaceNumber")) + if (gdi.hasWidget("IncludeRaceNumber")) includeStage = gdi.isChecked("IncludeRaceNumber"); gdi.getSelection("ClassNewEntries", allTransfer); @@ -1784,7 +1784,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) if (save.empty()) throw meosException("Filnamn kan inte vara tomt"); - //bool individual = !gdi.hasField("ExportTeam") || gdi.isChecked("ExportTeam"); + //bool individual = !gdi.hasWidget("ExportTeam") || gdi.isChecked("ExportTeam"); gdi.getSelection("ClassNewEntries", allTransfer); checkReadyForResultExport(gdi, allTransfer); @@ -1795,7 +1795,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) bool unroll = gdi.isChecked("UnrollLoops"); // If not applicable, field does not exist. bool includeStage = true; - if (gdi.hasField("IncludeRaceNumber")) + if (gdi.hasWidget("IncludeRaceNumber")) includeStage = gdi.isChecked("IncludeRaceNumber"); gdi.setWaitCursor(true); @@ -2259,7 +2259,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) } else if (type==GUI_CLEAR) { if (gdi.isInputChanged("")) { - if (gdi.hasField("SaveSettings")) { + if (gdi.hasWidget("SaveSettings")) { gdi.sendCtrlMessage("SaveSettings"); } else { @@ -2310,7 +2310,7 @@ int TabCompetition::restoreCB(gdioutput &gdi, int type, void *data) { else { const wstring &name = oe->getName(); if (name.find_last_of(L"}") != name.length()-1) - oe->setName(name + L" {" + lang.tl(L"återställd") + L"}"); + oe->setName(name + L" {" + lang.tl(L"återställd") + L"}", false); oe->restoreBackup(); @@ -2357,7 +2357,7 @@ void TabCompetition::copyrightLine(gdioutput &gdi) const gdi.dropLine(0.4); gdi.fillDown(); - gdi.addString("", 0, makeDash(L"#Copyright © 2007-2019 Melin Software HB")); + gdi.addString("", 0, makeDash(L"#Copyright © 2007-2020 Melin Software HB")); gdi.dropLine(1); gdi.popX(); @@ -2387,7 +2387,7 @@ void TabCompetition::loadAboutPage(gdioutput &gdi) const gdi.dropLine(1.5); gdi.setCX(gdi.getCX() + gdi.scaleLength(20)); - gdi.addStringUT(1, makeDash(L"Copyright © 2007-2019 Melin Software HB")); + gdi.addStringUT(1, makeDash(L"Copyright © 2007-2020 Melin Software HB")); gdi.dropLine(); gdi.addStringUT(10, "The database connection used is MySQL++\nCopyright " "(c) 1998 by Kevin Atkinson, (c) 1999, 2000 and 2001 by MySQL AB," @@ -3210,14 +3210,14 @@ void TabCompetition::loadRunnerDB(gdioutput &gdi, int tableToShow, bool updateTa if (tableToShow == 1) { oe->updateRunnerDatabase(); - Table *tb = oe->getRunnerDatabase().getRunnerTB(); - gdi.addTable(tb, 40, gdi.getCY()); + auto tb = oe->getRunnerDatabase().getRunnerTB(); + gdi.addTable(tb, gdi.scaleLength(40), gdi.getCY()); gdi.registerEvent("CellAction", CompetitionCB); } else if (tableToShow == 2) { oe->updateRunnerDatabase(); - Table *tb = oe->getRunnerDatabase().getClubTB(); - gdi.addTable(tb, 40, gdi.getCY()); + auto tb = oe->getRunnerDatabase().getClubTB(); + gdi.addTable(tb, gdi.scaleLength(40), gdi.getCY()); } gdi.refresh(); @@ -3646,17 +3646,17 @@ TabCompetition::FlowOperation TabCompetition::checkStageFilter(gdioutput & gdi, gdi.scrollToBottom(); gdi.refresh(); gdi.runSubCommand(); - while (gdi.hasField("OK_Stage") && gdi.getExtraInt("OK_Stage") == 0 && gdi.getExtraInt("Cancel_Stage") == 0) { + while (gdi.hasWidget("OK_Stage") && gdi.getExtraInt("OK_Stage") == 0 && gdi.getExtraInt("Cancel_Stage") == 0) { mainMessageLoop(0, 10); } bool ok = false, cancel = false; - if (gdi.hasField("OK_Stage")) { + if (gdi.hasWidget("OK_Stage")) { ok = gdi.getExtraInt("OK_Stage") != 0; cancel = gdi.getExtraInt("Cancel_Stage") != 0; - gdi.removeControl("OK_Stage"); - gdi.removeControl("Cancel_Stage"); + gdi.removeWidget("OK_Stage"); + gdi.removeWidget("Cancel_Stage"); if (stageFilter) gdi.disableInput("Stage"); if (idtype) @@ -3827,20 +3827,20 @@ void TabCompetition::selectExportSplitOptions(gdioutput &gdi) { } void TabCompetition::setExportOptionsStatus(gdioutput &gdi, int format) const { - if (gdi.hasField("LegType")) { + if (gdi.hasWidget("LegType")) { gdi.setInputStatus("LegType", format == ImportFormats::IOF30 || format == ImportFormats::IOF203); // Enable on IOF-XML } - if (gdi.hasField("ExportTeam")) { + if (gdi.hasWidget("ExportTeam")) { gdi.setInputStatus("ExportTeam", format == ImportFormats::IOF30); // Enable on IOF-XML } - if (gdi.hasField("ExportSplitTimes")) { + if (gdi.hasWidget("ExportSplitTimes")) { gdi.setInputStatus("ExportSplitTimes", format == ImportFormats::OE); if (format == ImportFormats::IOF203 || format == ImportFormats::IOF30) gdi.check("ExportSplitTimes", true); } - if (gdi.hasField("IncludeRaceNumber")) { + if (gdi.hasWidget("IncludeRaceNumber")) { gdi.setInputStatus("IncludeRaceNumber", format == ImportFormats::IOF30); // Enable on IOF-XML } @@ -4047,7 +4047,7 @@ void TabCompetition::saveSettings(gdioutput &gdi) { oe->getPayModes(modes); for (size_t k = 0; k < modes.size(); k++) { string field = "M"+itos(k); - if (gdi.hasField(field)) { + if (gdi.hasWidget(field)) { wstring mode = gdi.getText("M"+itos(k)); int id = gdi.getBaseInfo(field.c_str()).getExtraInt(); oe->setPayMode(id, mode); diff --git a/code/TabCompetition.h b/code/TabCompetition.h index 57d5784..8689d70 100644 --- a/code/TabCompetition.h +++ b/code/TabCompetition.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/TabControl.cpp b/code/TabControl.cpp index 4b2a485..deacbaf 100644 --- a/code/TabControl.cpp +++ b/code/TabControl.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -96,7 +96,7 @@ void TabControl::selectControl(gdioutput &gdi, pControl pc) gdi.setText("Name", pc->getName()); gdi.setText("TimeAdjust", pc->getTimeAdjustS()); gdi.setText("MinTime", pc->getMinTimeS()); - if (gdi.hasField("Point")) + if (gdi.hasWidget("Point")) gdi.setText("Point", pc->getRogainingPointsS()); controlId = pc->getId(); @@ -114,7 +114,7 @@ void TabControl::selectControl(gdioutput &gdi, pControl pc) if (st == oControl::StatusNoTiming) gdi.disableInput("TimeAdjust"); - if (gdi.hasField("Point") && st != oControl::StatusRogaining) + if (gdi.hasWidget("Point") && st != oControl::StatusRogaining) gdi.disableInput("Point"); } } @@ -127,7 +127,7 @@ void TabControl::selectControl(gdioutput &gdi, pControl pc) gdi.setText("ControlID", makeDash(L"-"), true); gdi.setText("TimeAdjust", L"00:00"); - if (gdi.hasField("Point")) + if (gdi.hasWidget("Point")) gdi.setText("Point", L""); gdi.disableInput("Remove"); @@ -169,7 +169,7 @@ void TabControl::save(gdioutput &gdi) pc->setRogainingPoints(0); } else { - if (gdi.hasField("Point")) { + if (gdi.hasWidget("Point")) { pc->setMinTime(0); pc->setRogainingPoints(gdi.getTextNo("Point")); } @@ -355,7 +355,7 @@ int TabControl::controlCB(gdioutput &gdi, int type, void *data) else if (bi.id=="Visitors") { save(gdi); - Table *table=new Table(oe, 20, L"Kontroll X#" + itow(controlId), "controlvisitor"); + shared_ptr
table=make_shared
(oe, 20, L"Kontroll X#" + itow(controlId), "controlvisitor"); table->addColumn("Id", 70, true, true); table->addColumn("Ändrad", 70, false); @@ -375,11 +375,11 @@ int TabControl::controlCB(gdioutput &gdi, int type, void *data) int xp=gdi.getCX(); gdi.fillDown(); gdi.addButton("Show", "Återgå", ControlsCB); - gdi.addTable(table, xp, gdi.getCY()); + gdi.addTable(table, xp, gdi.getCY()); gdi.refresh(); } else if (bi.id=="Courses") { - Table *table=new Table(oe, 20, L"Kontroll X#" + itow(controlId), "controlcourse"); + auto table=make_shared
(oe, 20, L"Kontroll X#" + itow(controlId), "controlcourse"); table->addColumn("Id", 70, true, true); table->addColumn("Ändrad", 70, false); @@ -395,7 +395,7 @@ int TabControl::controlCB(gdioutput &gdi, int type, void *data) int xp=gdi.getCX(); gdi.fillDown(); gdi.addButton("Show", "Ã…tergÃ¥", ControlsCB); - gdi.addTable(table, xp, gdi.getCY()); + gdi.addTable(table, xp, gdi.getCY()); gdi.refresh(); } else if (bi.id=="Show") { @@ -445,8 +445,7 @@ bool TabControl::loadPage(gdioutput &gdi) ControlsCB, "Välj vy", false, false).fixedCorner(); if (tableMode) { - Table *tbl=oe->getControlTB(); - gdi.addTable(tbl, xp, 30); + gdi.addTable(oControl::getTable(oe), xp, gdi.scaleLength(30)); return true; } diff --git a/code/TabControl.h b/code/TabControl.h index 8a92d2e..aeb8dd6 100644 --- a/code/TabControl.h +++ b/code/TabControl.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/TabCourse.cpp b/code/TabCourse.cpp index 8cb4d12..67bc4e4 100644 --- a/code/TabCourse.cpp +++ b/code/TabCourse.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -59,7 +59,7 @@ void LoadClassPage(gdioutput &gdi); void TabCourse::selectCourse(gdioutput &gdi, pCourse pc) { - if (gdi.hasField("Rogaining")) { + if (gdi.hasWidget("Rogaining")) { gdi.setText("TimeLimit", L""); gdi.disableInput("TimeLimit"); gdi.setText("PointLimit", L""); @@ -77,7 +77,8 @@ void TabCourse::selectCourse(gdioutput &gdi, pCourse pc) wstring uis = pc->getControlsUI(); gdi.setText("Controls", uis); - gdi.setText("CourseExpanded", encodeCourse(uis, pc->useFirstAsStart(), pc->useLastAsFinish()), true); + gdi.setText("CourseExpanded", encodeCourse(uis, pc->getMaximumRogainingTime() > 0, + pc->useFirstAsStart(), pc->useLastAsFinish()), true); gdi.setText("Name", pc->getName()); @@ -88,7 +89,7 @@ void TabCourse::selectCourse(gdioutput &gdi, pCourse pc) gdi.check("FirstAsStart", pc->useFirstAsStart()); gdi.check("LastAsFinish", pc->useLastAsFinish()); - if (gdi.hasField("Rogaining")) { + if (gdi.hasWidget("Rogaining")) { int rt = pc->getMaximumRogainingTime(); int rp = pc->getMinimumRogainingPoints(); @@ -215,20 +216,20 @@ void TabCourse::selectCourse(gdioutput &gdi, pCourse pc) gdi.setInputStatus("DrawCourse", pc != 0); } -int CourseCB(gdioutput *gdi, int type, void *data) -{ +int CourseCB(gdioutput *gdi, int type, void *data) { TabCourse &tc = dynamic_cast(*gdi->getTabs().get(TCourseTab)); - return tc.courseCB(*gdi, type, data); } -void TabCourse::save(gdioutput &gdi, int canSwitchViewMode) -{ +void TabCourse::save(gdioutput &gdi, int canSwitchViewMode) { DWORD cid = courseId; pCourse pc; wstring name=gdi.getText("Name"); + if (cid == 0 && name.empty()) + return; + if (name.empty()) { gdi.alert("Banan mÃ¥ste ha ett namn."); return; @@ -292,7 +293,7 @@ void TabCourse::save(gdioutput &gdi, int canSwitchViewMode) else pc->setShorterVersion(false, 0); - if (gdi.hasField("Rogaining")) { + if (gdi.hasWidget("Rogaining")) { string t; pc->setMaximumRogainingTime(convertAbsoluteTimeMS(gdi.getText("TimeLimit"))); pc->setMinimumRogainingPoints(_wtoi(gdi.getText("PointLimit").c_str())); @@ -338,6 +339,12 @@ int TabCourse::courseCB(gdioutput &gdi, int type, void *data) if (bi.id=="Save") { save(gdi, 1); } + else if (bi.id == "SwitchMode") { + if (!tableMode) + save(gdi, true); + tableMode = !tableMode; + loadPage(gdi); + } else if (bi.id == "LegLengths") { save(gdi, 2); @@ -723,9 +730,22 @@ bool TabCourse::loadPage(gdioutput &gdi) { gdi.getData("RunnerID", RunnerID); gdi.clearPage(false); + int xp = gdi.getCX(); gdi.setData("ClassID", ClassID); gdi.setData("RunnerID", RunnerID); + + string switchMode; + const int button_w = gdi.scaleLength(90); + switchMode = tableMode ? "Formulärläge" : "Tabelläge"; + gdi.addButton(2, 2, button_w, "SwitchMode", switchMode, + CourseCB, "Välj vy", false, false).fixedCorner(); + + if (tableMode) { + gdi.addTable(oCourse::getTable(oe), xp, gdi.scaleLength(30)); + return true; + } + gdi.addString("", boldLarge, "Banor"); gdi.pushY(); @@ -1125,17 +1145,21 @@ oEvent::DrawMethod TabCourse::getDefaultMethod() const { void TabCourse::clearCompetitionData() { courseId = 0; addedCourse = false; + tableMode = false; } void TabCourse::refreshCourse(const wstring &text, gdioutput &gdi) { bool firstAsStart = gdi.isChecked("FirstAsStart"); bool lastAsFinish = gdi.isChecked("LastAsFinish"); - wstring controls = encodeCourse(text, firstAsStart, lastAsFinish); + bool rogaining = gdi.hasWidget("Rogaining") && gdi.getSelectedItem("Rogaining").first == 1; + + wstring controls = encodeCourse(text, rogaining, firstAsStart, lastAsFinish); + if (controls != gdi.getText("CourseExpanded")) gdi.setText("CourseExpanded", controls, true); } -wstring TabCourse::encodeCourse(const wstring &in, bool firstStart, bool lastFinish) { +wstring TabCourse::encodeCourse(const wstring &in, bool rogaining, bool firstStart, bool lastFinish) { vector newC; string ins; wide2String(in, ins); @@ -1144,35 +1168,53 @@ wstring TabCourse::encodeCourse(const wstring &in, bool firstStart, bool lastFin wstring out; out.reserve(in.length() * 2); wstring bf; - for (size_t i = 0; i < newC.size(); ++i) { - if (i == 0 && (newC.size() > 1 || firstStart)) { - out += lang.tl("Start"); - if (firstStart) - out += L"(" + itow(newC[i]) + L")"; - else - out += dash + formatControl(newC[i], bf); - if (newC.size() == 1) { - out += dash + lang.tl("MÃ¥l"); - break; - } - continue; - } - else - out += dash; + if (!rogaining) { + for (size_t i = 0; i < newC.size(); ++i) { + if (i == 0 && (newC.size() > 1 || firstStart)) { + out += lang.tl("Start"); + if (firstStart) + out += L"(" + itow(newC[i]) + L")"; + else + out += dash + formatControl(newC[i], bf); - if (i+1 == newC.size()) { - if (i == 0) { - out = lang.tl("Start") + dash; + if (newC.size() == 1) { + out += dash + lang.tl("MÃ¥l"); + break; + } + continue; } - if (lastFinish) - out += lang.tl("MÃ¥l") + L"(" + itow(newC[i]) + L")"; else - out += formatControl(newC[i], bf) + dash + lang.tl("MÃ¥l"); + out += dash; + + if (i + 1 == newC.size()) { + if (i == 0) { + out = lang.tl("Start") + dash; + } + if (lastFinish) + out += lang.tl("MÃ¥l") + L"(" + itow(newC[i]) + L")"; + else + out += formatControl(newC[i], bf) + dash + lang.tl("MÃ¥l"); + } + else { + out += formatControl(newC[i], bf); + } } - else { + } + else { + int pcnt = 0; + for (size_t i = 0; i < newC.size(); ++i) { + if (i > 0) + out += L"; "; + auto pc = oe->getControl(newC[i]); + if (pc) + pcnt += pc->getRogainingPoints(); + out += formatControl(newC[i], bf); } + + if (pcnt > 0) + out += L" = " + itow(pcnt) + L"p"; } return out; } diff --git a/code/TabCourse.h b/code/TabCourse.h index 1339ec5..863c658 100644 --- a/code/TabCourse.h +++ b/code/TabCourse.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -38,6 +38,8 @@ class TabCourse : wstring point_limit; wstring point_reduction; + bool tableMode = false; + void fillCourseControls(gdioutput &gdi, const wstring &ctrl); void fillOtherCourses(gdioutput &gdi, oCourse &crs, bool withLoops); @@ -47,7 +49,7 @@ class TabCourse : oEvent::DrawMethod getDefaultMethod() const; - wstring encodeCourse(const wstring &in, bool firstStart, bool lastFinish); + wstring encodeCourse(const wstring &in, bool rogaining, bool firstStart, bool lastFinish); void refreshCourse(const wstring &text, gdioutput &gdi); const wstring &formatControl(int id, wstring &bf) const; diff --git a/code/TabList.cpp b/code/TabList.cpp index b7f678f..f27d06f 100644 --- a/code/TabList.cpp +++ b/code/TabList.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -52,12 +52,14 @@ #include "animationdata.h" #include #include "HTMLWriter.h" +#include "generalresult.h" const static int CUSTOM_OFFSET = 10; const static int NUMTEXTSAMPLE = 13; const static int ForcePageBreak = 1024; const static int IgnoreLimitPer = 512; +const static int AddTeamRogaining = 5; const static int AddTeamClasses = 4; const static int AddPatrolClasses = 3; const static int AddRogainingClasses = 2; @@ -65,20 +67,12 @@ const static int AddAllClasses = 1; TabList::TabList(oEvent *poe):TabBase(poe) { - listEditor = 0; - methodEditor = 0; - lastHtmlTarget = poe->getPropertyString("LastExportTarget", L""); clearCompetitionData(); } TabList::~TabList(void) { - delete listEditor; - delete methodEditor; - listEditor = 0; - methodEditor = 0; - for (size_t k = 0; k < liveResults.size(); k++) { delete liveResults[k]; liveResults[k] = 0; @@ -140,7 +134,7 @@ int NoStartRunnerCB(gdioutput *gdi, int type, void *data) pRunner p = tc.getEvent()->getRunner(id, 0); if (p) { - p->setStatus(StatusDNS, true, false); + p->setStatus(StatusDNS, true, oBase::ChangeType::Update); p->synchronize(); ti->callBack=0; ti->highlight=false; @@ -368,7 +362,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) else if (bi.id == "ClassSelection") { gdioutput *gdi_settings = getExtraWindow("list_class", true); if (!gdi_settings) { - gdi_settings = createExtraWindow("list_class", lang.tl("Klassval"), gdi.scaleLength(350), gdi.scaleLength(600), true); + gdi_settings = createExtraWindow("list_class", lang.tl("Klassval"), gdi.scaleLength(350), gdi.scaleLength(650), true); } if (gdi_settings) { loadClassSettings(*gdi_settings, gdi.getTag()); @@ -560,7 +554,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) lst.insert(-1); gdi.setSelection("ListSelection", lst); - if (gdi.hasField("ResultType")) { + if (gdi.hasWidget("ResultType")) { ListBoxInfo entry; gdi.getSelectedItem("ResultType", entry); gdi.setInputStatus("Generate", int(entry.data) >= 0); @@ -569,7 +563,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) else if (bi.id=="SelectNone") { set lst; gdi.setSelection("ListSelection", lst); - if (gdi.hasField("ResultType")) { + if (gdi.hasWidget("ResultType")) { gdi.setInputStatus("Generate", false); } } @@ -580,18 +574,18 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) const char *ctype = (char *)gdi.getData("Type"); saveExtraLines(*oe, ctype, gdi); - if (gdi.hasField("SplitAnalysis")) { + if (gdi.hasWidget("SplitAnalysis")) { int aflag = (gdi.isChecked("SplitAnalysis") ? 0 : 1) + (gdi.isChecked("Speed") ? 0 : 2) + (gdi.isChecked("Results") ? 0 : 4); oe->getDI().setInt("Analysis", aflag); } - if (gdi.hasField("WideFormat")) { + if (gdi.hasWidget("WideFormat")) { bool wide = gdi.isChecked("WideFormat"); oe->setProperty("WideSplitFormat", wide); - if (wide && gdi.hasField("NumPerPage")) { + if (wide && gdi.hasWidget("NumPerPage")) { pair res = gdi.getSelectedItem("NumPerPage"); if (res.second) oe->setProperty("NumSplitsOnePage", res.first); @@ -665,19 +659,12 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) loadGeneralList(gdi); } else if (bi.id == "EditList") { - if (!listEditor) - listEditor = new ListEditor(oe); - gdi.clearPage(false); - listEditor->show(gdi); + getListEditor().show(this, gdi); gdi.refresh(); } else if (bi.id == "EditMethod") { - if (!methodEditor) - methodEditor = new MethodEditor(oe); - - gdi.clearPage(false); - methodEditor->show(gdi); + getMethodEditor().show(this, gdi); gdi.refresh(); } else if (bi.id=="ResultIndividual") { @@ -686,7 +673,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); getResultIndividual(par, cnf); - cnf.getIndividual(par.selection); + cnf.getIndividual(par.selection, false); readSettings(gdi, par, true); oe->generateListInfo(par, gdi.getLineHeight(), currentList); @@ -698,7 +685,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) oListParam par; ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); - cnf.getIndividual(par.selection); + cnf.getIndividual(par.selection, false); par.listCode = EStdResultList; readSettings(gdi, par, true); @@ -901,7 +888,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) par.back().listCode = EIndCourseList; par.back().showInterTitle = false; par.back().setLegNumberCoded(-1); - cnf.getIndividual(par.back().selection); + cnf.getIndividual(par.back().selection, true); } oe->generateListInfo(par, gdi.getLineHeight(), currentList); @@ -923,7 +910,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); oListParam par; - cnf.getIndividual(par.selection); + cnf.getIndividual(par.selection, false); par.listCode = EIndPriceList; par.showHeader = gdi.isChecked("ShowHeader"); par.filterMaxPer = gdi.getSelectedItem("ClassLimit").first; @@ -956,6 +943,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) bool isStartList = bi.id.substr(0, 7) == "StartL:"; bool allClasses = baseType == AddAllClasses; bool rogaining = baseType == AddRogainingClasses; + bool rogainingTeam = baseType == AddTeamRogaining; bool patrol = baseType == AddPatrolClasses; bool team = baseType == AddTeamClasses; oe->sanityCheck(gdi, bi.id.substr(0, 7) == "Result:"); @@ -979,6 +967,11 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) oe->getClassConfigurationInfo(cnf); cnf.getRogaining(par.selection); } + else if (rogainingTeam) { + ClassConfigInfo cnf; + oe->getClassConfigurationInfo(cnf); + cnf.getRogaining(par.selection); + } else if (team) { ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); @@ -987,7 +980,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) else if (!isReport && !allClasses) { ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); - cnf.getIndividual(par.selection); + cnf.getIndividual(par.selection, isStartList); cnf.getPatrol(par.selection); } @@ -1044,7 +1037,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) oListInfo::EBaseType type = oe->getListContainer().getList(index).getListType(); if (oListInfo::addRunners(type)) - cnf.getIndividual(par.selection); + cnf.getIndividual(par.selection, true); if (oListInfo::addPatrols(type)) cnf.getPatrol(par.selection); if (oListInfo::addTeams(type)) @@ -1186,7 +1179,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) } else if (lbi.id == "ListSelection") { gdi.getSelection(lbi.id, lastClassSelection); - if (gdi.hasField("ResultType")) { + if (gdi.hasWidget("ResultType")) { ListBoxInfo entry; gdi.getSelectedItem("ResultType", entry); gdi.setInputStatus("Generate", !lastClassSelection.empty() && int(entry.data) >= 0); @@ -1225,7 +1218,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) oe->synchronize(false); oe->getListContainer().load(MetaListContainer::ExternalList, xlist, false); oe->synchronize(true); - oe->loadGeneralResults(true); + oe->loadGeneralResults(true, false); } ButtonInfo bi; bi.id = "ImportCustom"; @@ -1261,10 +1254,10 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) else if (ti.id == "EditInstalled") { int ix = ti.getExtraInt(); if (!listEditor) - listEditor = new ListEditor(oe); + listEditor = make_shared(oe); gdi.clearPage(false); listEditor->load(oe->getListContainer(), ix); - listEditor->show(gdi); + listEditor->show(this, gdi); gdi.refresh(); } } @@ -1646,7 +1639,7 @@ void TabList::handleRememberSettings(gdioutput &gdi, BaseInfo &info, GuiEventTyp } oe->synchronize(true); - dest_gdi.removeControl("Remember"); + dest_gdi.removeWidget("Remember"); gdi.closeWindow(); } else if (bi.id == "DoMerge") { @@ -1876,13 +1869,13 @@ void TabList::handleListSettings(gdioutput &gdi, BaseInfo &info, GuiEventType ty addAnimationSettings(gdi, tmpSettingsParam); } else { - if (gdi.hasField("Time")) + if (gdi.hasWidget("Time")) saveAnimationSettings(gdi, tmpSettingsParam); - gdi.removeControl("Time"); - gdi.removeControl("NPage"); - gdi.removeControl("Margin"); - gdi.removeControl("Animate"); + gdi.removeWidget("Time"); + gdi.removeWidget("NPage"); + gdi.removeWidget("Margin"); + gdi.removeWidget("Animate"); } gdi.refresh(); } @@ -2000,7 +1993,7 @@ void TabList::handleHTMLSettings(gdioutput &gdi, BaseInfo &info, GuiEventType ty int time_ms = 0; double scale = 0; - if (gdi.hasField("Margin")) { + if (gdi.hasWidget("Margin")) { margin = gdi.getTextNo("Margin"); if (gdi.isChecked("UseRows")) rows = gdi.getTextNo("Rows"); @@ -2129,6 +2122,14 @@ void TabList::loadClassSettings(gdioutput &gdi, string targetTag) { gdi.dropLine(2.5); gdi.fillDown(); + gdi.addSelection("AgeFilter", 200, 150, nullptr, L"Ã…ldersfiltrering:"); + vector> ages; + ages.emplace_back(L"Alla", size_t(oListParam::AgeFilter::All)); + ages.emplace_back(L"Ungdom", size_t(oListParam::AgeFilter::OnlyYouth)); + ages.emplace_back(L"Vuxna", size_t(oListParam::AgeFilter::ExludeYouth)); + gdi.addItem("AgeFilter", ages); + gdi.selectItemByData("AgeFilter", int(currentList.getParam().ageFilter)); + gdi.addCheckbox("PageBreak", "Sidbrytning mellan klasser", 0, currentList.getParam().pageBreak).setHandler(&settingsClassSelection); gdi.addCheckbox("ShowHeader", "Visa rubrik", 0, currentList.getParam().showHeader).setHandler(&settingsClassSelection); @@ -2141,12 +2142,6 @@ void TabList::loadClassSettings(gdioutput &gdi, string targetTag) { gdi.addButton("Cancel", "Avbryt").setCancel().setHandler(&settingsClassSelection); gdi.dropLine(2.5); gdi.popX(); - // - // int ey = gdi.getCY() + gdi.scaleLength(4); -// int ex = gdi.getWidth(); -// RECT rc = { sx, sy, ex, ey }; -// gdi.addRectangle(rc, colorLightBlue); - gdi.refresh(); } @@ -2159,6 +2154,9 @@ void TabList::handleClassSettings(gdioutput &gdi, BaseInfo &info, GuiEventType t oListParam ¶m = currentList.getParam(); param.lockUpdate = true; gdi.getSelection("ListSelection", param.selection); + + auto filter = gdi.getSelectedItem("AgeFilter"); + param.ageFilter = oListParam::AgeFilter(filter.first); bool pb = gdi.isChecked("PageBreak"); param.pageBreak = pb; @@ -2347,6 +2345,19 @@ bool TabList::loadPage(gdioutput &gdi) oe->checkDB(); oe->synchronize(); gdi.selectTab(tabId); + + if (getMethodEditor().isShown(this)) { + getMethodEditor().show(this, gdi); + gdi.refresh(); + return true; + } + + if (getListEditor().isShown(this)) { + getListEditor().show(this, gdi); + gdi.refresh(); + return true; + } + noReEvaluate = false; gdi.clearPage(false); if (SelectedList!="") { @@ -2402,6 +2413,11 @@ bool TabList::loadPage(gdioutput &gdi) } } + if (cnf.hasRogainingTeam()) { + checkWidth(gdi); + gdi.addButton("StartL:teamstartlist", "Rogaining", ListsCB).setExtra(AddRogainingClasses); + } + checkWidth(gdi); gdi.addButton("MinuteStartList", "Minutstartlista", ListsCB); @@ -2496,6 +2512,11 @@ bool TabList::loadPage(gdioutput &gdi) gdi.addButton("Result:rogainingind", "Rogaining", ListsCB).setExtra(AddRogainingClasses); } + if (cnf.hasRogainingTeam()) { + checkWidth(gdi); + gdi.addButton("Result:teamrogainingresult", "Rogaining", ListsCB).setExtra(AddTeamRogaining); + } + checkWidth(gdi); gdi.addButton("ResultList", "Avancerat...", ListsCB); @@ -2680,7 +2701,7 @@ bool TabList::loadPage(gdioutput &gdi) } void TabList::enableWideFormat(gdioutput &gdi, bool wide) { - if (gdi.hasField("NumPerPage")) { + if (gdi.hasWidget("NumPerPage")) { gdi.setInputStatus("NumPerPage", wide); bool needTime = gdi.getSelectedItem("NumPerPage").first != 1; @@ -2982,10 +3003,8 @@ void TabList::clearCompetitionData() { lastLeg = 0; lastFilledResultClassType = -1; - delete listEditor; - delete methodEditor; - listEditor = 0; - methodEditor = 0; + listEditor.reset(); + methodEditor.reset(); } void TabList::setAnimationMode(gdioutput &gdi) { @@ -2997,7 +3016,7 @@ void TabList::setAnimationMode(gdioutput &gdi) { void TabList::getStartIndividual(oListParam &par, ClassConfigInfo &cnf){ par.listCode = EStdStartList; par.setLegNumberCoded(-1); - cnf.getIndividual(par.selection); + cnf.getIndividual(par.selection, true); } void TabList::getStartClub(oListParam &par) { @@ -3006,7 +3025,7 @@ void TabList::getStartClub(oListParam &par) { } void TabList::getResultIndividual(oListParam &par, ClassConfigInfo &cnf) { - cnf.getIndividual(par.selection); + cnf.getIndividual(par.selection, false); par.listCode = EStdResultList; par.showInterTimes = true; par.setLegNumberCoded(-1); @@ -3015,7 +3034,7 @@ void TabList::getResultIndividual(oListParam &par, ClassConfigInfo &cnf) { void TabList::getResultClub(oListParam &par, ClassConfigInfo &cnf) { par.listCode = EStdClubResultList; par.setLegNumberCoded(-1); - cnf.getIndividual(par.selection); + cnf.getIndividual(par.selection, true); cnf.getPatrol(par.selection); } @@ -3124,3 +3143,20 @@ void TabList::readSettings(gdioutput &gdi, oListParam &par, bool forResult) { par.filterMaxPer = gdi.getSelectedItem("ClassLimit").first; } } + +MethodEditor &TabList::getMethodEditor() { + if (!methodEditor) + methodEditor = make_shared(oe); + + return *methodEditor; +} + +ListEditor &TabList::getListEditor() { + if (!listEditor) + listEditor = make_shared(oe); + return *listEditor; +} + +ListEditor *TabList::getListEditorPtr() const { + return listEditor.get(); +} diff --git a/code/TabList.h b/code/TabList.h index 835980e..49624e1 100644 --- a/code/TabList.h +++ b/code/TabList.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -67,8 +67,8 @@ protected: bool hideButtons; bool ownWindow; - ListEditor *listEditor; - MethodEditor *methodEditor; + shared_ptr listEditor; + shared_ptr methodEditor; bool noReEvaluate; @@ -112,6 +112,8 @@ public: /** Returns a collection of public lists. */ void static getPublicLists(oEvent &oe, vector &lists); + MethodEditor &getMethodEditor(); + bool loadPage(gdioutput &gdi); bool loadPage(gdioutput &gdi, const string &command); @@ -149,7 +151,8 @@ public: static void saveExtraLines(oEvent &oe, const char *dataField, gdioutput &gdi); static void enableWideFormat(gdioutput &gdi, bool wide); - ListEditor *getListeditor() const {return listEditor;} + ListEditor &getListEditor(); + ListEditor *getListEditorPtr() const; const char * getTypeStr() const {return "TListTab";} TabType getType() const {return TListTab;} diff --git a/code/TabMulti.cpp b/code/TabMulti.cpp index 95d9a6b..340fd2e 100644 --- a/code/TabMulti.cpp +++ b/code/TabMulti.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/TabMulti.h b/code/TabMulti.h index f9270ca..acf0aab 100644 --- a/code/TabMulti.h +++ b/code/TabMulti.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/TabRunner.cpp b/code/TabRunner.cpp index 9ede707..6e2efb6 100644 --- a/code/TabRunner.cpp +++ b/code/TabRunner.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -131,7 +131,7 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { gdi.selectItemByData("Runners", -1); gdi.setInputFocus("Name", true); - if (gdi.hasField("MultiR")) { + if (gdi.hasWidget("MultiR")) { gdi.clearList("MultiR"); gdi.disableInput("MultiR"); } @@ -139,18 +139,18 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { enableControlButtons(gdi, false, false); disablePunchCourse(gdi); - if (gdi.hasField("EditTeam")) + if (gdi.hasWidget("EditTeam")) gdi.disableInput("EditTeam"); gdi.setText("RunnerInfo", L"", true); gdi.setText("TimeAdjust", makeDash(L"-")); gdi.setText("PointAdjust", L""); - if (gdi.hasField("StatusIn")) { + if (gdi.hasWidget("StatusIn")) { gdi.selectFirstItem("StatusIn"); gdi.setText("PlaceIn", L""); gdi.setText("TimeIn", makeDash(L"-")); - if (gdi.hasField("PointIn")) + if (gdi.hasWidget("PointIn")) gdi.setText("PointIn", L""); } @@ -163,9 +163,8 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { pRunner parent = r->getMultiRunner(0); r->synchronizeAll(); - //r->apply(false); vector mp; - r->evaluateCard(true, mp, 0, false); + r->evaluateCard(true, mp, 0, oBase::ChangeType::Quiet); gdi.selectItemByData("Runners", parent->getId()); @@ -174,7 +173,7 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { gdi.setText("Name", r->getNameRaw()); wstring bib = r->getBib(); - if (gdi.hasField("Bib")) { + if (gdi.hasWidget("Bib")) { gdi.setText("Bib", bib); bool controlBib = r->getTeam() == 0 || (r->getClassRef(true) && r->getClassRef(true)->getBibMode() == BibFree); gdi.setInputStatus("Bib", controlBib); @@ -188,7 +187,7 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { gdi.addItem("RClass", lang.tl("Ingen klass"), 0); gdi.selectItemByData("RClass", r->getClassId(true)); - if (gdi.hasField("EditTeam")) { + if (gdi.hasWidget("EditTeam")) { gdi.setInputStatus("EditTeam", r->getTeam() != 0); if (r->getTeam()) { @@ -239,7 +238,7 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { gdi.refresh(); #endif - if (gdi.hasField("MultiR")) { + if (gdi.hasWidget("MultiR")) { int numMulti = parent->getNumMulti(); if (numMulti == 0) { gdi.clearList("MultiR"); @@ -268,7 +267,7 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { warnDuplicateCard(gdi, cno, r); gdi.check("RentCard", r->isHiredCard()); - bool hasFee = gdi.hasField("Fee"); + bool hasFee = gdi.hasWidget("Fee"); if (hasFee) gdi.setText("Fee", oe->formatCurrency(parent->getDI().getInt("Fee"))); @@ -282,24 +281,25 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { if (hasFee) gdi.setInputStatus("Fee", parent == r); - if(gdi.hasField("Club")) + if(gdi.hasWidget("Club")) gdi.setInputStatus("Club", parent == r); enableControlButtons(gdi, true, r->isVacant()); - gdi.setText("Start", r->getStartTimeS()); + //gdi.setText("Start", r->getStartTimeS()); gdi.setInputStatus("Start", canSetStart(r)); - gdi.setText("Finish", r->getFinishTimeS()); + //gdi.setText("Finish", r->getFinishTimeS()); gdi.setInputStatus("Finish", canSetFinish(r)); - gdi.setText("Time", r->getRunningTimeS()); - gdi.setText("Points", itow(r->getRogainingPoints(false))); + //gdi.setText("Time", r->getRunningTimeS(true)); + //gdi.setText("Points", itow(r->getRogainingPoints(false))); - gdi.selectItemByData("Status", r->getStatus()); - gdi.setText("RunnerInfo", lang.tl(r->getProblemDescription()), true); + //gdi.selectItemByData("Status", r->getStatus()); + //gdi.setText("RunnerInfo", lang.tl(r->getProblemDescription()), true); + updateStatus(gdi, r); - if (gdi.hasField("StatusIn")) { + if (gdi.hasWidget("StatusIn")) { gdi.selectItemByData("StatusIn", r->getInputStatus()); int ip = r->getInputPlace(); if (ip > 0) @@ -308,7 +308,7 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { gdi.setText("PlaceIn", makeDash(L"-")); gdi.setText("TimeIn", r->getInputTimeS()); - if (gdi.hasField("PointIn")) + if (gdi.hasWidget("PointIn")) gdi.setText("PointIn", r->getInputPoints()); } @@ -554,7 +554,7 @@ pRunner TabRunner::save(gdioutput &gdi, int runnerId, bool willExit) { r->getCard()->getCardNo() != cardNo && r->getCardNo() != cardNo) { if (gdi.ask(L"Vill du koppla isär X frÃ¥n inläst bricka Y?#" + r->getName() + L"#" + r->getCard()->getCardNoString())) { - r->setStatus(StatusUnknown, true, false, false); + r->setStatus(StatusUnknown, true, oBase::ChangeType::Update, false); r->setCard(0); r->setFinishTime(0); r->synchronize(true); @@ -581,7 +581,7 @@ pRunner TabRunner::save(gdioutput &gdi, int runnerId, bool willExit) { r->getDI().setInt("CardFee", 0); } - if (gdi.hasField("Fee")) + if (gdi.hasWidget("Fee")) r->getDI().setInt("Fee", oe->interpretCurrency(gdi.getText("Fee"))); gdioutput *gdi_settings = getExtraWindow("ecosettings", false); @@ -593,16 +593,16 @@ pRunner TabRunner::save(gdioutput &gdi, int runnerId, bool willExit) { r->setStartTimeS(gdi.getText("Start")); r->setFinishTimeS(gdi.getText("Finish")); - if (gdi.hasField("NumShort")) { + if (gdi.hasWidget("NumShort")) { r->setNumShortening(gdi.getSelectedItem("NumShort").first); } - if (gdi.hasField("TimeAdjust")) { + if (gdi.hasWidget("TimeAdjust")) { int t = convertAbsoluteTimeMS(gdi.getText("TimeAdjust")); if (t != NOTIME) r->setTimeAdjustment(t); } - if (gdi.hasField("PointAdjust")) { + if (gdi.hasWidget("PointAdjust")) { r->setPointAdjustment(-gdi.getTextNo("PointAdjust")); } @@ -635,25 +635,56 @@ pRunner TabRunner::save(gdioutput &gdi, int runnerId, bool willExit) { r->setClassId(classId, true); - if (gdi.hasField("Bib")) { + if (gdi.hasWidget("Bib")) { const wstring &bib = gdi.getText("Bib"); wchar_t pat[32]; int num = oClass::extractBibPattern(bib, pat); bool lockedForking = r->getClassRef(true) && r->getClassRef(true)->lockedForking(); - r->setBib(bib, num, num>0 && !lockedForking, false); + r->setBib(bib, num, num>0 && !lockedForking); } - r->setCourseId(gdi.getSelectedItem("RCourse").first); + int crsId = gdi.getSelectedItem("RCourse").first; + if (crsId > 0 && r->getCourseId() != -1) { + pClass cls = r->getClassRef(true); + pCourse crs = oe->getCourse(crsId); + vector courses; + if (cls && crs) { + cls->getCourses(r->getLegNumber(), courses); + set crsIds; + for (auto c : courses) + crsIds.insert(c->getId()); + + if (crsIds.count(crsId) == 0) { + vector clsRunner; + oe->getRunners(cls->getId(), -1, clsRunner, false); + bool ok = clsRunner.size() < 2; + for (auto cr : clsRunner) { + if (cr->getCourseId() > 0 && !crsIds.count(cr->getCourseId())) { + ok = true; + break; + } + } + + if (!ok) { + if (!gdi.ask(L"ask:usecourseinclass")) + crsId = 0; + } + } + } + } + + + r->setCourseId(crsId); RunnerStatus sIn = (RunnerStatus)gdi.getSelectedItem("Status").first; bool checkStatus = sIn != originalStatus; if (r->getStatus() != sIn && !noSetStatus) { - r->setStatus(sIn, true, false); + r->setStatus(sIn, true, oBase::ChangeType::Update); } r->addClassDefaultFee(false); vector mp; - r->evaluateCard(true, mp, 0, true); + r->evaluateCard(true, mp, 0, oBase::ChangeType::Update); if (r->getClassId(true) != classId && r->getClassId(false) != classId) { gdi.alert("Deltagarens klass styrs av laget."); @@ -662,16 +693,16 @@ pRunner TabRunner::save(gdioutput &gdi, int runnerId, bool willExit) { if (checkStatus && sIn != r->getStatus()) gdi.alert("Status matchar inte data i löparbrickan."); - if (gdi.hasField("StatusIn") && readStatusIn) { + if (gdi.hasWidget("StatusIn") && readStatusIn) { r->setInputStatus(RunnerStatus(gdi.getSelectedItem("StatusIn").first)); r->setInputPlace(gdi.getTextNo("PlaceIn")); r->setInputTime(gdi.getText("TimeIn")); - if (gdi.hasField("PointIn")) + if (gdi.hasWidget("PointIn")) r->setInputPoints(gdi.getTextNo("PointIn")); } r->synchronizeAll(); - + if (r->getClassRef(false) && r->getClassRef(false)->hasClassGlobalDependence()) { set cls; cls.insert(r->getClassId(false)); @@ -827,7 +858,7 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) else if (bi.id=="SetDNS") { for (size_t k=0; kgetStatus()==StatusUnknown) { - unknown[k]->setStatus(StatusDNS, true, false); + unknown[k]->setStatus(StatusDNS, true, oBase::ChangeType::Update); unknown[k]->setFlag(oAbstractRunner::FlagAutoDNS, true); unknown[k]->synchronize(true); } @@ -842,7 +873,7 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) oe->getRunners(0, 0, runners, true); for (pRunner r : runners) { if (r->getStatus() == StatusDNS && r->hasFlag(oAbstractRunner::FlagAutoDNS)) { - r->setStatus(StatusUnknown, true, false); + r->setStatus(StatusUnknown, true, oBase::ChangeType::Update); r->setFlag(oAbstractRunner::FlagAutoDNS, false); r->synchronize(true); } @@ -855,7 +886,7 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) else if (bi.id=="SetUnknown") { for (size_t k=0; kgetStatus()==StatusDNS) { - known_dns[k]->setStatus(StatusUnknown, true, false); + known_dns[k]->setStatus(StatusUnknown, true, oBase::ChangeType::Update); known_dns[k]->synchronize(true); } } @@ -984,7 +1015,7 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) if (r->getStartTime()>0) { pRunner newRunner = oe->addRunnerVacant(r->getClassId(true)); newRunner->cloneStartTime(r); - newRunner->setStartNo(r->getStartNo(), false); + newRunner->setStartNo(r->getStartNo(), oBase::ChangeType::Update); if (r->getCourseId()) newRunner->setCourseId(r->getCourseId()); newRunner->synchronizeAll(); @@ -993,9 +1024,9 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) for (int k=0;kgetNumMulti()+1; k++) { pRunner rr = r->getMultiRunner(k); if (rr) { - rr->setStartTime(0, true, false); + rr->setStartTime(0, true, oBase::ChangeType::Update); //rr->setStartNo(0, false); - rr->setStatus(StatusCANCEL, true, false); + rr->setStatus(StatusCANCEL, true, oBase::ChangeType::Update); rr->setCardNo(0, false); } } @@ -1047,11 +1078,11 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) card->synchronize(); //Update runner vector mp; - r->evaluateCard(true, mp, 0, true); - + r->evaluateCard(true, mp, 0, oBase::ChangeType::Update); + r->synchronize(); card->fillPunches(gdi, "Punches", r->getCourse(true)); - gdi.setText("Time", r->getRunningTimeS()); + gdi.setText("Time", r->getRunningTimeS(true)); gdi.selectItemByData("Status", r->getStatus()); } else if (bi.id=="Check") { @@ -1314,19 +1345,19 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) oRunner temp(oe, 0); temp.setTemporary(); - temp.setBib(r->getBib(), 0, false, false); - temp.setStartNo(r->getStartNo(), false); + temp.setBib(r->getBib(), 0, false); + temp.setStartNo(r->getStartNo(), oBase::ChangeType::Update); temp.setClassId(r->getClassId(true), true); - temp.apply(false, 0, false); + temp.apply(oBase::ChangeType::Update, nullptr); temp.cloneStartTime(r); r->setClassId(vacancy->getClassId(true), true); // Remove or create multi runners r->createMultiRunner(true, true); - r->apply(false, 0, false); + r->apply(oBase::ChangeType::Quiet, nullptr); r->cloneStartTime(vacancy); - r->setBib(vacancy->getBib(), 0, false, false); - r->setStartNo(vacancy->getStartNo(), false); + r->setBib(vacancy->getBib(), 0, false); + r->setStartNo(vacancy->getStartNo(), oBase::ChangeType::Update); if (oe->hasPrevStage()) { if (gdi.ask(L"Vill du sätta resultatet frÃ¥n tidigare etapper till ?")) @@ -1336,10 +1367,10 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) vacancy->setClassId(temp.getClassId(true), true); // Remove or create multi runners vacancy->createMultiRunner(true, true); - vacancy->apply(false, 0, false); + vacancy->apply(oBase::ChangeType::Update, nullptr); vacancy->cloneStartTime(&temp); - vacancy->setBib(temp.getBib(), 0, false, false); - vacancy->setStartNo(temp.getStartNo(), false); + vacancy->setBib(temp.getBib(), 0, false); + vacancy->setStartNo(temp.getStartNo(), oBase::ChangeType::Update); r->synchronizeAll(); vacancy->synchronizeAll(); @@ -1361,8 +1392,7 @@ void TabRunner::showCardsList(gdioutput &gdi) addToolbar(gdi); gdi.dropLine(); cardModeStartY=gdi.getCY(); - Table *t=oe->getCardsTB(); - gdi.addTable(t,gdi.getCX(),gdi.getCY()+15); + gdi.addTable(oCard::getTable(oe), gdi.getCX(), gdi.getCY() + gdi.scaleLength(15)); gdi.registerEvent("CellAction", RunnerCB); gdi.refresh(); } @@ -1410,7 +1440,7 @@ int TabRunner::vacancyCB(gdioutput &gdi, int type, void *data) wstring club; int birthYear = 0; - if (gdi.hasField("Club")) { + if (gdi.hasWidget("Club")) { club = gdi.getText("Club"); pClub pc = oe->getClubCreate(0, club); if (pc) @@ -1422,7 +1452,7 @@ int TabRunner::vacancyCB(gdioutput &gdi, int type, void *data) r->setClub(club); int fee = 0; - if (gdi.hasField("Fee")) { + if (gdi.hasWidget("Fee")) { ListBoxInfo lbi; if (gdi.getSelectedItem("Fee", lbi) && lbi.data == -1) { lastFee = L"@"; @@ -1454,7 +1484,7 @@ int TabRunner::vacancyCB(gdioutput &gdi, int type, void *data) TabSI::writePayMode(gdi, fee + cardFee, *r); - if (gdi.hasField("AllStages")) { + if (gdi.hasWidget("AllStages")) { r->setFlag(oRunner::FlagTransferSpecified, true); r->setFlag(oRunner::FlagTransferNew, gdi.isChecked("AllStages")); } @@ -1506,7 +1536,7 @@ int TabRunner::vacancyCB(gdioutput &gdi, int type, void *data) tsi.storedInfo.allStages = gdi.isChecked("AllStages"); tsi.storedInfo.rentState = gdi.isChecked("RentCard"); tsi.storedInfo.hasPaid = gdi.isChecked("Paid"); - tsi.storedInfo.payMode = gdi.hasField("PayMode") ? gdi.getSelectedItem("PayMode").first : 0; + tsi.storedInfo.payMode = gdi.hasWidget("PayMode") ? gdi.getSelectedItem("PayMode").first : 0; return 1; } return 0; @@ -1595,7 +1625,7 @@ void TabRunner::showRunnerReport(gdioutput &gdi) { runnerReport(oe, gdi, runnersToReport[k].first, runnersToReport[k].second); } else { - oe.calculateTeamResults(false); + oe.calculateTeamResults(clsSet, oEvent::ResultType::ClassResult); set selectedRunners; bool selHasRes = false; @@ -1607,13 +1637,13 @@ void TabRunner::showRunnerReport(gdioutput &gdi) { } wstring tInfo = t->getName(); - if (t->statusOK()) { - tInfo += L", " + t->getRunningTimeS() + lang.tl(".S Placering: ") + t->getPlaceS(); + if (t->statusOK(true)) { + tInfo += L", " + t->getRunningTimeS(true) + lang.tl(", Placering: ") + t->getPlaceS(); if (t->getTimeAfter(-1) > 0) tInfo += L", +" + formatTime(t->getTimeAfter(-1)); } - else if (t->getStatus() != StatusUnknown) { - tInfo += L" " + t->getStatusS(true); + else if (t->getStatusComputed() != StatusUnknown) { + tInfo += L" " + t->getStatusS(true, true); } gdi.addStringUT(fontMediumPlus, t->getClass(true)); @@ -1676,7 +1706,7 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) { gdi.dropLine(0.3); - if (r->statusOK()) { + if (r->statusOK(true)) { int total, finished, dns; r->getClassRef(true)->getNumResults(r->getLegNumber(), total, finished, dns); @@ -1693,7 +1723,7 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) { } } } - else if (r->getStatus() != StatusUnknown) { + else if (r->getStatusComputed() != StatusUnknown) { gdi.addStringUT(fontMediumPlus, str).setColor(colorRed); } @@ -2125,7 +2155,7 @@ void TabRunner::listRunners(gdioutput &gdi, const vector &r, bool filte gdi.addStringUT(yp, xp+gdi.scaleLength(550), 0, "(" + itos(c) + ")", 190); } else { - TextInfo &ti = gdi.addStringUT(yp, xp+gdi.scaleLength(550), 0, L"(" + itow(c) + lang.tl(", reused card") + L")", gdi.scaleLength(100)); + TextInfo &ti = gdi.addStringUT(yp, xp+gdi.scaleLength(550), 0, L"(" + itow(c) + lang.tl(", reused card") + L")", gdi.scaleLength(150)); wstring tt; for (size_t j = 0; j < out.size(); j++) { if (out[j] == r[k]->getMultiRunner(0)) @@ -2219,15 +2249,27 @@ void disablePunchCourse(gdioutput &gdi) disablePunchCourseChange(gdi); } -void UpdateStatus(gdioutput &gdi, pRunner r) -{ +void TabRunner::updateStatus(gdioutput &gdi, pRunner r) { if (!r) return; gdi.setText("Start", r->getStartTimeS()); gdi.setText("Finish", r->getFinishTimeS()); - gdi.setText("Time", r->getRunningTimeS()); + gdi.setText("Time", r->getRunningTimeS(false)); + gdi.setText("Points", itow(r->getRogainingPoints(false, false))); + gdi.selectItemByData("Status", r->getStatus()); - gdi.setText("RunnerInfo", lang.tl(r->getProblemDescription()), true); + auto ri = r->getRaceInfo(); + BaseInfo *bi = gdi.setText("RunnerInfo", ri.first, true); + TextInfo *ti = dynamic_cast(bi); + assert(ti); + if (ti) { + if (ri.second > 0) + ti->setColor(GDICOLOR::colorGreen); + else if (ri.second < 0) + ti->setColor(GDICOLOR::colorRed); + else + ti->setColor(GDICOLOR::colorDefault); + } } int TabRunner::punchesCB(gdioutput &gdi, int type, void *data) { @@ -2313,7 +2355,8 @@ int TabRunner::punchesCB(gdioutput &gdi, int type, void *data) { if (bi.id == "AddC") { vector mp; - r->evaluateCard(true, mp); + r->synchronize(); + r->evaluateCard(true, mp, 0, oBase::ChangeType::Update); pCourse pc = r->getCourse(true); @@ -2330,30 +2373,30 @@ int TabRunner::punchesCB(gdioutput &gdi, int type, void *data) { vector nmp; if (oc->getStatus() == oControl::StatusRogaining) { - r->evaluateCard(true, nmp, oc->getFirstNumber()); //Add this punch + r->evaluateCard(true, nmp, oc->getFirstNumber(), oBase::ChangeType::Update); //Add this punch } else { for (size_t k = 0; k < mp.size(); k++) { if (oc->hasNumber(mp[k])) - r->evaluateCard(true, nmp, mp[k]); //Add this punch + r->evaluateCard(true, nmp, mp[k], oBase::ChangeType::Update); //Add this punch } } //synchronize SQL card->synchronize(); - r->synchronize(true); - r->evaluateCard(true, mp); + r->synchronizeAll(true); + r->evaluateCard(true, mp, 0, oBase::ChangeType::Quiet); r->hasManuallyUpdatedTimeStatus(); card->fillPunches(gdi, "Punches", pc); - UpdateStatus(gdi, r); + updateStatus(gdi, r); } else if (bi.id == "AddAllC") { vector mp; - r->evaluateCard(true, mp); + r->evaluateCard(true, mp, 0, oBase::ChangeType::Quiet); vector::iterator it = mp.begin(); while (it != mp.end()) { vector nmp; - r->evaluateCard(true, nmp, *it); //Add this punch + r->evaluateCard(true, nmp, *it, oBase::ChangeType::Update); //Add this punch ++it; if (nmp.empty()) break; @@ -2361,11 +2404,11 @@ int TabRunner::punchesCB(gdioutput &gdi, int type, void *data) { //synchronize SQL card->synchronize(); - r->synchronize(true); - r->evaluateCard(true, mp); + r->synchronizeAll(true); + r->evaluateCard(true, mp, 0, oBase::ChangeType::Quiet); card->fillPunches(gdi, "Punches", r->getCourse(true)); r->hasManuallyUpdatedTimeStatus(); - UpdateStatus(gdi, r); + updateStatus(gdi, r); } else if (bi.id == "SaveC") { if (!savePunchTime(r, gdi)) @@ -2392,10 +2435,10 @@ int TabRunner::punchesCB(gdioutput &gdi, int type, void *data) { card->synchronize();*/ vector mp; r->synchronize(); - r->evaluateCard(true, mp); + r->evaluateCard(true, mp, 0, oBase::ChangeType::Quiet); r->hasManuallyUpdatedTimeStatus(); card->fillPunches(gdi, "Punches", r->getCourse(true)); - UpdateStatus(gdi, r); + updateStatus(gdi, r); gdi.selectItemByData("Punches", lbi.data); } } @@ -2419,10 +2462,11 @@ bool TabRunner::savePunchTime(pRunner r, gdioutput &gdi) { card->setPunchTime(pp, gdi.getText("PTime")); vector mp; - r->evaluateCard(true, mp); + r->evaluateCard(true, mp, 0, oBase::ChangeType::Update); //synchronize SQL card->synchronize(); + r->synchronizeAll(); return true; } @@ -2440,10 +2484,9 @@ bool TabRunner::loadPage(gdioutput &gdi) int basex = gdi.getCX(); if (currentMode == 1) { - Table *tbl = oe->getRunnersTB(); addToolbar(gdi); gdi.dropLine(1); - gdi.addTable(tbl, basex, gdi.getCY()); + gdi.addTable(oRunner::getTable(oe), basex, gdi.getCY()); return true; } else if (currentMode == 2) { @@ -2625,8 +2668,8 @@ bool TabRunner::loadPage(gdioutput &gdi) gdi.popX(); gdi.selectItemByData("Status", 0); - gdi.addString("RunnerInfo", 1, "").setColor(colorRed); - + gdi.addString("RunnerInfo", 0, "").setColor(colorRed); + gdi.dropLine(0.4); const bool multiDay = oe->hasPrevStage(); if (multiDay) { @@ -2714,13 +2757,13 @@ bool TabRunner::loadPage(gdioutput &gdi) gdi.dropLine(0.2); gdi.fillRight(); gdi.addButton(gdi.getCX(), gdi.getCY(), gdi.scaleLength(120), "SplitPrint", - "Skriv ut sträcktider", RunnerCB, "", false, false).isEdit(true).setExtra(0); + "Sträcktider", RunnerCB, "", false, false).isEdit(true).setExtra(0); gdi.addButton("PrintSettings", "...", RunnerCB, "Inställningar").isEdit(true).setExtra(0); gdi.dropLine(2.5); gdi.setCX(contX); gdi.addButton(gdi.getCX(), gdi.getCY(), gdi.scaleLength(120), "SplitPrint", - "Skriv ut startbevis", RunnerCB, "", false, false).isEdit(true).setExtra(1); + "Startbevis", RunnerCB, "", false, false).isEdit(true).setExtra(1); gdi.addButton("PrintSettings", "...", RunnerCB, "Inställningar").isEdit(true).setExtra(1); gdi.pushY(); @@ -2908,7 +2951,7 @@ int TabRunner::numShorteningLevels() const { } void TabRunner::updateNumShort(gdioutput &gdi, pCourse crs, pRunner r) { - if (gdi.hasField("NumShort")) { + if (gdi.hasWidget("NumShort")) { if (crs && crs->getShorterVersion().first) { gdi.enableInput("NumShort"); if (r) @@ -3086,12 +3129,12 @@ void TabRunner::handleAutoComplete(gdioutput &gdi, AutoCompleteInfo &info) { auto &db = oe->getRunnerDatabase(); auto runner = db.getRunnerByIndex(ix); - if (runner && gdi.hasField("Club") && gdi.getText("Club").empty()) { + if (runner && gdi.hasWidget("Club") && gdi.getText("Club").empty()) { pClub club = db.getClub(runner->dbe().clubNo); if (club) gdi.setText("Club", club->getName()); } - if (runner && runner->dbe().cardNo > 0 && gdi.hasField("CardNo") && gdi.getText("CardNo").empty()) { + if (runner && runner->dbe().cardNo > 0 && gdi.hasWidget("CardNo") && gdi.getText("CardNo").empty()) { gdi.setText("CardNo", runner->dbe().cardNo); } } @@ -3102,7 +3145,7 @@ void TabRunner::handleAutoComplete(gdioutput &gdi, AutoCompleteInfo &info) { pClub TabRunner::extractClub(oEvent *oe, gdioutput &gdi) { oClub *dbClub = nullptr; - if (gdi.hasField("Club")) { + if (gdi.hasWidget("Club")) { auto &db = oe->getRunnerDatabase(); int clubId = gdi.getExtraInt("Club"); if (clubId >= 0) { diff --git a/code/TabRunner.h b/code/TabRunner.h index 309d721..4acad7a 100644 --- a/code/TabRunner.h +++ b/code/TabRunner.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -90,7 +90,7 @@ private: int numShorteningLevels() const; void updateNumShort(gdioutput &gdi, pCourse crs, pRunner r); - + static void updateStatus(gdioutput &gdi, pRunner r); static void autoGrowCourse(gdioutput &gdi); void loadEconomy(gdioutput &gdi, oRunner &r); diff --git a/code/TabSI.cpp b/code/TabSI.cpp index 68288de..6eb9974 100644 --- a/code/TabSI.cpp +++ b/code/TabSI.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -67,8 +67,7 @@ TabSI::TabSI(oEvent *poe):TabBase(poe), activeSIC(ConvertedTimeStatus::Unknown) lastClubId=0; lastClassId=0; - logger = 0; - + minRunnerId = 0; inputId = 0; printErrorShown = false; @@ -77,9 +76,6 @@ TabSI::TabSI(oEvent *poe):TabBase(poe), activeSIC(ConvertedTimeStatus::Unknown) TabSI::~TabSI(void) { - if (logger!=0) - delete logger; - logger = 0; } @@ -93,8 +89,8 @@ static void entryTips(gdioutput &gdi) { void TabSI::logCard(gdioutput &gdi, const SICard &card) { - if (logger == 0) { - logger = new csvparser; + if (!logger) { + logger = make_shared(); wstring readlog = L"sireadlog_" + getLocalTimeFileName() + L".csv"; wchar_t file[260]; wstring subfolder = makeValidFileName(oe->getName(), true); @@ -124,6 +120,20 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) if (type == GUI_BUTTON) { ButtonInfo bi = *(ButtonInfo *)data; + if (bi.id == "LockFunction") { + lockedFunction = true; + loadPage(gdi); + } + else if (bi.id == "UnlockFunction") { + lockedFunction = false; + loadPage(gdi); + } + else if (bi.id == "AllowStart") + allowStart = gdi.isChecked(bi.id); + else if (bi.id == "AllowControl") + allowControl = gdi.isChecked(bi.id); + else if (bi.id == "AllowFinish") + allowFinish = gdi.isChecked(bi.id); if (bi.id == "ClearMemory") { if (gdi.ask(L"Do you want to clear the card memory?")) { savedCards.clear(); @@ -675,7 +685,9 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) int year = 0; pRunner r = gEvent->addRunner(gdi.getText("Runners"), club, classes[0]->getId(), activeSIC.CardNumber, year, true); - + if (oe->isHiredCard(activeSIC.CardNumber)) { + r->getDI().setInt("CardFee", oe->getBaseCardFee()); + } gdi.setData("RunnerId", r->getId()); gdi.restore(); @@ -742,6 +754,10 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) int year = 0; pRunner r = gEvent->addRunner(gdi.getText("Runners"), club, lbi.data, activeSIC.CardNumber, year, true); + + if (activeSIC.CardNumber > 0 && oe->isHiredCard(activeSIC.CardNumber)) { + r->getDI().setInt("CardFee", oe->getBaseCardFee()); + } r->setStartTimeS(gdi.getText("StartTime")); r->setCardNo(activeSIC.CardNumber, false); @@ -777,7 +793,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) oe->getClass(1)->getNumRunners(false, false, false) == 0) { pclass = oe->getClass(1); pclass->setType(L""); - pclass->setName(gdi.getText("ClassName")); + pclass->setName(gdi.getText("ClassName"), true); pc = pclass->getCourse(); if (pc) pc->setName(gdi.getText("ClassName")); @@ -798,6 +814,9 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) pRunner r = gEvent->addRunner(gdi.getText("Runners"), gdi.getText("Club", true), pclass->getId(), activeSIC.CardNumber, year, true); + if (activeSIC.CardNumber > 0 && oe->isHiredCard(activeSIC.CardNumber)) { + r->getDI().setInt("CardFee", oe->getBaseCardFee()); + } r->setStartTimeS(gdi.getText("StartTime")); r->setCardNo(activeSIC.CardNumber, false); gdi.restore(); @@ -925,7 +944,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) di.setString("Phone", gdi.getText("Phone")); - r->setFlag(oRunner::FlagTransferSpecified, gdi.hasField("AllStages")); + r->setFlag(oRunner::FlagTransferSpecified, gdi.hasWidget("AllStages")); r->setFlag(oRunner::FlagTransferNew, gdi.isChecked("AllStages")); r->setStartTimeS(gdi.getText("StartTime")); @@ -943,7 +962,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) } } else { - r->setBib(bibIn, 0, false, false); + r->setBib(bibIn, 0, false); bib = L", " + lang.tl(L"Nummerlapp: ") + r->getBib(); } r->synchronize(); @@ -1178,7 +1197,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) pRunner r = gEvent->getRunner(bi.data, 0); if (r) { gdi.setData("RunnerId", bi.data); - if (gdi.hasField("Club")) + if (gdi.hasWidget("Club")) gdi.setText("Club", r->getClub()); gdi.setText("FindMatch", lang.tl("Press Enter to continue"), true); } @@ -1203,7 +1222,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) gdi.restore("SIPageLoaded"); mode = SIMode(bi.data); gdi.setInputStatus("StartInfo", mode == ModeEntry); - + if (mode==ModeAssignCards || mode==ModeEntry) { if (mode==ModeAssignCards) { gdi.dropLine(1); @@ -1231,6 +1250,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) showManualInput(gdi); } else if (mode == ModeCardData) { + numSavedCardsOnCmpOpen = savedCards.size(); showModeCardData(gdi); } else if (mode == ModeCheckCards) { @@ -1239,6 +1259,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) else if (mode == ModeRegisterCards) { showRegisterHiredCards(gdi); } + updateReadoutFunction(gdi); gdi.refresh(); } else if (bi.id=="Fee") { @@ -1282,7 +1303,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) if (!db_r && lastClubId) db_r = oe->dbLookUpByName(bi.text, lastClubId, 0, 0); - if (db_r && gdi.hasField("Club")) { + if (db_r && gdi.hasWidget("Club")) { gdi.setText("Club", db_r->getClub()); } } @@ -1325,7 +1346,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) gdi.setInputFocus("OK1"); gdi.setText("Runners", r->getName()); gdi.setData("RunnerId", runnerMatchedId); - if (gdi.hasField("Club")) + if (gdi.hasWidget("Club")) gdi.setText("Club", r->getClub()); inputId = -1; gdi.setText("FindMatch", lang.tl("Press Enter to continue"), true); @@ -1530,7 +1551,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) storedInfo.clear(); storedInfo.storedName = gdi.getText("Name"); storedInfo.storedCardNo = gdi.getText("CardNo"); - storedInfo.storedClub = gdi.hasField("Club") ? gdi.getText("Club") : L""; + storedInfo.storedClub = gdi.hasWidget("Club") ? gdi.getText("Club") : L""; storedInfo.storedFee = gdi.getText("Fee", true); ListBoxInfo lbi; @@ -1542,7 +1563,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) storedInfo.allStages = gdi.isChecked("AllStages"); storedInfo.rentState = gdi.isChecked("RentCard"); storedInfo.hasPaid = gdi.isChecked("Paid"); - storedInfo.payMode = gdi.hasField("PayMode") ? gdi.getSelectedItem("PayMode").first : 0; + storedInfo.payMode = gdi.hasWidget("PayMode") ? gdi.getSelectedItem("PayMode").first : 0; } return 1; } @@ -1748,18 +1769,38 @@ bool TabSI::loadPage(gdioutput &gdi) { if (!oe->empty()) { gdi.setCX(xb + gdi.scaleLength(10)); gdi.setCY(yb + gdi.scaleLength(10)); - gdi.addString("", fontMediumPlus, "Funktion:"); - gdi.addSelection("ReadType", 200, 200, SportIdentCB); - gdi.addItem("ReadType", lang.tl("Avläsning/radiotider"), ModeReadOut); - gdi.addItem("ReadType", lang.tl("Tilldela hyrbrickor"), ModeAssignCards); - gdi.addItem("ReadType", lang.tl("Avstämning hyrbrickor"), ModeCheckCards); - gdi.addItem("ReadType", lang.tl("Registrera hyrbrickor"), ModeRegisterCards); - gdi.addItem("ReadType", lang.tl("Anmälningsläge"), ModeEntry); - gdi.addItem("ReadType", lang.tl("Print card data"), ModeCardData); + + if (!lockedFunction) { + gdi.addString("", fontMediumPlus, "Funktion:"); + gdi.addSelection("ReadType", 200, 200, SportIdentCB); + gdi.addItem("ReadType", lang.tl("Avläsning/radiotider"), ModeReadOut); + gdi.addItem("ReadType", lang.tl("Tilldela hyrbrickor"), ModeAssignCards); + gdi.addItem("ReadType", lang.tl("Avstämning hyrbrickor"), ModeCheckCards); + gdi.addItem("ReadType", lang.tl("Registrera hyrbrickor"), ModeRegisterCards); + gdi.addItem("ReadType", lang.tl("Anmälningsläge"), ModeEntry); + gdi.addItem("ReadType", lang.tl("Print card data"), ModeCardData); + gdi.selectItemByData("ReadType", mode); - gdi.selectItemByData("ReadType", mode); - gdi.dropLine(2.5); - gdi.setCX(xb + gdi.scaleLength(10)); + gdi.dropLine(-0.1); + gdi.addButton("LockFunction", "LÃ¥s funktion...", SportIdentCB); + readoutFunctionX = gdi.getCX(); + readoutFunctionY = gdi.getCY(); + gdi.dropLine(0.3); + + gdi.addString("Allow", 0, "TillÃ¥t:"); + gdi.addCheckbox("AllowStart", "Start", SportIdentCB, allowStart); + gdi.addCheckbox("AllowControl", "Radio", SportIdentCB, allowControl); + gdi.addCheckbox("AllowFinish", "MÃ¥l", SportIdentCB, allowFinish); + + updateReadoutFunction(gdi); + + gdi.dropLine(2.5); + gdi.setCX(xb + gdi.scaleLength(10)); + } + else { + gdi.addButton("UnlockFunction", "LÃ¥s upp", SportIdentCB); + gdi.dropLine(0.2); + } } else { mode = ModeCardData; @@ -1831,6 +1872,14 @@ bool TabSI::loadPage(gdioutput &gdi) { return true; } +void TabSI::updateReadoutFunction(gdioutput &gdi) { + bool hide = mode != SIMode::ModeReadOut; + gdi.hideWidget("Allow", hide); + gdi.hideWidget("AllowStart", hide); + gdi.hideWidget("AllowControl", hide); + gdi.hideWidget("AllowFinish", hide); +} + void InsertSICard(gdioutput &gdi, SICard &sic) { TabSI &tsi = dynamic_cast(*gdi.getTabs().get(TSITab)); @@ -1856,6 +1905,10 @@ pRunner TabSI::autoMatch(const SICard &sic, pRunner db_r) if (classes.size()==1 && dist>=-1 && dist<=1) { //Almost perfect match found. Assume it is it! r = gEvent->addRunnerFromDB(db_r, classes[0]->getId(), true); r->setCardNo(sic.CardNumber, false); + + if (oe->isHiredCard(sic.CardNumber)) { + r->getDI().setInt("CardFee", oe->getBaseCardFee()); + } } else r=0; //Do not assume too much... } @@ -1968,6 +2021,11 @@ void TabSI::insertSICardAux(gdioutput &gdi, SICard &sic) else sic.analyseHour12Time(zt2); } + + // Write read card to log + logCard(gdi, sic); + + bool first = savedCards.size() == numSavedCardsOnCmpOpen; savedCards.push_back(make_pair(savedCardUniqueId++, sic)); if (printSplits) { @@ -1980,11 +2038,15 @@ void TabSI::insertSICardAux(gdioutput &gdi, SICard &sic) if (pageLoaded) { gdi.enableInput("CreateCompetition", true); - printCard(gdi, savedCards.back().first, false); + printCard(gdi, savedCards.back().first, nullptr, false); gdi.dropLine(); gdi.refreshFast(); gdi.scrollToBottom(); } + + if (first && !oe->empty()) + gdi.alert(L"warn:printmodeonly"); + return; } gEvent->synchronizeList({ oListId::oLCardId, oListId::oLRunnerId }); @@ -2209,7 +2271,7 @@ void TabSI::startInteractive(gdioutput &gdi, const SICard &sic, pRunner r, pRunn if (db_r) gdi.setText("Club", db_r->getClub()); //Data from DB } - if (gdi.getText("Runners").empty() || !gdi.hasField("Club")) + if (gdi.getText("Runners").empty() || !gdi.hasWidget("Club")) gdi.setInputFocus("Runners"); else gdi.setInputFocus("Club"); @@ -2444,7 +2506,7 @@ bool TabSI::processCard(gdioutput &gdi, pRunner runner, const SICard &csic, bool if (pcourse) pcourse->synchronize(); else if (pclass && pclass->hasDirectResult()) - runner->setStatus(StatusOK, true, false, false); + runner->setStatus(StatusOK, true, oBase::ChangeType::Update, false); //silent=true; SICard sic(csic); wstring info, warnings, cardno; @@ -2502,20 +2564,21 @@ bool TabSI::processCard(gdioutput &gdi, pRunner runner, const SICard &csic, bool runner->setCard(0); if (csic.statusOK) { - runner->setStatus(StatusOK, true, false); + runner->setStatus(StatusOK, true, oBase::ChangeType::Update); runner->setFinishTime(csic.relativeFinishTime); } else if (csic.statusDNF) { - runner->setStatus(StatusDNF, true, false); + runner->setStatus(StatusDNF, true, oBase::ChangeType::Update); runner->setFinishTime(0); } else { - runner->setStatus(StatusMP, true, false); + runner->setStatus(StatusMP, true, oBase::ChangeType::Update); runner->setFinishTime(csic.relativeFinishTime); } cardno = makeDash(L"-"); - runner->evaluateCard(true, MP, false, false); + runner->evaluateCard(true, MP, 0, oBase::ChangeType::Update); + runner->synchronizeAll(true); runner->hasManuallyUpdatedTimeStatus(); } @@ -2531,13 +2594,14 @@ bool TabSI::processCard(gdioutput &gdi, pRunner runner, const SICard &csic, bool if (!warnings.empty()) rc.bottom+=gdi.getLineHeight(); - if (runner->getStatus()==StatusOK) { - set clsSet; - if (runner->getClassId(false)) - clsSet.insert(runner->getClassId(true)); - gEvent->calculateResults(clsSet, oEvent::ResultType::ClassResult); - if (runner->getTeam()) - gEvent->calculateTeamResults(runner->getLegNumber(), false); + set clsSet; + if (runner->getClassId(false)) + clsSet.insert(runner->getClassId(true)); + gEvent->calculateResults(clsSet, oEvent::ResultType::ClassResult); + if (runner->getTeam()) + gEvent->calculateTeamResults(clsSet, oEvent::ResultType::ClassResult); + + if (runner->getStatusComputed()==StatusOK || isPossibleResultStatus(runner->getStatusComputed())) { bool qfClass = runner->getClassId(false) != runner->getClassId(true); wstring placeS = (runner->getTeam() && !qfClass) ? runner->getTeam()->getLegPlaceS(runner->getLegNumber(), false) : @@ -2553,27 +2617,27 @@ bool TabSI::processCard(gdioutput &gdi, pRunner runner, const SICard &csic, bool gdi.addStringUT(rc.top+6+2*lh, rc.left+20, 0, warnings); wstring statusline = lang.tl(L"Status OK, ") + - lang.tl(L"Tid: ") + runner->getRunningTimeS() + - lang.tl(L", Prel. placering: ") + placeS; - + lang.tl(L"Tid: ") + runner->getRunningTimeS(true); + if (!placeS.empty()) + statusline += lang.tl(L", Prel. placering: ") + placeS; statusline += lang.tl(L", Prel. bomtid: ") + runner->getMissedTimeS(); gdi.addStringUT(rc.top+6+lh, rc.left+20, 0, statusline); - if (runner->isHiredCard()) + if (runner->isHiredCard() || oe->isHiredCard(sic.CardNumber)) rentCardInfo(gdi, rc.right-rc.left); gdi.scrollToBottom(); } else { wstring msg = L"#" + runner->getName() + L" (" + cardno + L")\n"+ runner->getClub() + L". " + runner->getClass(true) + - L"\n" + lang.tl("Tid: ") + runner->getRunningTimeS() + lang.tl(L", Plats ") + placeS; + L"\n" + lang.tl("Tid: ") + runner->getRunningTimeS(true) + lang.tl(L", Plats ") + placeS; gdi.addInfoBox("SIINFO", msg, 10000); } } else { - wstring msg=lang.tl(L"Status: ") + runner->getStatusS(true); + wstring msg=lang.tl(L"Status: ") + runner->getStatusS(true, true); if (!MP.empty()) { msg=msg + L", ("; @@ -2625,20 +2689,32 @@ bool TabSI::processCard(gdioutput &gdi, pRunner runner, const SICard &csic, bool void TabSI::processPunchOnly(gdioutput &gdi, const SICard &csic) { - SICard sic=csic; + SICard sic = csic; DWORD loaded; gEvent->convertTimes(nullptr, sic); - oFreePunch *ofp=0; - - if (sic.nPunch==1) - ofp=gEvent->addFreePunch(sic.Punch[0].Time, sic.Punch[0].Code, sic.CardNumber, true); - else if (sic.FinishPunch.Time > 0) - ofp=gEvent->addFreePunch(sic.FinishPunch.Time, oPunch::PunchFinish, sic.CardNumber, true); - else if (sic.StartPunch.Time > 0) - ofp=gEvent->addFreePunch(sic.StartPunch.Time, oPunch::PunchStart, sic.CardNumber, true); - else - ofp=gEvent->addFreePunch(sic.CheckPunch.Time, oPunch::PunchCheck, sic.CardNumber, true); - + oFreePunch *ofp = 0; + wstring accessError; + if (sic.nPunch == 1) { + if (allowControl) + ofp = gEvent->addFreePunch(sic.Punch[0].Time, sic.Punch[0].Code, sic.CardNumber, true); + else + accessError = L"Radio tillÃ¥ts inte (X)#" + itow(sic.CardNumber); + } + else if (sic.FinishPunch.Time > 0) { + if (allowFinish) + ofp = gEvent->addFreePunch(sic.FinishPunch.Time, oPunch::PunchFinish, sic.CardNumber, true); + else + accessError = L"MÃ¥lstämpling tillÃ¥ts inte (X)#" + itow(sic.CardNumber); + } + else if (sic.StartPunch.Time > 0) { + if (allowStart) + ofp = gEvent->addFreePunch(sic.StartPunch.Time, oPunch::PunchStart, sic.CardNumber, true); + else + accessError = L"Startstämpling tillÃ¥ts inte (X)#" + itow(sic.CardNumber); + } + else { + ofp = gEvent->addFreePunch(sic.CheckPunch.Time, oPunch::PunchCheck, sic.CardNumber, true); + } if (ofp) { pRunner r = ofp->getTiedRunner(); if (gdi.getData("SIPageLoaded", loaded)){ @@ -2650,7 +2726,7 @@ void TabSI::processPunchOnly(gdioutput &gdi, const SICard &csic) gdi.dropLine(); } else { - wstring str=L"SI " + itow(sic.CardNumber) + lang.tl(" (okänd) stämplade vid ") + ofp->getSimpleString(); + wstring str= itow(sic.CardNumber) + lang.tl(" (okänd) stämplade vid ") + ofp->getSimpleString(); gdi.addStringUT(0, str); gdi.dropLine(0.3); } @@ -2659,14 +2735,21 @@ void TabSI::processPunchOnly(gdioutput &gdi, const SICard &csic) tabForceSync(gdi, gEvent); gdi.makeEvent("DataUpdate", "sireadout", r ? r->getId() : 0, 0, true); - + } + else if (!accessError.empty()) { + if (gdi.getData("SIPageLoaded", loaded)) { + gdi.addString("", 0, accessError).setColor(colorDarkRed); + gdi.dropLine(0.3); + gdi.scrollToBottom(); + } + else + gdi.addInfoBox("Access", accessError); } checkMoreCardsInQueue(gdi); return; } - void TabSI::entryCard(gdioutput &gdi, const SICard &sic) { gdi.setText("CardNo", sic.CardNumber); @@ -2692,10 +2775,10 @@ void TabSI::entryCard(gdioutput &gdi, const SICard &sic) name=wstring(sic.lastName) + L", " + wstring(sic.firstName); gdi.setText("Name", name); - if (gdi.hasField("Club") && !club.empty()) + if (gdi.hasWidget("Club") && !club.empty()) gdi.setText("Club", club); - if (club.empty() && gdi.hasField("Club")) + if (club.empty() && gdi.hasWidget("Club")) gdi.setInputFocus("Club"); else if (name.empty()) gdi.setInputFocus("Name"); @@ -2863,27 +2946,27 @@ void TabSI::generateEntryLine(gdioutput &gdi, pRunner r) { gdi.setText("CardNo", r->getCardNo()); gdi.setText("Name", r->getNameRaw()); - if (gdi.hasField("Club")) { + if (gdi.hasWidget("Club")) { gdi.selectItemByData("Club", r->getClubId()); } gdi.selectItemByData("Class", r->getClassId(true)); oDataConstInterface dci = r->getDCI(); - if (gdi.hasField("Fee")) + if (gdi.hasWidget("Fee")) gdi.setText("Fee", oe->formatCurrency(dci.getInt("Fee"))); gdi.setText("Phone", dci.getString("Phone")); gdi.setText("Bib", r->getBib()); gdi.check("RentCard", dci.getInt("CardFee") != 0); - if (gdi.hasField("Paid")) + if (gdi.hasWidget("Paid")) gdi.check("Paid", dci.getInt("Paid")>0); - else if (gdi.hasField("PayMode")) { + else if (gdi.hasWidget("PayMode")) { int paidId = dci.getInt("Paid") > 0 ? r->getPaymentMode() : 1000; gdi.selectItemByData("PayMode", paidId); } - if (gdi.hasField("AllStages")) { + if (gdi.hasWidget("AllStages")) { gdi.check("AllStages", r->hasFlag(oRunner::FlagTransferNew)); } } @@ -2926,7 +3009,7 @@ void TabSI::updateEntryInfo(gdioutput &gdi) wstring method; if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Economy)) { bool invoice = true; - if (gdi.hasField("PayMode")) { + if (gdi.hasWidget("PayMode")) { invoice = gdi.getSelectedItem("PayMode").first == 1000; } else @@ -2956,7 +3039,7 @@ void TabSI::generateSplits(const pRunner r, gdioutput &gdi) else { gdioutput gdiprint(2.0, gdi.getHWNDTarget(), splitPrinter); vector mp; - r->evaluateCard(true, mp); + r->evaluateCard(true, mp, 0, oBase::ChangeType::Quiet); r->printSplits(gdiprint); printProtected(gdi, gdiprint); //gdiprint.print(splitPrinter, oe, false, true); @@ -3217,7 +3300,7 @@ void TabSI::showModeCardData(gdioutput &gdi) { gdi.dropLine(3); gdi.popX(); bool first = true; - for (list >::iterator it = savedCards.begin(); it != savedCards.end(); ++it) { + for (auto it = savedCards.begin(); it != savedCards.end(); ++it) { gdi.dropLine(0.5); if (!first) { RECT rc = {30, gdi.getCY(), gdi.scaleLength(250), gdi.getCY() + 3}; @@ -3225,7 +3308,7 @@ void TabSI::showModeCardData(gdioutput &gdi) { } first = false; - printCard(gdi, it->first, false); + printCard(gdi, it->first, &it->second, false); } } @@ -3235,10 +3318,10 @@ void TabSI::EditCardData::handle(gdioutput &gdi, BaseInfo &info, GuiEventType ty int cardId = ti.getExtraInt(); SICard &card = tabSI->getCard(cardId); ti.id = "card" + itos(cardId); - gdi.removeControl("CardName"); - gdi.removeControl("ClubName"); - gdi.removeControl("OKCard"); - gdi.removeControl("CancelCard"); + gdi.removeWidget("CardName"); + gdi.removeWidget("ClubName"); + gdi.removeWidget("OKCard"); + gdi.removeWidget("CancelCard"); wstring name, club; if (card.firstName[0]) @@ -3283,10 +3366,10 @@ void TabSI::EditCardData::handle(gdioutput &gdi, BaseInfo &info, GuiEventType ty gdi.setText("card" + itos(cardId), s, true); } - gdi.removeControl("CardName"); - gdi.removeControl("ClubName"); - gdi.removeControl("OKCard"); - gdi.removeControl("CancelCard"); + gdi.removeWidget("CardName"); + gdi.removeWidget("ClubName"); + gdi.removeWidget("OKCard"); + gdi.removeWidget("CancelCard"); } else if (type == GUI_FOCUS) { InputInfo &ii = dynamic_cast(info); @@ -3297,8 +3380,11 @@ void TabSI::EditCardData::handle(gdioutput &gdi, BaseInfo &info, GuiEventType ty } } -void TabSI::printCard(gdioutput &gdi, int cardId, bool forPrinter) const { - SICard &c = getCard(cardId); +void TabSI::printCard(gdioutput &gdi, int cardId, SICard *crdRef, bool forPrinter) const { + if (crdRef == nullptr) + crdRef = &getCard(cardId); + + SICard &c = *crdRef; if (c.readOutTime[0] == 0) strcpy_s(c.readOutTime, getLocalTimeN().c_str()); @@ -3421,7 +3507,7 @@ int TabSI::analyzePunch(SIPunch &p, int &start, int &accTime, int &days) { void TabSI::generateSplits(int cardId, gdioutput &gdi) { gdioutput gdiprint(2.0, gdi.getHWNDTarget(), splitPrinter); - printCard(gdiprint, cardId, true); + printCard(gdiprint, cardId, nullptr, true); printProtected(gdi, gdiprint); } @@ -3511,7 +3597,7 @@ void TabSI::createCompetitionFromCards(gdioutput &gdi) { if (zeroTime < 0) zeroTime += 3600 * 24; zeroTime -= zeroTime % 1800; - oe->setZeroTime(formatTime(zeroTime)); + oe->setZeroTime(formatTime(zeroTime), false); // Add competitors for (size_t k = 0; k < cards.size(); k++) { @@ -3578,6 +3664,17 @@ void TabSI::clearCompetitionData() { checkedCardFlags.clear(); currentAssignIndex = 0; warnedClassOutOfMaps.clear(); + + lockedFunction = false; + allowControl = true; + allowFinish = true; + allowStart = false; + + if (mode == ModeCardData) + mode = SIMode::ModeReadOut; + + logger.reset(); + numSavedCardsOnCmpOpen = savedCards.size(); } SICard &TabSI::getCard(int id) const { @@ -3761,7 +3858,7 @@ void TabSI::showCheckCardStatus(gdioutput &gdi, const string &cmd) { wstring cp = r[k]->getCompleteIdentification(); if (r[k]->getStatus() != StatusUnknown) - cp += L" " + r[k]->getStatusS(true); + cp += L" " + r[k]->getStatusS(true, true); else cp += makeDash(L" -"); @@ -3889,7 +3986,7 @@ bool TabSI::writePayMode(gdioutput &gdi, int amount, oRunner &r) { int paid = 0; bool hasPaid = false; - if (gdi.hasField("PayMode")) + if (gdi.hasWidget("PayMode")) hasPaid = gdi.getSelectedItem("PayMode").first != 1000; bool fixPay = gdi.isChecked("Paid"); @@ -3928,7 +4025,7 @@ bool TabSI::checkpPrintQueue(gdioutput &gdi) { printPunchRunnerIdQueue.pop_front(); pRunner r = oe->getRunner(rid, 0); if (r) { - r->evaluateCard(true, mp); + r->evaluateCard(true, mp, 0, oBase::ChangeType::Quiet); r->printSplits(gdiprint); } gdiprint.dropLine(4); @@ -3950,7 +4047,7 @@ void TabSI::printSIInfo(gdioutput &gdi, const wstring &port) const { oClub *TabSI::extractClub(gdioutput &gdi) const { auto &db = oe->getRunnerDatabase(); oClub *dbClub = nullptr; - if (gdi.hasField("Club")) { + if (gdi.hasWidget("Club")) { int clubId = gdi.getExtraInt("Club"); if (clubId > 0) { dbClub = db.getClub(clubId-1); @@ -4128,14 +4225,14 @@ void TabSI::handleAutoComplete(gdioutput &gdi, AutoCompleteInfo &info) { } } if (r) { - if (gdi.hasField("Club") && r->dbe().clubNo) { + if (gdi.hasWidget("Club") && r->dbe().clubNo) { if (gdi.getText("Club").empty()) { auto pclub = oe->getRunnerDatabase().getClub(r->dbe().clubNo); if (pclub) gdi.setText("Club", pclub->getName()); } } - if (gdi.hasField("CardNo") && r->dbe().cardNo) { + if (gdi.hasWidget("CardNo") && r->dbe().cardNo) { if (gdi.getText("CardNo").empty()) gdi.setText("CardNo", r->dbe().cardNo); } diff --git a/code/TabSI.h b/code/TabSI.h index 0f58a4d..9f8a299 100644 --- a/code/TabSI.h +++ b/code/TabSI.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -81,8 +81,13 @@ private: bool printErrorShown; void printProtected(gdioutput &gdi, gdioutput &gdiprint); - //Interactive card assign + //Operation mode SIMode mode; + bool lockedFunction = false; + bool allowControl = true; + bool allowFinish = true; + bool allowStart = false; + int currentAssignIndex; void printSIInfo(gdioutput &gdi, const wstring &port) const; @@ -96,7 +101,7 @@ private: int lastClubId; wstring lastFee; int inputId; - + int numSavedCardsOnCmpOpen = 0; void showCheckCardStatus(gdioutput &gdi, const string &cmd); void showRegisterHiredCards(gdioutput &gdi); @@ -142,7 +147,7 @@ private: void generateSplits(const pRunner r, gdioutput &gdi); int logcounter; - csvparser *logger; + shared_ptr logger; string insertCardNumberField; @@ -159,7 +164,7 @@ private: void showModeCardData(gdioutput &gdi); - void printCard(gdioutput &gdi, int cardId, bool forPrinter) const; + void printCard(gdioutput &gdi, int cardId, SICard *crdRef, bool forPrinter) const; void generateSplits(int cardId, gdioutput &gdi); static int analyzePunch(SIPunch &p, int &start, int &accTime, int &days); @@ -197,6 +202,10 @@ private: oClub *extractClub(gdioutput &gdi) const; RunnerWDBEntry *extractRunner(gdioutput &gdi) const; + void updateReadoutFunction(gdioutput &gdi); + int readoutFunctionX = 0; + int readoutFunctionY = 0; + protected: void clearCompetitionData(); diff --git a/code/TabSpeaker.cpp b/code/TabSpeaker.cpp index b41a9c1..40d3638 100644 --- a/code/TabSpeaker.cpp +++ b/code/TabSpeaker.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -214,7 +214,7 @@ int TabSpeaker::processButton(gdioutput &gdi, const ButtonInfo &bu) gdi.clearPage(false); gdi.addButton("Cancel", "Stäng", tabSpeakerCB); gdi.dropLine(); - gdi.addTable(oe->getPunchesTB(), gdi.getCX(), gdi.getCY()); + gdi.addTable(oFreePunch::getTable(oe), gdi.getCX(), gdi.getCY()); gdi.refresh(); } else if (bu.id == "Report") { @@ -745,7 +745,7 @@ void TabSpeaker::generateControlList(gdioutput &gdi, int classId) return; bool keepLegs = false; - if (gdi.hasField("Leg")) { + if (gdi.hasWidget("Leg")) { DWORD clsSel = 0; if (gdi.getData("ClassSelection", clsSel) && clsSel == pc->getId()) { gdi.restore("LegSelection", true); @@ -1125,17 +1125,17 @@ bool TabSpeaker::loadPage(gdioutput &gdi) { if (classId == -1) { string btn = "Events"; - if (gdi.hasField(btn)) + if (gdi.hasWidget(btn)) gdi.sendCtrlMessage(btn); } else if (classId == -2) { string btn = "Report"; - if (gdi.hasField(btn)) + if (gdi.hasWidget(btn)) gdi.sendCtrlMessage(btn); } else if (classId > 0) { string btn = "cid" + itos(classId); - if (gdi.hasField(btn)) + if (gdi.hasWidget(btn)) gdi.sendCtrlMessage(btn); } @@ -1265,7 +1265,7 @@ void TabSpeaker::savePriorityClass(gdioutput &gdi) { pRunner r = oe->getRunner(runnersToSet[k], 0); if (r) { int id = runnersToSet[k]; - if (!gdi.hasField("A" + itos(id))) { + if (!gdi.hasWidget("A" + itos(id))) { runnersToSet.clear(); //Page not loaded. Abort. return; } diff --git a/code/TabSpeaker.h b/code/TabSpeaker.h index 433d9bc..ab66693 100644 --- a/code/TabSpeaker.h +++ b/code/TabSpeaker.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/TabTeam.cpp b/code/TabTeam.cpp index 804918c..36f6cb9 100644 --- a/code/TabTeam.cpp +++ b/code/TabTeam.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -179,7 +179,7 @@ void TabTeam::selectTeam(gdioutput &gdi, pTeam t) { if (t){ t->synchronize(); - t->evaluate(false); + t->evaluate(oBase::ChangeType::Quiet); teamId=t->getId(); @@ -192,7 +192,7 @@ void TabTeam::selectTeam(gdioutput &gdi, pTeam t) gdi.selectItemByData("RClass", t->getClassId(false)); gdi.selectItemByData("Teams", t->getId()); - if (gdi.hasField("StatusIn")) { + if (gdi.hasWidget("StatusIn")) { gdi.selectItemByData("StatusIn", t->getInputStatus()); int ip = t->getInputPlace(); if (ip > 0) @@ -201,11 +201,11 @@ void TabTeam::selectTeam(gdioutput &gdi, pTeam t) gdi.setText("PlaceIn", makeDash(L"-")); gdi.setText("TimeIn", t->getInputTimeS()); - if (gdi.hasField("PointIn")) + if (gdi.hasWidget("PointIn")) gdi.setText("PointIn", t->getInputPoints()); } - if (gdi.hasField("NoRestart")) { + if (gdi.hasWidget("NoRestart")) { gdi.check("NoRestart", t->preventRestart()); } @@ -224,11 +224,11 @@ void TabTeam::selectTeam(gdioutput &gdi, pTeam t) gdi.selectItemByData("Teams", -1); - if (gdi.hasField("StatusIn")) { + if (gdi.hasWidget("StatusIn")) { gdi.selectFirstItem("StatusIn"); gdi.setText("PlaceIn", L""); gdi.setText("TimeIn", makeDash(L"-")); - if (gdi.hasField("PointIn")) + if (gdi.hasWidget("PointIn")) gdi.setText("PointIn", L""); } @@ -243,11 +243,11 @@ void TabTeam::updateTeamStatus(gdioutput &gdi, pTeam t) { if (!t) { gdi.setText("Name", L""); - if (gdi.hasField("StartNo")) + if (gdi.hasWidget("StartNo")) gdi.setText("StartNo", L""); - if (gdi.hasField("Club")) + if (gdi.hasWidget("Club")) gdi.setText("Club", L""); - bool hasFee = gdi.hasField("Fee"); + bool hasFee = gdi.hasWidget("Fee"); if (hasFee) { gdi.setText("Fee", L""); } @@ -262,19 +262,19 @@ void TabTeam::updateTeamStatus(gdioutput &gdi, pTeam t) } gdi.setText("Name", t->getName()); - if (gdi.hasField("StartNo")) + if (gdi.hasWidget("StartNo")) gdi.setText("StartNo", t->getBib()); - if (gdi.hasField("Club")) + if (gdi.hasWidget("Club")) gdi.setText("Club", t->getClub()); - bool hasFee = gdi.hasField("Fee"); + bool hasFee = gdi.hasWidget("Fee"); if (hasFee) { gdi.setText("Fee", oe->formatCurrency(t->getDI().getInt("Fee"))); } gdi.setText("Start", t->getStartTimeS()); gdi.setText("Finish",t->getFinishTimeS()); - gdi.setText("Time", t->getRunningTimeS()); + gdi.setText("Time", t->getRunningTimeS(true)); gdi.setText("TimeAdjust", getTimeMS(t->getTimeAdjustment())); gdi.setText("PointAdjust", -t->getPointAdjustment()); gdi.selectItemByData("Status", t->getStatus()); @@ -305,13 +305,13 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) { bool bibModified = false; if (t) { t->setName(name, true); - if (gdi.hasField("StartNo")) { + if (gdi.hasWidget("StartNo")) { const wstring &bib = gdi.getText("StartNo"); if (bib != t->getBib()) { bibModified = true; wchar_t pat[32]; int no = oClass::extractBibPattern(bib, pat); - t->setBib(bib, no, no > 0, false); + t->setBib(bib, no, no > 0); } } wstring start = gdi.getText("Start"); @@ -321,16 +321,16 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) { t->setFinishTimeS(gdi.getText("Finish")); - if (gdi.hasField("Fee")) + if (gdi.hasWidget("Fee")) t->getDI().setInt("Fee", oe->interpretCurrency(gdi.getText("Fee"))); - if (gdi.hasField("NoRestart")) + if (gdi.hasWidget("NoRestart")) t->preventRestart(gdi.isChecked("NoRestart")); - t->apply(false, 0, false); + t->apply(oBase::ChangeType::Quiet, nullptr); - if (gdi.hasField("Club")) { + if (gdi.hasWidget("Club")) { ListBoxInfo lbi; gdi.getSelectedItem("Club", lbi); @@ -351,13 +351,13 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) { RunnerStatus sIn = (RunnerStatus)lbi.data; // Must be done AFTER all runners are set. But setting runner can modify status, so decide here. - bool setDNS = (sIn == StatusDNS || sIn == StatusCANCEL) && (t->getStatus() != sIn); + bool setTeamStatus = (!oAbstractRunner::isResultStatus(sIn) || sIn == StatusOK) && (t->getStatus() != sIn); bool checkStatus = (sIn != t->getStatus()); - if (sIn == StatusUnknown && (t->getStatus() == StatusDNS || t->getStatus() == StatusCANCEL)) - t->setTeamNoStart(false, StatusDNS); + if (sIn == StatusUnknown && !oAbstractRunner::isResultStatus(t->getStatus())) + t->setTeamMemberStatus(StatusUnknown); else if ((RunnerStatus)lbi.data != t->getStatus()) - t->setStatus((RunnerStatus)lbi.data, true, false); + t->setStatus((RunnerStatus)lbi.data, true, oBase::ChangeType::Update); gdi.getSelectedItem("RClass", lbi); @@ -384,27 +384,27 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) { if (pc) { pair snoBib = pc->getNextBib(); if (snoBib.first > 0) { - t->setBib(snoBib.second, snoBib.first, true, false); + t->setBib(snoBib.second, snoBib.first, true); } } } t->setClassId(classId, true); - if (gdi.hasField("TimeAdjust")) { + if (gdi.hasWidget("TimeAdjust")) { int time = convertAbsoluteTimeMS(gdi.getText("TimeAdjust")); if (time != NOTIME) t->setTimeAdjustment(time); } - if (gdi.hasField("PointAdjust")) { + if (gdi.hasWidget("PointAdjust")) { t->setPointAdjustment(-gdi.getTextNo("PointAdjust")); } - if (gdi.hasField("StatusIn") && readStatusIn) { + if (gdi.hasWidget("StatusIn") && readStatusIn) { t->setInputStatus(RunnerStatus(gdi.getSelectedItem("StatusIn").first)); t->setInputPlace(gdi.getTextNo("PlaceIn")); t->setInputTime(gdi.getText("TimeIn")); - if (gdi.hasField("PointIn")) + if (gdi.hasWidget("PointIn")) t->setInputPoints(gdi.getTextNo("PointIn")); } @@ -416,7 +416,7 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) { for (unsigned i=0;igetNumStages(); i++) { char bf[16]; sprintf_s(bf, "R%d", i); - if (!gdi.hasField("SI" + itos(i))) // Skip if field not loaded in page + if (!gdi.hasWidget("SI" + itos(i))) // Skip if field not loaded in page continue; if (pc->getLegRunner(i)==i) { @@ -474,10 +474,10 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) { } } } - } - if (setDNS) - t->setTeamNoStart(true, sIn); + + if (setTeamStatus) + t->setTeamMemberStatus(sIn); if (t->checkValdParSetup()) { gdi.alert("Laguppställningen hade fel, som har rättats"); @@ -486,7 +486,8 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) { if (t->getRunner(0)) t->getRunner(0)->setStartTimeS(start); - t->evaluate(true); + t->applyBibs(); + t->evaluate(oBase::ChangeType::Update); if (globalDep) oe->reEvaluateAll(classes, false); @@ -660,7 +661,7 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data) else { oldR->setClassId(0, true); vector mp; - oldR->evaluateCard(true, mp, 0, true); + oldR->evaluateCard(true, mp, 0, oBase::ChangeType::Update); oldR->synchronize(true); t->setRunner(leg, r, true); t->checkValdParSetup(); @@ -731,17 +732,9 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data) int currentKey = max(newSno-1, 0) % nf; if (currentKey == lbi.data) { - t->setStartNo(newSno, false); - t->apply(false, 0, false); + t->setStartNo(newSno, oBase::ChangeType::Update); t->synchronize(true); - for (int i = 0; i < t->getNumRunners(); i++) { - pRunner r = t->getRunner(i); - if (r) { - vector mp; - r->evaluateCard(true, mp); - r->synchronize(true); - } - } + t->evaluate(oBase::ChangeType::Update); selectTeam(gdi, t); return 0; } @@ -921,11 +914,12 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data) if (pc) { pair snoBib = pc->getNextBib(); if (snoBib.first > 0) { - t->setBib(snoBib.second, snoBib.first, true, false); + t->setBib(snoBib.second, snoBib.first, true); } } t->setClassId(clsId, true); + t->synchronize(); fillTeamList(gdi); //oe->fillTeams(gdi, "Teams"); @@ -993,7 +987,7 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data) if (gdi.isInputChanged("")) { pTeam t = oe->getTeam(teamId); bool newName = t && t->getName() != gdi.getText("Name"); - bool newBib = gdi.hasField("StartNo") && t && t->getBib() != gdi.getText("StartNo"); + bool newBib = gdi.hasWidget("StartNo") && t && t->getBib() != gdi.getText("StartNo"); save(gdi, true); if (newName || newBib) { @@ -1272,13 +1266,13 @@ void TabTeam::loadTeamMembers(gdioutput &gdi, int ClassId, int ClubId, pTeam t) gdi.check("RENT" + itos(i), r->getDCI().getInt("CardFee") != 0); } string sid = "STATUS"+itos(i); - if (r->statusOK()) { - TextInfo * ti = (TextInfo *)gdi.setText(sid, L"OK, " + r->getRunningTimeS(), false); + if (r->statusOK(true)) { + TextInfo * ti = (TextInfo *)gdi.setText(sid, L"OK, " + r->getRunningTimeS(true), false); if (ti) ti->setColor(colorGreen); } - else if (r->getStatus() != StatusUnknown) { - TextInfo * ti = (TextInfo *)gdi.setText(sid, r->getStatusS(false) + L", " + r->getRunningTimeS(), false); + else if (r->getStatusComputed() != StatusUnknown) { + TextInfo * ti = (TextInfo *)gdi.setText(sid, r->getStatusS(false, true) + L", " + r->getRunningTimeS(true), false); if (ti) ti->setColor(colorRed); } @@ -1344,10 +1338,9 @@ bool TabTeam::loadPage(gdioutput &gdi) gdi.clearPage(false); if (currentMode == 1) { - Table *tbl=oe->getTeamsTB(); addToolbar(gdi); gdi.dropLine(1); - gdi.addTable(tbl, gdi.getCX(), gdi.getCY()); + gdi.addTable(oTeam::getTable(oe), gdi.getCX(), gdi.getCY()); return true; } @@ -1790,7 +1783,7 @@ void TabTeam::doAddTeamMembers(gdioutput &gdi) { } } if (ch) { - mt->apply(false, 0, false); + mt->apply(oBase::ChangeType::Update, nullptr); mt->synchronize(); for (int j = 0; j < mt->getNumRunners(); j++) { if (mt->getRunner(j) != 0) { @@ -1905,9 +1898,9 @@ void TabTeam::switchRunners(pTeam t, int leg, pRunner r, pRunner oldR) { int otherLeg = r->getLegNumber(); otherTeam->setRunner(otherLeg, oldR, true); if (oldR) - oldR->evaluateCard(true, mp, 0, true); + oldR->evaluateCard(true, mp, 0, oBase::ChangeType::Update); otherTeam->checkValdParSetup(); - otherTeam->apply(true, 0, false); + otherTeam->apply(oBase::ChangeType::Update, nullptr); otherTeam->synchronize(true); } else if (oldR) { @@ -1918,14 +1911,14 @@ void TabTeam::switchRunners(pTeam t, int leg, pRunner r, pRunner oldR) { else oldR->setClassId(r->getClassId(false), true); removeAnnonumousTeamMember = oldR->isAnnonumousTeamMember(); - oldR->evaluateCard(true, mp, 0, true); + oldR->evaluateCard(true, mp, 0, oBase::ChangeType::Update); oldR->synchronize(true); } t->setRunner(leg, r, true); - r->evaluateCard(true, mp, 0, true); + r->evaluateCard(true, mp, 0, oBase::ChangeType::Update); t->checkValdParSetup(); - t->apply(true, 0, false); + t->apply(oBase::ChangeType::Update, nullptr); t->synchronize(true); if (removeAnnonumousTeamMember) @@ -1981,12 +1974,12 @@ void TabTeam::handleAutoComplete(gdioutput &gdi, AutoCompleteInfo &info) { auto &db = oe->getRunnerDatabase(); auto runner = db.getRunnerByIndex(ix); - if (runner && gdi.hasField("Club") && gdi.getText("Club").empty()) { + if (runner && gdi.hasWidget("Club") && gdi.getText("Club").empty()) { pClub club = db.getClub(runner->dbe().clubNo); if (club) gdi.setText("Club", club->getName()); } - if (runner && runner->dbe().cardNo > 0 && gdi.hasField("DirCard") && gdi.getText("DirCard").empty()) { + if (runner && runner->dbe().cardNo > 0 && gdi.hasWidget("DirCard") && gdi.getText("DirCard").empty()) { gdi.setText("DirCard", runner->dbe().cardNo); } } diff --git a/code/TabTeam.h b/code/TabTeam.h index 231ccaa..a584f90 100644 --- a/code/TabTeam.h +++ b/code/TabTeam.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/Table.cpp b/code/Table.cpp index 364638a..fe4c017 100644 --- a/code/Table.cpp +++ b/code/Table.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -72,7 +72,6 @@ Table::Table(oEvent *oe_, int rowH, partialCell = false; startSelect = false; - ownerCounter = 0; clearCellSelection(0); tableProp = -1; dataPointer = -1; @@ -86,18 +85,10 @@ Table::Table(oEvent *oe_, int rowH, Table::~Table(void) { - assert(ownerCounter == 0); if (hEdit) DestroyWindow(hEdit); } - -void Table::releaseOwnership() { - ownerCounter--; - if (ownerCounter == 0) - delete this; -} - void Table::clearCellSelection(gdioutput *gdi) { upperRow = -1; lowerRow = -1; @@ -111,8 +102,12 @@ void Table::clearCellSelection(gdioutput *gdi) { } int Table::addColumn(const string &Title, int width, bool isnum, bool formatRight) { + return addColumn(lang.tl(Title).c_str(), width, isnum, formatRight); +} + +int Table::addColumn(const wstring &translatedTitle, int width, bool isnum, bool formatRight) { ColInfo ri; - wcscpy_s(ri.name, lang.tl(Title).c_str()); + wcscpy_s(ri.name, translatedTitle.c_str()); ri.baseWidth = width; ri.width = 0; ri.padWidthZeroSort = 0; @@ -121,7 +116,7 @@ int Table::addColumn(const string &Title, int width, bool isnum, bool formatRigh Titles.push_back(ri); columns.push_back(nTitles); nTitles++; - return Titles.size()-1; + return Titles.size() - 1; } int Table::addColumnPaddedSort(const string &title, int width, int padding, bool formatRight) { @@ -440,26 +435,21 @@ void Table::sort(int col) } } -int TablesCB(gdioutput *gdi, int type, void *data) -{ +int TablesCB(gdioutput *gdi, int type, void *data) { if (type!=GUI_LINK || gdi->Tables.empty()) return 0; TableInfo &tai=gdi->Tables.front(); - Table *t=tai.table; + auto &t = tai.table; TextInfo *ti=(TextInfo *)data; - if (ti->id.substr(0,4)=="sort"){ int col=atoi(ti->id.substr(4).c_str()); t->sort(col); } gdi->refresh(); - //gdi->Restore(); - //t->Render(*gdi); -//*/ return 0; } @@ -720,17 +710,26 @@ int tblSelectionCB(gdioutput *gdi, int type, void *data) } void Table::selection(gdioutput &gdi, const wstring &text, int data) { - if (size_t(selectionRow) >= Data.size() || size_t(selectionCol) >= Titles.size()) - throw std::exception("Index out of bounds."); + if (size_t(selectionRow) >= Data.size() || size_t(selectionCol) >= Titles.size()) + throw std::exception("Index out of bounds."); - TableCell &cell = Data[selectionRow].cells[selectionCol]; - int id = Data[selectionRow].id; - if (cell.hasOwner()) - cell.getOwner()->inputData(cell.id, text, data, cell.contents, false); - reloadRow(id); - RECT rc; - getRowRect(selectionRow, rc); - InvalidateRect(gdi.getHWNDTarget(), &rc, false); + TableCell &cell = Data[selectionRow].cells[selectionCol]; + int id = Data[selectionRow].id; + pair res; + if (cell.hasOwner()) + res = cell.getOwner()->inputData(cell.id, text, data, cell.contents, false); + if (res.second) { + update(); + gdi.refresh(); + } + else { + reloadRow(id); + if (res.first) + reloadRow(res.first); + //RECT rc; + //getRowRect(selectionRow, rc); + InvalidateRect(gdi.getHWNDTarget(), nullptr, false); + } } #ifndef MEOSDB @@ -838,7 +837,7 @@ bool Table::destroyEditControl(gdioutput &gdi) { hEdit=0; } } - gdi.removeControl(tId); + gdi.removeWidget(tId); if (drawFilterLabel) { drawFilterLabel=false; @@ -1643,7 +1642,7 @@ bool Table::enter(gdioutput &gdi) } } } - else if (gdi.hasField(tId)) { + else if (gdi.hasWidget(tId)) { ListBoxInfo lbi; gdi.getSelectedItem(tId, lbi); @@ -1662,7 +1661,7 @@ void Table::escape(gdioutput &gdi) DestroyWindow(hEdit); hEdit = 0; } - gdi.removeControl(tId); + gdi.removeWidget(tId); drawFilterLabel=false; gdi.refresh(); } @@ -2279,7 +2278,7 @@ void Table::autoSelectColumns() { for (size_t r = 2; r 1000 /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/animationdata.cpp b/code/animationdata.cpp index 2958d3e..cc0d8d0 100644 --- a/code/animationdata.cpp +++ b/code/animationdata.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software -Copyright (C) 2009-2019 Melin Software HB +Copyright (C) 2009-2020 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 diff --git a/code/animationdata.h b/code/animationdata.h index fc8bba9..d4df7b0 100644 --- a/code/animationdata.h +++ b/code/animationdata.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software -Copyright (C) 2009-2019 Melin Software HB +Copyright (C) 2009-2020 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 diff --git a/code/autocomplete.cpp b/code/autocomplete.cpp index a4625d4..d2c95b1 100644 --- a/code/autocomplete.cpp +++ b/code/autocomplete.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software -Copyright (C) 2009-2019 Melin Software HB +Copyright (C) 2009-2020 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 diff --git a/code/autocomplete.h b/code/autocomplete.h index 0d2adae..8a47993 100644 --- a/code/autocomplete.h +++ b/code/autocomplete.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/autocompletehandler.h b/code/autocompletehandler.h index 8a30ae4..81c4c18 100644 --- a/code/autocompletehandler.h +++ b/code/autocompletehandler.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/autotask.cpp b/code/autotask.cpp index 5349390..de385ae 100644 --- a/code/autotask.cpp +++ b/code/autotask.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/autotask.h b/code/autotask.h index 610f7b1..7189065 100644 --- a/code/autotask.h +++ b/code/autotask.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/classconfiginfo.cpp b/code/classconfiginfo.cpp index 43f220f..02c00fc 100644 --- a/code/classconfiginfo.cpp +++ b/code/classconfiginfo.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -48,11 +48,15 @@ void ClassConfigInfo::clear() { } bool ClassConfigInfo::empty() const { - return individual.empty() && relay.empty() && patrol.empty() && raceNStart.empty(); + return individual.empty() && rogainingClasses.empty() + && relay.empty() && patrol.empty() + && raceNStart.empty() && rogainingTeam.empty(); } -void ClassConfigInfo::getIndividual(set &sel) const { +void ClassConfigInfo::getIndividual(set &sel, bool forStartList) const { sel.insert(individual.begin(), individual.end()); + if (forStartList) + sel.insert(rogainingClasses.begin(), rogainingClasses.end()); } void ClassConfigInfo::getRelay(set &sel) const { @@ -79,6 +83,9 @@ void ClassConfigInfo::getRogaining(set &sel) const { sel.insert(rogainingClasses.begin(), rogainingClasses.end()); } +void ClassConfigInfo::getRogainingTeam(set &sel) const { + sel.insert(rogainingTeam.begin(), rogainingTeam.end()); +} void ClassConfigInfo::getRaceNStart(int race, set &sel) const { if (size_t(race) < raceNStart.size() && !raceNStart[race].empty()) @@ -124,8 +131,6 @@ void oEvent::getClassConfigurationInfo(ClassConfigInfo &cnf) const cnf.maximumLegNumber = max(cnf.maximumLegNumber, it->getNumStages()); ClassType ct = it->getClassType(); - if (it->isRogaining()) - cnf.rogainingClasses.push_back(it->getId()); if (it->getCourse() == 0) cnf.classWithoutCourse.push_back(it->getName()); //MultiCourse not analysed... @@ -187,12 +192,20 @@ void oEvent::getClassConfigurationInfo(ClassConfigInfo &cnf) const cnf.hasMultiCourse = true; } } + if (ct == oClassIndividual) { - cnf.individual.push_back(it->getId()); + if (it->isRogaining()) + cnf.rogainingClasses.push_back(it->getId()); + else + cnf.individual.push_back(it->getId()); + if (cnf.timeStart.empty()) cnf.timeStart.resize(1); cnf.timeStart[0].push_back(it->getId()); } + else if ((ct == oClassPatrol || ct == oClassRelay) && it->isRogaining()) { + cnf.rogainingTeam.push_back(it->getId()); + } else if (ct == oClassPatrol) cnf.patrol.push_back(it->getId()); else if (ct == oClassRelay) { diff --git a/code/classconfiginfo.h b/code/classconfiginfo.h index 96cfdad..b198bed 100644 --- a/code/classconfiginfo.h +++ b/code/classconfiginfo.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -35,6 +35,7 @@ public: vector individual; vector relay; vector patrol; + vector rogainingTeam; vector< vector > legNStart; vector< vector > raceNStart; @@ -65,15 +66,18 @@ public: bool hasRelay() const {return relay.size()>0;} bool hasPatrol() const {return patrol.size()>0;} bool hasRogaining() const {return rogainingClasses.size()>0;} + bool hasRogainingTeam() const { return rogainingTeam.size()>0; } + bool empty() const; // Return true of this is an event in a sequence of events. bool isMultiStageEvent() const {return hasMultiEvent;} - void getIndividual(set &sel) const; + void getIndividual(set &sel, bool forStartList) const; void getRelay(set &sel) const; void getPatrol(set &sel) const; void getTeamClass(set &sel) const; void getRogaining(set &sel) const; + void getRogainingTeam(set &sel) const; bool hasTeamClass() const; diff --git a/code/csvparser.cpp b/code/csvparser.cpp index 09c9d3b..f86ac13 100644 --- a/code/csvparser.cpp +++ b/code/csvparser.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -165,18 +165,18 @@ bool csvparser::importOS_CSV(oEvent &event, const wstring &file) pTeam team=event.addTeam(sp[OSclub] + L" " + sp[OSdesc], ClubId, ClassId); team->setEntrySource(externalSourceId); - team->setStartNo(wtoi(sp[OSstno]), false); + team->setStartNo(wtoi(sp[OSstno]), oBase::ChangeType::Update); if (sp[12].length()>0) - team->setStatus( ConvertOEStatus( wtoi(sp[OSstatus]) ), true, false); + team->setStatus( ConvertOEStatus( wtoi(sp[OSstatus]) ), true, oBase::ChangeType::Update); - team->setStartTime(event.convertAbsoluteTime(sp[OSstart]), true, false); + team->setStartTime(event.convertAbsoluteTime(sp[OSstart]), true, oBase::ChangeType::Update); if (sp[OStime].length()>0) team->setFinishTime( event.convertAbsoluteTime(sp[OSstart])+event.convertAbsoluteTime(sp[OStime])-event.getZeroTimeNum() ); if (team->getStatus()==StatusOK && team->getFinishTime()==0) - team->setStatus(StatusUnknown, true, false); + team->setStatus(StatusUnknown, true, oBase::ChangeType::Update); unsigned rindex=Offset; @@ -204,14 +204,14 @@ bool csvparser::importOS_CSV(oEvent &event, const wstring &file) DI.setInt("CardFee", event.getDCI().getInt("CardFee")); //r->setCardNo(atoi(sp[rindex+OSRcard]), false); - r->setStartTime(event.convertAbsoluteTime(sp[rindex+OSRstart]), true, false); + r->setStartTime(event.convertAbsoluteTime(sp[rindex+OSRstart]), true, oBase::ChangeType::Update); r->setFinishTime( event.convertAbsoluteTime(sp[rindex+OSRfinish]) ); if (sp[rindex+OSRstatus].length()>0) - r->setStatus( ConvertOEStatus( wtoi(sp[rindex+OSRstatus]) ), true, false, false); + r->setStatus( ConvertOEStatus( wtoi(sp[rindex+OSRstatus]) ), true, oBase::ChangeType::Update, false); - if (r->getStatus()==StatusOK && r->getRunningTime()==0) - r->setStatus(StatusUnknown, true, false, false); + if (r->getStatus()==StatusOK && r->getRunningTime(false)==0) + r->setStatus(StatusUnknown, true, oBase::ChangeType::Update, false); r->addClassDefaultFee(false); @@ -225,7 +225,7 @@ bool csvparser::importOS_CSV(oEvent &event, const wstring &file) if (pc && runner>(int)pc->getNumStages()) pc->setNumStages(runner); - team->apply(true, 0, false); + team->evaluate(oBase::ChangeType::Update); } } fin.close(); @@ -324,14 +324,14 @@ bool csvparser::importOE_CSV(oEvent &event, const wstring &file) { pr->setClubId(pclub ? pclub->getId():0); pr->setCardNo( wtoi(sp[OEcard]), false ); - pr->setStartTime(event.convertAbsoluteTime(sp[OEstart]), true, false); + pr->setStartTime(event.convertAbsoluteTime(sp[OEstart]), true, oBase::ChangeType::Update); pr->setFinishTime(event.convertAbsoluteTime(sp[OEfinish])); if (sp[OEstatus].length()>0) - pr->setStatus( ConvertOEStatus( wtoi(sp[OEstatus]) ), true, false); + pr->setStatus( ConvertOEStatus( wtoi(sp[OEstatus]) ), true, oBase::ChangeType::Update); - if (pr->getStatus()==StatusOK && pr->getRunningTime()==0) - pr->setStatus(StatusUnknown, true, false); + if (pr->getStatus()==StatusOK && pr->getRunningTime(false)==0) + pr->setStatus(StatusUnknown, true, oBase::ChangeType::Update); //Autocreate class if it does not exist... int classId=wtoi(sp[OEclassno]); @@ -350,9 +350,9 @@ bool csvparser::importOE_CSV(oEvent &event, const wstring &file) { if (needSno || newEntry) { if (stno>0) - pr->setStartNo(stno, false); + pr->setStartNo(stno, oBase::ChangeType::Update); else - pr->setStartNo(nimport, false); + pr->setStartNo(nimport, oBase::ChangeType::Update); } oDataInterface DI=pr->getDI(); @@ -361,7 +361,7 @@ bool csvparser::importOE_CSV(oEvent &event, const wstring &file) { DI.setString("Nationality", sp[OEnat]); if (sp.size()>OEbib && needBib) - pr->setBib(sp[OEbib], 0, false, false); + pr->setBib(sp[OEbib], 0, false); if (sp.size()>=38) {//ECO DI.setInt("Fee", wtoi(sp[OEfee])); @@ -682,7 +682,7 @@ bool csvparser::importRAID(oEvent &event, const wstring &file) //Club is autocreated... pTeam team=event.addTeam(sp[RAIDteam], ClubId, ClassId); - team->setStartNo(wtoi(sp[RAIDid]), false); + team->setStartNo(wtoi(sp[RAIDid]), oBase::ChangeType::Update); if (sp.size()>8) team->getDI().setInt("SortIndex", wtoi(sp[RAIDcanoe])); oDataInterface teamDI=team->getDI(); @@ -703,7 +703,7 @@ bool csvparser::importRAID(oEvent &event, const wstring &file) pRunner r2=event.addRunner(sp[RAIDrunner2], ClubId, ClassId, 0, 0, false); team->setRunner(1, r2, false); - team->apply(true, 0, false); + team->evaluate(oBase::ChangeType::Update); } } fin.close(); diff --git a/code/csvparser.h b/code/csvparser.h index 8d2805c..ec4bf7e 100644 --- a/code/csvparser.h +++ b/code/csvparser.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/download.cpp b/code/download.cpp index 833abd0..201b581 100644 --- a/code/download.cpp +++ b/code/download.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/download.h b/code/download.h index 630c5bc..c3540a1 100644 --- a/code/download.h +++ b/code/download.h @@ -7,7 +7,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/english.lng b/code/english.lng index 0e14a6b..542a635 100644 --- a/code/english.lng +++ b/code/english.lng @@ -1421,8 +1421,8 @@ xml-data = xml data Ändrad = Modified Ändrade avgift för X deltagare = Modified fee for X competitor(s) Ã…ldersfilter = Age filter -Ã…ldersgräns ungdom = Age limit, low -Ã…ldersgräns äldre = Age limit, high +Ã…ldersgräns ungdom = Age limit, low (youth) +Ã…ldersgräns äldre = Age limit, high (pensioner) Ã…ldersgränser, reducerad anmälningsavgift = Age Limits, Fee Reduction Ã…ngra = Undo Ã…teransluten mot databasen, tävlingen synkroniserad = Reconnected to database, competition synchronized @@ -1871,7 +1871,7 @@ Byt till rätt klass (behÃ¥ll eventuell starttid) = Switch to the right class (k Byt till vakansplats i rätt klass (om möjligt) = Switch to a vacant position in the right class (if possible) TillÃ¥t ny klass, behÃ¥ll resultat frÃ¥n annan klass = Allow new class and keep results from other class TillÃ¥t ny klass, inget totalresultat = Allow new class but without total result -tooltip_explain_status = - = Unknown Status (No result yet)\nOK = Valid result\nDNS = Did Not Start\nCancelled = Cancelled entry (shown in start list)\nMP = Missing Punch\nDNF = Did Not Finish\nDISQ = Disqualified\nOMT = Over Maximum Time\nNTP = Not Taking Part +tooltip_explain_status = - = Unknown Status (No result yet)\nOK = Valid result\nDNS = Did Not Start\nCancelled = Cancelled entry (shown in start list)\nMP = Missing Punch\nDNF = Did Not Finish\nDISQ = Disqualified\nOMT = Over Maximum Time\nOOC = Out-of-competition\nNTP = Not Taking Part Placering = Place Resultat frÃ¥n tidigare etapper = Results from Earlier Stages Input Results = Input Results @@ -2106,7 +2106,7 @@ prefsMaximumSpeakerDelay = Maximum delay in announcer's view update prefsOrganizer = Organizer prefsPort = MySQL network port prefsRentCard = Rent card -prefsSeniorAge = Upper age limit +prefsSeniorAge = Upper (pensioner) age limit prefsServer = Default network server prefsSpeakerShortNames = Use initials in names prefsStreet = Organizer street address @@ -2435,4 +2435,43 @@ Runner check time = Runner check time ClassNumEntries = Number of entries in class Flera lopp i valfri ordning = Several races in any order Knockout sammanställning = Knock-out summary +Utom tävlan = OOC +Status code for running out-of-competition = Status code for running out-of-competition (OOC) X anmälda = X entries +LÃ¥s funktion = Lock function +LÃ¥s upp = Unlock +MÃ¥lstämpling tillÃ¥ts inte (X) = Finish punch disallowed (X) +Radio = Radio +Radio tillÃ¥ts inte (X) = Radio controls disallowed (X) +Startstämpling tillÃ¥ts inte (X) = Start punch disallowed (X) +TillÃ¥t = Allow +warn:printmodeonly = Note that you will only generate a printout of the card contents.\n\nTo save a result to the competition you need to select function readout/radio. +There is no result module with X as identifier = There is no result module with X as identifier +Senast sedd: X vid Y = Last seen: X at Y +Referens = Reference +Poäng E[stageno] = Points S +Status E[stageno] = Status S +Tid E[stageno] = Time S +Plac. E[stageno] = Pl. S +FrÃ¥n första = From first +Kartor = Maps +Poängreduktion = Reduction +Stigning = Climb +Till sista = To last +Heat = Heat +Kvalschema = Qualification scheme +LÃ¥st gaffling = Locked forking +Result module = Result module +ask:usecourseinclass = The course is not used by any other competitor in this class.\n\nWould you like to use it anyway? +Inkludera bana = Include course +Rogaining points before automatic reduction = Rogaining points before automatic reduction +Runner/team earlier stage places = Runner/team earlier stage places +Runner/team earlier stage points = Runner/team earlier stage points +Runner/team earlier stage running times = Runner/team earlier stage running times +Runner/team earlier stage statuses = Runner/team earlier stage statuses +Status code for cancelled entry = Status code for cancelled entry +Ã…ldersfiltrering = Age filter +Age (on last day of current year) = Age (on last day of current year) +Age above or equal implies senior/pensioner = Age above or equal implies senior/pensioner +Age below or equal implies youth = Age below or equal implies youth +Status code for not no timing = Status code for not no timing diff --git a/code/gdiconstants.h b/code/gdiconstants.h index 7ded93a..4b19a3c 100644 --- a/code/gdiconstants.h +++ b/code/gdiconstants.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/gdifonts.h b/code/gdifonts.h index 1d8fc02..e7b2cb8 100644 --- a/code/gdifonts.h +++ b/code/gdifonts.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -60,25 +60,28 @@ const int timerIgnoreSign = 1<<15; const int Capitalize = 1<<16; const int absolutePosition = 1 << 17; const int skipBoundingBox = 1 << 18; +const int hiddenText = 1 << 19; -enum GDICOLOR {colorBlack = RGB(0,0,0), - colorRed = RGB(128,0,0), - colorGreen = RGB(0,128,0), - colorDarkGrey = RGB(40,40,40), - colorDarkRed = RGB(64,0,0), - colorGreyBlue = RGB(92,92,128), - colorDarkBlue = RGB(0,0,92), - colorDarkGreen = RGB(0,64,0), - colorYellow = RGB(255, 230, 0), - colorLightBlue = RGB(240,240,255), - colorLightRed = RGB(255,230,230), - colorLightGreen = RGB(180, 255, 180), - colorLightYellow = RGB(255, 255, 200), - colorLightCyan = RGB(200, 255, 255), - colorLightMagenta = RGB(255, 200, 255), - colorMediumRed = RGB(255,200,200), - colorMediumDarkRed = RGB(240,120,120), - colorWindowBar = -2, - colorDefault = -1, - colorTransparent = -3}; +enum GDICOLOR { + colorBlack = RGB(0, 0, 0), + colorRed = RGB(128, 0, 0), + colorGreen = RGB(0, 128, 0), + colorDarkGrey = RGB(40, 40, 40), + colorDarkRed = RGB(64, 0, 0), + colorGreyBlue = RGB(92, 92, 128), + colorDarkBlue = RGB(0, 0, 92), + colorDarkGreen = RGB(0, 64, 0), + colorYellow = RGB(255, 230, 0), + colorLightBlue = RGB(235, 238, 254), + colorLightRed = RGB(255, 230, 230), + colorLightGreen = RGB(219, 247, 209), + colorLightYellow = RGB(248, 248, 216), + colorLightCyan = RGB(220, 249, 245),//RGB(200, 255, 255), + colorLightMagenta = RGB(240, 207, 244),//RGB(255, 200, 255), + colorMediumRed = RGB(255, 200, 200), + colorMediumDarkRed = RGB(240, 120, 120), + colorWindowBar = -2, + colorDefault = -1, + colorTransparent = -3 +}; diff --git a/code/gdiimpl.h b/code/gdiimpl.h index 8405472..c42e44c 100644 --- a/code/gdiimpl.h +++ b/code/gdiimpl.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/gdioutput.cpp b/code/gdioutput.cpp index 3a5fc3f..480782c 100644 --- a/code/gdioutput.cpp +++ b/code/gdioutput.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -100,10 +100,11 @@ EventInfo::EventInfo() : callBack(0), keyEvent(KC_NONE) {} /** Return true if rendering text should be skipped for this format. */ bool gdioutput::skipTextRender(int format) { - format &= 0xFF; + format &= 0xFF | hiddenText; return format == pageNewPage || format == pagePageInfo || - format == pageNewChapter; + format == pageNewChapter || + (format & hiddenText) == hiddenText; } #ifndef MEOSDB @@ -376,11 +377,8 @@ gdioutput::~gdioutput() if (toolbar) delete toolbar; toolbar = 0; - //delete table; - while(!Tables.empty()){ - Tables.front().table->releaseOwnership(); - Tables.pop_front(); - } + + Tables.clear(); if (tabs) { delete tabs; @@ -507,7 +505,7 @@ void gdioutput::drawBackground(HDC hDC, RECT &rc) SelectObject(hDC, GetStockObject(ANSI_FIXED_FONT)); DeleteObject(hInfo); } - +/* DWORD c=GetSysColor(COLOR_3DFACE); double red = double(GetRValue(c)) *0.9; double green = double(GetGValue(c)) * 0.85; @@ -523,7 +521,15 @@ void gdioutput::drawBackground(HDC hDC, RECT &rc) double blue1=min(255., blue*1.3); double green1=min(255., green*1.3); double red1=min(255., red*1.3); + */ + double red = 242.0; + double green = 247.0; + double blue = 254.0; + + double blue1 = 250.0; + double green1 = 232.0; + double red1 = 223.0; TRIVERTEX vert[2]; if (hideBG) { @@ -661,8 +667,10 @@ void gdioutput::draw(HDC hDC, RECT &rc, RECT &drawArea) } void gdioutput::renderRectangle(HDC hDC, RECT *clipRegion, const RectangleInfo &ri) { - if (ri.drawBorder) - SelectObject(hDC, GetStockObject(BLACK_PEN)); + if (ri.drawBorder) { + SelectObject(hDC, GetStockObject(DC_PEN)); + SetDCPenColor(hDC, RGB(40,40,60)); + } else SelectObject(hDC, GetStockObject(NULL_PEN)); @@ -1213,7 +1221,8 @@ void gdioutput::enableCheckBoxLink(TextInfo &ti, bool enable) { else { needRefresh = ti.callBack != 0; ti.callBack = 0; - ti.setColor(colorDarkGrey); + DWORD c = GetSysColor(COLOR_GRAYTEXT); + ti.setColor(GDICOLOR(c)); } if (needRefresh) InvalidateRect(hWndTarget, &ti.textRect, true); @@ -2980,12 +2989,12 @@ void gdioutput::clearPage(bool autoRefresh, bool keepToolbar) { lockUpDown = false; hasAnyTimer = false; enableTables(); - #ifndef MEOSDB - if (toolbar && !keepToolbar) - toolbar->hide(); - #endif +#ifndef MEOSDB + if (toolbar && !keepToolbar) + toolbar->hide(); +#endif - while(!timers.empty()) { + while (!timers.empty()) { KillTimer(hWndTarget, (UINT_PTR)&timers.back()); timers.back().setWnd = 0; timers.back().parent = 0; @@ -2994,11 +3003,11 @@ void gdioutput::clearPage(bool autoRefresh, bool keepToolbar) { restorePoints.clear(); shownStrings.clear(); - onClear=0; + onClear = 0; FocusList.clear(); currentFocus = 0; TL.clear(); - itTL=TL.end(); + itTL = TL.end(); listDescription.clear(); if (hWndTarget && autoRefresh) @@ -3010,14 +3019,14 @@ void gdioutput::clearPage(bool autoRefresh, bool keepToolbar) { for (ToolList::iterator it = toolTips.begin(); it != toolTips.end(); ++it) { if (hWndToolTip) { - SendMessage(hWndToolTip, TTM_DELTOOL, 0, (LPARAM) &it->ti); + SendMessage(hWndToolTip, TTM_DELTOOL, 0, (LPARAM)&it->ti); } } toolTips.clear(); { list::iterator it; - for(it=BI.begin(); it != BI.end(); ++it) { + for (it = BI.begin(); it != BI.end(); ++it) { it->callBack = 0; it->setHandler(0); DestroyWindow(it->hWnd); @@ -3027,7 +3036,7 @@ void gdioutput::clearPage(bool autoRefresh, bool keepToolbar) { } { list::iterator it; - for(it=II.begin(); it != II.end(); ++it) { + for (it = II.begin(); it != II.end(); ++it) { it->callBack = 0; it->setHandler(0); DestroyWindow(it->hWnd); @@ -3038,7 +3047,7 @@ void gdioutput::clearPage(bool autoRefresh, bool keepToolbar) { { list::iterator it; - for(it=LBI.begin(); it != LBI.end(); ++it) { + for (it = LBI.begin(); it != LBI.end(); ++it) { it->callBack = 0; it->setHandler(0); DestroyWindow(it->hWnd); @@ -3049,14 +3058,10 @@ void gdioutput::clearPage(bool autoRefresh, bool keepToolbar) { LBI.clear(); } - //delete table; - //table=0; - while(!Tables.empty()){ - Table *t=Tables.front().table; - Tables.front().table=0; + while (!Tables.empty()) { + auto t = Tables.front().table; Tables.pop_front(); t->hide(*this); - t->releaseOwnership(); } DataInfo.clear(); @@ -3065,16 +3070,16 @@ void gdioutput::clearPage(bool autoRefresh, bool keepToolbar) { Rectangles.clear(); - MaxX=40; - MaxY=100; + MaxX = scaleLength(60); + MaxY = scaleLength(100); - CurrentX=40; - CurrentY=START_YP; + CurrentX = scaleLength(40); + CurrentY = scaleLength(START_YP); - OffsetX=0; - OffsetY=0; + OffsetX = 0; + OffsetY = 0; - renderOptimize=true; + renderOptimize = true; backgroundColor1 = -1; backgroundColor2 = -1; @@ -3097,7 +3102,7 @@ void gdioutput::clearPage(bool autoRefresh, bool keepToolbar) { wstring msg = ex.wwhat(); alert(msg); } - catch(const std::exception &ex) { + catch (const std::exception &ex) { if (isTestMode) throw ex; string msg(ex.what()); @@ -3105,7 +3110,7 @@ void gdioutput::clearPage(bool autoRefresh, bool keepToolbar) { } postClear = 0; - manualUpdate=!autoRefresh; + manualUpdate = !autoRefresh; } void gdioutput::getWindowText(HWND hWnd, wstring &text) @@ -3208,7 +3213,7 @@ const wstring &gdioutput::getText(const char *id, bool acceptMissing) const return _EmptyWString; } -bool gdioutput::hasField(const string &id) const +bool gdioutput::hasWidget(const string &id) const { for(list::const_iterator it=II.begin(); it != II.end(); ++it){ @@ -3602,8 +3607,8 @@ void gdioutput::setTabStops(const string &Name, int t1, int t2) int n=1; LONG bu=GetDialogBaseUnits(); int baseunitX=LOWORD(bu); - array[0]=(t1 * 4) / baseunitX ; - array[1]=(t2 * 4) / baseunitX ; + array[0]=int(t1 * 4.2) / baseunitX ; + array[1]=int(t2 * 4.2) / baseunitX ; int lastTabStop = 0; if (t2>0) { n=2; @@ -3989,7 +3994,6 @@ void gdioutput::removeString(string id) for (it=TL.begin(); it != TL.end(); ++it) { if (it->id==id) { HDC hDC=GetDC(hWndTarget); - //TextOut( RECT rc; rc.left=it->xp; rc.top=it->yp; @@ -4213,12 +4217,12 @@ void gdioutput::RenderString(TextInfo &ti, HDC hDC) ti.textRect.bottom+=ti.yp+dx; if (ti.format == 10) { - DWORD c=GetSysColor(COLOR_INFOBK); + DWORD c = colorLightYellow;// GetSysColor(COLOR_INFOBK); double red=GetRValue(c); double green=GetGValue(c); double blue=GetBValue(c); - double blue1=min(255., blue*0.8); + double blue1=min(255., blue*1.05); double green1=min(255., green*1.05); double red1=min(255., red*1.05); @@ -4273,7 +4277,7 @@ void gdioutput::RenderString(TextInfo &ti, HDC hDC) rc.bottom+=ti.yp+dx-OffsetY; rc.right=ti.xp+dx+width-OffsetX; - SetTextColor(hDC, GetSysColor(COLOR_INFOTEXT)); + SetTextColor(hDC, 0); DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_LEFT|DT_NOPREFIX|DT_WORDBREAK); } @@ -4787,7 +4791,7 @@ void gdioutput::CheckInterfaceTimeouts(DWORD T) } } -bool gdioutput::removeControl(const string &id) +bool gdioutput::removeWidget(const string &id) { list::iterator it=BI.begin(); @@ -4834,13 +4838,15 @@ bool gdioutput::removeControl(const string &id) return false; } -bool gdioutput::hideControl(const string &id) -{ +bool gdioutput::hideWidget(const string &id, bool hide) { list::iterator it=BI.begin(); while (it!=BI.end()) { - if (it->id==id) { - ShowWindow(it->hWnd, SW_HIDE); + if (it->id == id) { + ShowWindow(it->hWnd, hide ? SW_HIDE : SW_SHOW); + if (it->isCheckbox) { + hideWidget("T" + id, hide); + } return true; } ++it; @@ -4850,7 +4856,7 @@ bool gdioutput::hideControl(const string &id) while (lit!=LBI.end()) { if (lit->id==id) { - ShowWindow(lit->hWnd, SW_HIDE); + ShowWindow(lit->hWnd, hide ? SW_HIDE : SW_SHOW); return true; } ++lit; @@ -4860,15 +4866,25 @@ bool gdioutput::hideControl(const string &id) while (iit!=II.end()) { if (iit->id==id) { - ShowWindow(iit->hWnd, SW_HIDE); + ShowWindow(iit->hWnd, hide ? SW_HIDE : SW_SHOW); return true; } ++iit; } + + for (auto &ti : TL) { + if (ti.id == id) { + if (hide) + ti.format |= hiddenText; + else + ti.format &= ~hiddenText; + + return true; + } + } return false; } - void gdioutput::setRestorePoint() { setRestorePoint(""); @@ -4974,11 +4990,9 @@ void gdioutput::restoreInternal(const RestoreInfo &ri) } while(Tables.size() > unsigned(ri.nTables)){ - Table *t=Tables.back().table; - Tables.back().table=0; + auto t=Tables.back().table; Tables.pop_back(); t->hide(*this); - t->releaseOwnership(); } int dataRemove=DataInfo.size()-ri.nData; @@ -5656,7 +5670,7 @@ Table &gdioutput::getTable() const { if (Tables.empty()) throw std::exception("No table defined"); - return *const_cast
(Tables.back().table); + return *const_cast
(Tables.back().table.get()); } static int gdiTableCB(gdioutput *gdi, int type, void *data) @@ -5774,16 +5788,10 @@ void gdioutput::enableTables() useTables=true; #ifndef MEOSDB if (!Tables.empty()) { - Table *t = Tables.front().table; + auto &t = Tables.front().table; if (toolbar == 0) toolbar = new Toolbar(*this); -/* RECT rc; - rc.top = 10; - rc.bottom = 100; - rc.left = 100; - rc.right = 200; - addToolTip("Hej hopp!", 0, &rc); -*/ + toolbar->setData(t); string tname = string("table") + itos(t->canDelete()) + itos(t->canInsert()) + itos(t->canPaste()); @@ -5809,19 +5817,18 @@ void gdioutput::enableTables() #endif } -void gdioutput::processToolbarMessage(const string &id, void *data) { +void gdioutput::processToolbarMessage(const string &id, Table *tbl) { if (hasCommandLock()) return; wstring msg; string cmd; if (getRecorder().recording()) { - Table *tbl = (Table *)data; cmd = "tableCmd(\"" + id + "\"); //" + narrow(tbl->getTableName()); } try { ButtonInfo bi; bi.id = id; - tableCB(bi, (Table *)data); + tableCB(bi, tbl); getRecorder().record(cmd); } catch (meosException &ex) { @@ -5879,7 +5886,7 @@ void gdioutput::disableTables() if (bit->id.substr(0, 3)=="tbl" && bit->getExtra()!=0) { string id = bit->id; ++bit; - removeControl(id); + removeWidget(id); } else ++bit; @@ -5887,7 +5894,7 @@ void gdioutput::disableTables() } -void gdioutput::addTable(Table *t, int x, int y) +void gdioutput::addTable(const shared_ptr
&t, int x, int y) { TableInfo ti; ti.table = t; @@ -5899,7 +5906,6 @@ void gdioutput::addTable(Table *t, int x, int y) t->autoSelectColumns(); t->autoAdjust(*this); - ti.table->addOwnership(); Tables.push_back(ti); //updatePos(x, y, dx + TableXMargin, dy + TableYMargin); diff --git a/code/gdioutput.h b/code/gdioutput.h index ee8f4fb..601cdfe 100644 --- a/code/gdioutput.h +++ b/code/gdioutput.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -371,7 +371,7 @@ public: bool hasToolbar() const; void activateToolbar(bool active); - void processToolbarMessage(const string &id, void *data); + void processToolbarMessage(const string &id, Table *data); void synchronizeListScroll(const string &id1, const string &id2); @@ -424,7 +424,7 @@ public: void selectTab(int Id); - void addTable(Table *table, int x, int y); + void addTable(const shared_ptr
&table, int x, int y); Table &getTable() const; //Get the (last) table. If needed, add support for named tables... @@ -468,8 +468,8 @@ public: void setRestorePoint(); void setRestorePoint(const string &id); - bool removeControl(const string &id); - bool hideControl(const string &id); + bool removeWidget(const string &id); + bool hideWidget(const string &id, bool hide = true); void CheckInterfaceTimeouts(DWORD T); bool RemoveFirstInfoBox(const string &id); @@ -597,7 +597,7 @@ public: bool clearList(const string &id); - bool hasField(const string &id) const; + bool hasWidget(const string &id) const; const wstring &getText(const char *id, bool acceptMissing = false) const; diff --git a/code/gdistructures.h b/code/gdistructures.h index d8a10e2..b3307e9 100644 --- a/code/gdistructures.h +++ b/code/gdistructures.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -131,7 +131,7 @@ public: TableInfo():xp(0), yp(0), table(0) {} int xp; int yp; - Table *table; + shared_ptr
table; HWND getControlWindow() const {throw std::exception("Unsupported");} }; diff --git a/code/generalresult.cpp b/code/generalresult.cpp index bfc727c..e847625 100644 --- a/code/generalresult.cpp +++ b/code/generalresult.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -32,22 +32,26 @@ extern gdioutput *gdi_main; -GeneralResultCtr::GeneralResultCtr(const char *tagIn, const wstring &nameIn, GeneralResult *ptrIn) { +GeneralResultCtr::GeneralResultCtr(const char *tagIn, const wstring &nameIn, const shared_ptr &ptrIn) { name = nameIn; tag = tagIn; ptr = ptrIn; } -GeneralResultCtr::GeneralResultCtr(wstring &file, DynamicResult *ptrIn) { +GeneralResultCtr::GeneralResultCtr(wstring &file, const shared_ptr &ptrIn) { ptr = ptrIn; name = ptrIn->getName(false); tag = ptrIn->getTag(); fileSource = file; } +bool GeneralResultCtr::operator<(const GeneralResultCtr &c) const { + return CompareString(LOCALE_USER_DEFAULT, 0, + name.c_str(), name.length(), + c.name.c_str(), c.name.length()) == CSTR_LESS_THAN; +} + GeneralResultCtr::~GeneralResultCtr() { - delete ptr; - ptr = 0; } GeneralResultCtr::GeneralResultCtr(const GeneralResultCtr &ctr) { @@ -55,16 +59,15 @@ GeneralResultCtr::GeneralResultCtr(const GeneralResultCtr &ctr) { name = ctr.name; tag = ctr.tag; fileSource = ctr.fileSource; - ctr.ptr = 0; } void GeneralResultCtr::operator=(const GeneralResultCtr &ctr) { if (this == &ctr) return; - delete ptr; name = ctr.name; ptr = ctr.ptr; - ctr.ptr = 0; + tag = ctr.tag; + fileSource = ctr.fileSource; } bool GeneralResultCtr::isDynamic() const { @@ -87,6 +90,13 @@ void GRINSTANCE() { gr.sort(b, SortByFinishTime); } +const string &GeneralResult::getTimeStamp() const { + return gdioutput::narrow(getMeosDate()); +} + +bool GeneralResult::isRogaining() const { + return false; +} void GeneralResult::setContext(const oListParam *contextIn) { context = contextIn; @@ -112,7 +122,7 @@ int GeneralResult::getListParamTimeFromControl() const { struct GRSortInfo { int principalSort; - int score; + pair score; oAbstractRunner *tr; bool operator<(const GRSortInfo &other) const { @@ -130,72 +140,99 @@ struct GRSortInfo { } }; -void GeneralResult::calculateTeamResults(vector &teams, oListInfo::ResultType resType, bool sortTeams, int inputNumber) const { +void GeneralResult::calculateTeamResults(vector &teams, + bool classResult, + oListInfo::ResultType resType, + bool sortTeams, + int inputNumber) const { if (teams.empty()) return; - prepareCalculations(*teams[0]->oe, true, inputNumber); - //bool classSort = resType == oListInfo::Global ? false : true; - vector teamScore(teams.size()); - for (size_t k = 0; k < teams.size(); k++) { - if (resType == oListInfo::Classwise) { - teamScore[k].principalSort = teams[k]->Class ? teams[k]->Class->getSortIndex() * 50000 - + teams[k]->Class->getId() : 0; + if (lockPrepare) + throw meosException("Bad cyclic call"); + lockPrepare = true; + + try { + set clsId; + vector runners; + for (pTeam t : teams) { + clsId.insert(t->getClassId(true)); + for (int j = 0; j < t->getNumRunners(); j++) { + pRunner r = t->getRunner(j); + if (r) { + runners.push_back(r); + clsId.insert(r->getClassId(true)); + } + } } - else - teamScore[k].principalSort = 0; + prepareCalculations(*teams[0]->oe, classResult, clsId, runners, teams, inputNumber); - prepareCalculations(*teams[k]); - teams[k]->tmpResult.runningTime = teams[k]->getStartTime(); //XXX - teams[k]->tmpResult.runningTime = deduceTime(*teams[k]); - teams[k]->tmpResult.status = deduceStatus(*teams[k]); - teams[k]->tmpResult.points = deducePoints(*teams[k]); + vector teamScore(teams.size()); + for (size_t k = 0; k < teams.size(); k++) { + if (resType == oListInfo::Classwise) { + teamScore[k].principalSort = teams[k]->Class ? teams[k]->Class->getSortIndex() * 50000 + + teams[k]->Class->getId() : 0; + } + else + teamScore[k].principalSort = 0; - teamScore[k].score = score(*teams[k], teams[k]->tmpResult.status, - teams[k]->tmpResult.runningTime, - teams[k]->tmpResult.points); + prepareCalculations(*teams[k], classResult); + teams[k]->tmpResult.runningTime = teams[k]->getStartTime(); //XXX + teams[k]->tmpResult.runningTime = deduceTime(*teams[k]); + teams[k]->tmpResult.status = deduceStatus(*teams[k]); + teams[k]->tmpResult.points = deducePoints(*teams[k]); - storeOutput(teams[k]->tmpResult.outputTimes, - teams[k]->tmpResult.outputNumbers); + teamScore[k].score = score(*teams[k], teams[k]->tmpResult.status, + teams[k]->tmpResult.runningTime, + teams[k]->tmpResult.points); - teamScore[k].tr = teams[k]; + storeOutput(teams[k]->tmpResult.outputTimes, + teams[k]->tmpResult.outputNumbers); - } + teamScore[k].tr = teams[k]; - ::sort(teamScore.begin(), teamScore.end()); - int place = 1; - int iPlace = 1; - int leadtime = 0; - for (size_t k = 0; k < teamScore.size(); k++) { - if (k > 0 && teamScore[k - 1].principalSort != teamScore[k].principalSort) { - place = 1; - iPlace = 1; - } - else if (k > 0 && teamScore[k - 1].score != teamScore[k].score) { - place = iPlace; } - if (teamScore[k].tr->tmpResult.status == StatusOK) { - teamScore[k].tr->tmpResult.place = place; - iPlace++; - if (place == 1) { - leadtime = teamScore[k].tr->tmpResult.runningTime; - teamScore[k].tr->tmpResult.timeAfter = 0; + ::sort(teamScore.begin(), teamScore.end()); + int place = 1; + int iPlace = 1; + int leadtime = 0; + for (size_t k = 0; k < teamScore.size(); k++) { + if (k > 0 && teamScore[k - 1].principalSort != teamScore[k].principalSort) { + place = 1; + iPlace = 1; + } + else if (k > 0 && teamScore[k - 1].score != teamScore[k].score) { + place = iPlace; + } + + if (teamScore[k].tr->tmpResult.status == StatusOK) { + teamScore[k].tr->tmpResult.place = place; + iPlace++; + if (place == 1) { + leadtime = teamScore[k].tr->tmpResult.runningTime; + teamScore[k].tr->tmpResult.timeAfter = 0; + } + else { + teamScore[k].tr->tmpResult.timeAfter = teamScore[k].tr->tmpResult.runningTime - leadtime; + } } else { - teamScore[k].tr->tmpResult.timeAfter = teamScore[k].tr->tmpResult.runningTime - leadtime; + teamScore[k].tr->tmpResult.place = 0; + teamScore[k].tr->tmpResult.timeAfter = 0; } } - else { - teamScore[k].tr->tmpResult.place = 0; - teamScore[k].tr->tmpResult.timeAfter = 0; - } - } - if (sortTeams) { - for (size_t k = 0; k < teamScore.size(); k++) { - teams[k] = (oTeam *)teamScore[k].tr; + if (sortTeams) { + for (size_t k = 0; k < teamScore.size(); k++) { + teams[k] = (oTeam *)teamScore[k].tr; + } } + lockPrepare = false; + } + catch (...) { + lockPrepare = false; + throw; } } @@ -271,118 +308,138 @@ template void GeneralResult::sort(vector &rt, SortOrder so) const } void GeneralResult::calculateIndividualResults(vector &runners, + bool classResult, oListInfo::ResultType resType, bool sortRunners, int inputNumber) const { if (runners.empty()) return; - prepareCalculations(*runners[0]->oe, false, inputNumber); - //bool classSort = resType == oListInfo::Global ? false : true; - vector runnerScore(runners.size()); - for (size_t k = 0; k < runners.size(); k++) { - const oRunner *r = runners[k]; - if (resType == oListInfo::Classwise) { - runnerScore[k].principalSort = r->Class ? r->Class->getSortIndex() * 50000 - + r->Class->getId() : 0; - } - else if (resType == oListInfo::Legwise) { - runnerScore[k].principalSort = r->Class ? r->Class->getSortIndex() * 50000 - + r->Class->getId() : 0; - int ln = r->getLegNumber(); - const oTeam *pt = r->getTeam(); - if (pt) { - const oClass *tcls = pt->getClassRef(false); - if (tcls && tcls->getClassType() == oClassRelay) { - int dummy; - tcls->splitLegNumberParallel(r->getLegNumber(), ln, dummy); - } + if (lockPrepare) + throw meosException("Bad cyclic call"); + lockPrepare = true; + + try { + + set clsId; + for (pRunner r : runners) { + clsId.insert(r->getClassId(true)); + } + vector noTeams; + prepareCalculations(*runners[0]->oe, classResult, clsId, runners, noTeams, inputNumber); + //bool classSort = resType == oListInfo::Global ? false : true; + vector runnerScore(runners.size()); + for (size_t k = 0; k < runners.size(); k++) { + const oRunner *r = runners[k]; + if (resType == oListInfo::Classwise) { + runnerScore[k].principalSort = r->Class ? r->Class->getSortIndex() * 50000 + + r->Class->getId() : 0; } - runnerScore[k].principalSort = runnerScore[k].principalSort * 50 + ln; - } - else if (resType == oListInfo::Coursewise) { - pCourse crs = r->getCourse(false); - runnerScore[k].principalSort = crs ? crs->getId() : 0; - } - else - runnerScore[k].principalSort = 0; + else if (resType == oListInfo::Legwise) { + runnerScore[k].principalSort = r->Class ? r->Class->getSortIndex() * 50000 + + r->Class->getId() : 0; - int from = getListParamTimeFromControl(); - if (from <= 0) { - runners[k]->tmpResult.startTime = r->getStartTime(); - } - else { - int rt; - RunnerStatus stat; - runners[k]->getSplitTime(from, stat, rt); - if (stat == StatusOK) - runners[k]->tmpResult.startTime = runners[k]->getStartTime() + rt; + int ln = r->getLegNumber(); + const oTeam *pt = r->getTeam(); + if (pt) { + const oClass *tcls = pt->getClassRef(false); + if (tcls && tcls->getClassType() == oClassRelay) { + int dummy; + tcls->splitLegNumberParallel(r->getLegNumber(), ln, dummy); + } + } + runnerScore[k].principalSort = runnerScore[k].principalSort * 50 + ln; + } + else if (resType == oListInfo::Coursewise) { + pCourse crs = r->getCourse(false); + runnerScore[k].principalSort = crs ? crs->getId() : 0; + } else - runners[k]->tmpResult.startTime = runners[k]->getStartTime(); - } - prepareCalculations(*runners[k]); - runners[k]->tmpResult.runningTime = deduceTime(*runners[k], runners[k]->tmpResult.startTime); - runners[k]->tmpResult.status = deduceStatus(*runners[k]); - runners[k]->tmpResult.points = deducePoints(*runners[k]); + runnerScore[k].principalSort = 0; - runnerScore[k].score = score(*runners[k], runners[k]->tmpResult.status, - runners[k]->tmpResult.runningTime, - runners[k]->tmpResult.points, false); - - storeOutput(runners[k]->tmpResult.outputTimes, - runners[k]->tmpResult.outputNumbers); - - runnerScore[k].tr = runners[k]; - - } - - ::sort(runnerScore.begin(), runnerScore.end()); - int place = 1; - int iPlace = 1; - int leadtime = 0; - for (size_t k = 0; k < runnerScore.size(); k++) { - if (k > 0 && runnerScore[k - 1].principalSort != runnerScore[k].principalSort) { - place = 1; - iPlace = 1; - } - else if (k > 0 && runnerScore[k - 1].score != runnerScore[k].score) { - place = iPlace; - } - - if (runnerScore[k].tr->tmpResult.status == StatusOK) { - runnerScore[k].tr->tmpResult.place = place; - iPlace++; - if (place == 1) { - leadtime = runnerScore[k].tr->tmpResult.runningTime; - runnerScore[k].tr->tmpResult.timeAfter = 0; + int from = getListParamTimeFromControl(); + if (from <= 0) { + runners[k]->tmpResult.startTime = r->getStartTime(); } else { - runnerScore[k].tr->tmpResult.timeAfter = runnerScore[k].tr->tmpResult.runningTime - leadtime; + int rt; + RunnerStatus stat; + runners[k]->getSplitTime(from, stat, rt); + if (stat == StatusOK) + runners[k]->tmpResult.startTime = runners[k]->getStartTime() + rt; + else + runners[k]->tmpResult.startTime = runners[k]->getStartTime(); + } + + prepareCalculations(*runners[k], classResult); + runners[k]->tmpResult.runningTime = deduceTime(*runners[k], runners[k]->tmpResult.startTime); + runners[k]->tmpResult.status = deduceStatus(*runners[k]); + runners[k]->tmpResult.points = deducePoints(*runners[k]); + + runnerScore[k].score = score(*runners[k], runners[k]->tmpResult.status, + runners[k]->tmpResult.runningTime, + runners[k]->tmpResult.points, false); + + storeOutput(runners[k]->tmpResult.outputTimes, + runners[k]->tmpResult.outputNumbers); + + runnerScore[k].tr = runners[k]; + + } + + ::sort(runnerScore.begin(), runnerScore.end()); + int place = 1; + int iPlace = 1; + int leadtime = 0; + for (size_t k = 0; k < runnerScore.size(); k++) { + if (k > 0 && runnerScore[k - 1].principalSort != runnerScore[k].principalSort) { + place = 1; + iPlace = 1; + } + else if (k > 0 && runnerScore[k - 1].score != runnerScore[k].score) { + place = iPlace; + } + + if (runnerScore[k].tr->tmpResult.status == StatusOK) { + runnerScore[k].tr->tmpResult.place = place; + iPlace++; + if (place == 1) { + leadtime = runnerScore[k].tr->tmpResult.runningTime; + runnerScore[k].tr->tmpResult.timeAfter = 0; + } + else { + runnerScore[k].tr->tmpResult.timeAfter = runnerScore[k].tr->tmpResult.runningTime - leadtime; + } + } + else { + runnerScore[k].tr->tmpResult.place = 0; + runnerScore[k].tr->tmpResult.timeAfter = 0; } } - else { - runnerScore[k].tr->tmpResult.place = 0; - runnerScore[k].tr->tmpResult.timeAfter = 0; + + if (sortRunners) { + for (size_t k = 0; k < runnerScore.size(); k++) { + runners[k] = (oRunner *)runnerScore[k].tr; + } } } - - if (sortRunners) { - for (size_t k = 0; k < runnerScore.size(); k++) { - runners[k] = (oRunner *)runnerScore[k].tr; - } + catch (...) { + lockPrepare = false; + throw; } + lockPrepare = false; } -void GeneralResult::prepareCalculations(oEvent &oe, bool prepareForTeam, int inputNumber) const { +void GeneralResult::prepareCalculations(oEvent &oe, bool classResult, const set &cls, vector &runners, vector &teams, int inputNumber) const { } -void GeneralResult::prepareCalculations(oTeam &team) const { +void GeneralResult::prepareCalculations(oTeam &team, bool classResult) const { int nr = team.getNumRunners(); for (int j = 0; j < nr; j++) { pRunner r = team.getRunner(j); if (r) { - prepareCalculations(*r); + prepareCalculations(*r, classResult); r->tmpResult.runningTime = deduceTime(*r, r->getStartTime()); //XXX r->tmpResult.status = deduceStatus(*r); r->tmpResult.place = 0;//XXX? @@ -398,7 +455,7 @@ void GeneralResult::prepareCalculations(oTeam &team) const { } } -void GeneralResult::prepareCalculations(oRunner &runner) const { +void GeneralResult::prepareCalculations(oRunner &runner, bool classResult) const { int from = getListParamTimeFromControl(); runner.tmpResult.startTime = runner.getStartTime(); @@ -414,8 +471,8 @@ void GeneralResult::prepareCalculations(oRunner &runner) const { void GeneralResult::storeOutput(vector ×, vector &numbers) const { } -int GeneralResult::score(oTeam &team, RunnerStatus st, int rt, int points) const { - return (100 * RunnerStatusOrderMap[st] + team.getNumShortening()) * 900000 + rt; +pair GeneralResult::score(oTeam &team, RunnerStatus st, int rt, int points) const { + return make_pair((100 * RunnerStatusOrderMap[st] + team.getNumShortening()) * 100000 - points, + rt); } RunnerStatus GeneralResult::deduceStatus(oTeam &team) const { @@ -423,19 +480,20 @@ RunnerStatus GeneralResult::deduceStatus(oTeam &team) const { } int GeneralResult::deduceTime(oTeam &team) const { - return team.getRunningTime(); + return team.getRunningTime(false); } int GeneralResult::deducePoints(oTeam &team) const { - return team.getRogainingPoints(false); + return team.getRogainingPoints(false, false); } -int GeneralResult::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const { +pair GeneralResult::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const { if (asTeamMember) { - return runner.getLegNumber(); + return make_pair(runner.getLegNumber(), 0); + } + else { + return make_pair((100 * RunnerStatusOrderMap[st] + runner.getNumShortening()) * 100000 - points, time); } - else - return (RunnerStatusOrderMap[st] * 100 + runner.getNumShortening()) * 900000 + time; } RunnerStatus GeneralResult::deduceStatus(oRunner &runner) const { @@ -443,14 +501,14 @@ RunnerStatus GeneralResult::deduceStatus(oRunner &runner) const { } int GeneralResult::deduceTime(oRunner &runner, int startTime) const { - return runner.getRunningTime(); + return runner.getRunningTime(false); } int GeneralResult::deducePoints(oRunner &runner) const { - return runner.getRogainingPoints(false); + return runner.getRogainingPoints(false, false); } -int ResultAtControl::score(oTeam &team, RunnerStatus st, int time, int points) const { +pair ResultAtControl::score(oTeam &team, RunnerStatus st, int time, int points) const { return GeneralResult::score(team, st, time, points); } @@ -466,14 +524,11 @@ int ResultAtControl::deducePoints(oTeam &team) const { return GeneralResult::deducePoints(team); } -int ResultAtControl::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const { +pair ResultAtControl::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const { if (asTeamMember) - return runner.getLegNumber(); - const int TK = 3600 * 100; - if (st == StatusOK) - return time; - else - return TK + st; + return make_pair(runner.getLegNumber(), 0); + + return make_pair(RunnerStatusOrderMap[st], time); } RunnerStatus TotalResultAtControl::deduceStatus(oRunner &runner) const { @@ -492,7 +547,7 @@ RunnerStatus TotalResultAtControl::deduceStatus(oRunner &runner) const { while (legIx > 0 && (cls->isParallel(legIx) || cls->isOptional(legIx))) legIx--; if (legIx > 0) - inputStatus = t->getLegStatus(legIx - 1, true); + inputStatus = t->getLegStatus(legIx - 1, false, true); } else { inputStatus = t->getInputStatus(); @@ -522,7 +577,7 @@ int TotalResultAtControl::deduceTime(oRunner &runner, int startTime) const { while (legIx > 0 && (cls->isParallel(legIx) || cls->isOptional(legIx))) legIx--; if (legIx > 0) - inputTime = t->getLegRunningTime(legIx - 1, true); + inputTime = t->getLegRunningTime(legIx - 1, false, true); } else { inputTime = t->getInputTime(); @@ -535,16 +590,16 @@ int TotalResultAtControl::deduceTime(oRunner &runner, int startTime) const { return singleTime + inputTime; } -int TotalResultAtControl::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const { +pair TotalResultAtControl::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const { if (asTeamMember) - return runner.getLegNumber(); - const int TK = 3600 * 100; + return make_pair(runner.getLegNumber(), 0); + const int TK = 3600 * 24 * 7; RunnerStatus inputStatus = StatusOK; if (runner.getTeam()) { const pTeam t = runner.getTeam(); if (runner.getLegNumber() > 0) { - inputStatus = t->getLegStatus(runner.getLegNumber() - 1, true); + inputStatus = t->getLegStatus(runner.getLegNumber() - 1, false, true); } else { inputStatus = t->getInputStatus(); @@ -557,11 +612,7 @@ int TotalResultAtControl::score(oRunner &runner, RunnerStatus st, int time, int if (st != StatusUnknown) st = max(inputStatus, st); - if (st == StatusOK) { - return time; - } - else - return TK + st; + return make_pair(RunnerStatusOrderMap[st], time); } RunnerStatus ResultAtControl::deduceStatus(oRunner &runner) const { @@ -573,7 +624,7 @@ RunnerStatus ResultAtControl::deduceStatus(oRunner &runner) const { return stat; } RunnerStatus st = runner.getStatus(); - if (st == StatusUnknown && runner.getRunningTime() > 0) + if (st == StatusUnknown && runner.getRunningTime(false) > 0) return StatusOK; return st; } @@ -644,6 +695,7 @@ DynamicResult::DynamicResult(const DynamicResult &resIn) { } void DynamicResult::operator=(const DynamicResult &resIn) { + clear(); isCompiled = false; name = resIn.name; tag = resIn.tag; @@ -718,17 +770,21 @@ RunnerStatus DynamicResult::toStatus(int status) const { return StatusDQ; case StatusMAX: return StatusMAX; + case StatusOutOfCompetition: + return StatusOutOfCompetition; + case StatusNoTiming: + return StatusNoTiming; default: throw meosException("Unknown status code X#" + itos(status)); } } -int DynamicResult::score(oTeam &team, RunnerStatus st, int time, int points) const { +pair DynamicResult::score(oTeam &team, RunnerStatus st, int time, int points) const { if (getMethod(MTScore)) { parser.addSymbol("ComputedTime", time); parser.addSymbol("ComputedStatus", st); parser.addSymbol("ComputedPoints", points); - return getMethod(MTScore)->evaluate(parser); + return make_pair(0, getMethod(MTScore)->evaluate(parser)); } else if (getMethodSource(MTScore).empty()) return GeneralResult::score(team, st, time, points); @@ -759,12 +815,12 @@ int DynamicResult::deducePoints(oTeam &team) const { else throw meosException("Syntax error"); } -int DynamicResult::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const { +pair DynamicResult::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const { if (getMethod(MRScore)) { parser.addSymbol("ComputedTime", time); parser.addSymbol("ComputedStatus", st); parser.addSymbol("ComputedPoints", points); - return getMethod(MRScore)->evaluate(parser); + return make_pair(0, getMethod(MRScore)->evaluate(parser)); } else if (getMethodSource(MRScore).empty()) return GeneralResult::score(runner, st, time, points, asTeamMember); @@ -831,7 +887,7 @@ void DynamicResult::save(xmlparser &xml) const { void DynamicResult::clear() { parser.clear(); for (size_t k = 0; k < methods.size(); k++) { - methods[k].pn = 0; + methods[k].pn = nullptr; methods[k].source.clear(); methods[k].description.clear(); } @@ -937,6 +993,11 @@ void DynamicResult::declareSymbols(DynamicMethods m, bool clear) const { parser.declareSymbol("InputPlace", "Runner/team input place", false); parser.declareSymbol("InputPoints", "Runner/team input points", false); + parser.declareSymbol("StageStatus", "Runner/team earlier stage statuses", true); + parser.declareSymbol("StageTime", "Runner/team earlier stage running times", true); + parser.declareSymbol("StagePlace", "Runner/team earlier stage places", true); + parser.declareSymbol("StagePoints", "Runner/team earlier stage points", true); + parser.declareSymbol("Fee", "Runner/team fee", false); parser.declareSymbol("ClubId", "Club id number", false); @@ -963,6 +1024,9 @@ void DynamicResult::declareSymbols(DynamicMethods m, bool clear) const { parser.declareSymbol("LegPlace", "Place on course leg", true); parser.declareSymbol("Leg", "Leg number in team, zero indexed", false); parser.declareSymbol("BirthYear", "Year of birth", false); + parser.declareSymbol("Age", "Age (on last day of current year)", false); + parser.declareSymbol("AgeLowLimit", "Age below or equal implies youth", false); + parser.declareSymbol("AgeHighLimit", "Age above or equal implies senior/pensioner", false); parser.declareSymbol("CheckTime", "Runner check time", false); } else { @@ -997,7 +1061,9 @@ void DynamicResult::declareSymbols(DynamicMethods m, bool clear) const { parser.declareSymbol("StatusCANCEL", "Status code for cancelled entry", false); parser.declareSymbol("StatusMAX", "Status code for a time over the maximum", false); parser.declareSymbol("StatusDQ", "Status code for disqualification", false); + parser.declareSymbol("StatusOutOfCompetition", "Status code for running out-of-competition", false); parser.declareSymbol("StatusNotCompetiting", "Status code for not competing", false); + parser.declareSymbol("StatusNoTiming", "Status code for not no timing", false); parser.declareSymbol("ShortestClassTime", "Shortest time in class", false); @@ -1016,22 +1082,59 @@ void DynamicResult::getSymbolInfo(int ix, wstring &name, wstring &desc) const { parser.getSymbolInfo(ix, name, desc); } -void DynamicResult::prepareCalculations(oEvent &oe, bool prepareForTeam, int inputNumber) const { +void DynamicResult::prepareCalculations(oEvent &oe, + bool classResult, + const set &clsSelection, + vector &runners, + vector &teams, + int inputNumber) const { compile(false); - oe.calculateResults(set(), oEvent::ResultType::ClassResult); - oe.calculateResults(set(), oEvent::ResultType::TotalResult); + + + oDataConstInterface odc = oe.getDCI(); + lowAgeLimit = odc.getInt("YouthAge"); + highAgeLimit = odc.getInt("SeniorAge"); + + oEvent::ResultType single, total; + if (classResult) { + single = oEvent::ResultType::ClassResultDefault; + total = oEvent::ResultType::TotalResultDefault; + } + else { + single = oEvent::ResultType::ClassResult; + total = oEvent::ResultType::TotalResult; + } + oe.calculateResults(clsSelection, single); + oe.calculateResults(clsSelection, total); + vector *tPtr = &teams; + vector prepTeam; + if (teams.empty()) { + vector allTeam; + oe.getTeams(0, allTeam, false); + if (clsSelection.empty()) + prepTeam.swap(allTeam); + else { + for (pTeam t : allTeam) + if (clsSelection.count(t->getClassId(false))) { + prepTeam.push_back(t); + } + } + tPtr = &prepTeam; + } + + oe.calculateTeamResults(*tPtr, single); + oe.calculateTeamResults(*tPtr, total); declareSymbols(MRScore, true); - if (prepareForTeam) { + if (teams.size() > 0) { declareSymbols(MTScore, false); - vector t; - oe.getTeams(0, t, false); - for (size_t k = 0; k < t.size(); k++) - t[k]->resetResultCalcCache(); + for (pTeam t : teams) + t->resetResultCalcCache(); - oe.calculateTeamResults(false); - oe.calculateTeamResults(true); + oe.calculateTeamResults(teams, single); + oe.calculateTeamResults(teams, total); } + parser.addSymbol("StatusUnknown", StatusUnknown); parser.addSymbol("StatusOK", StatusOK); parser.addSymbol("StatusMP", StatusMP); @@ -1040,13 +1143,16 @@ void DynamicResult::prepareCalculations(oEvent &oe, bool prepareForTeam, int inp parser.addSymbol("StatusDNS", StatusDNS); parser.addSymbol("StatusMAX", StatusMAX); parser.addSymbol("StatusDQ", StatusDQ); + parser.addSymbol("StatusOutOfCompetition", StatusOutOfCompetition); + parser.addSymbol("StatusNoTiming", StatusNoTiming); parser.addSymbol("StatusNotCompetiting", StatusNotCompetiting); parser.addSymbol("MaxTime", oe.getMaximalTime()); parser.addSymbol("InputNumber", inputNumber); } -void DynamicResult::prepareCommon(oAbstractRunner &runner) const { +void DynamicResult::prepareCommon(oAbstractRunner &runner, bool classResult) const { + bool useComputed = classResult == false; parser.clearVariables(); int st = runner.getStatus(); int ft = runner.getFinishTime(); @@ -1055,12 +1161,12 @@ void DynamicResult::prepareCommon(oAbstractRunner &runner) const { parser.addSymbol("Status", st); parser.addSymbol("Start", runner.getStartTime()); parser.addSymbol("Finish", ft); - parser.addSymbol("Time", runner.getRunningTime()); + parser.addSymbol("Time", runner.getRunningTime(useComputed)); parser.addSymbol("Place", runner.getPlace()); - parser.addSymbol("Points", runner.getRogainingPoints(false)); - parser.addSymbol("PointReduction", runner.getRogainingReduction()); - parser.addSymbol("PointOvertime", runner.getRogainingOvertime()); - parser.addSymbol("PointGross", runner.getRogainingPointsGross()); + parser.addSymbol("Points", runner.getRogainingPoints(useComputed, false)); + parser.addSymbol("PointReduction", runner.getRogainingReduction(useComputed)); + parser.addSymbol("PointOvertime", runner.getRogainingOvertime(useComputed)); + parser.addSymbol("PointGross", runner.getRogainingPointsGross(useComputed)); parser.addSymbol("PointAdjustment", runner.getPointAdjustment()); parser.addSymbol("TimeAdjustment", runner.getTimeAdjustment()); @@ -1075,6 +1181,25 @@ void DynamicResult::prepareCommon(oAbstractRunner &runner) const { parser.addSymbol("InputPoints", runner.getInputPoints()); parser.addSymbol("Shorten", runner.getNumShortening()); + vector inst; + vector times; + vector points; + vector places; + vector iinst; + runner.getInputResults(inst, times, points, places); + for (RunnerStatus s : inst) + iinst.push_back(s); + + parser.addSymbol("StageStatus", iinst); + parser.addSymbol("StageTime", times); + parser.addSymbol("StagePlace", places); + parser.addSymbol("StagePoints", points); + + parser.addSymbol("InputStatus", runner.getInputStatus()); + parser.addSymbol("InputTime", runner.getInputTime()); + parser.addSymbol("InputPlace", runner.getInputPlace()); + parser.addSymbol("InputPoints", runner.getInputPoints()); + parser.addSymbol("Fee", runner.getDCI().getInt("Fee")); const pClub pc = runner.getClubRef(); @@ -1089,9 +1214,9 @@ void DynamicResult::prepareCommon(oAbstractRunner &runner) const { parser.addSymbol("Bib", _wtoi(runner.getBib().c_str())); } -void DynamicResult::prepareCalculations(oTeam &team) const { - GeneralResult::prepareCalculations(team); - prepareCommon(team); +void DynamicResult::prepareCalculations(oTeam &team, bool classResult) const { + GeneralResult::prepareCalculations(team, classResult); + prepareCommon(team, classResult); int nr = team.getNumRunners(); vector status(nr), time(nr), start(nr), finish(nr), points(nr); vector< vector > runnerOutputTimes(nr); @@ -1126,7 +1251,8 @@ void DynamicResult::prepareCalculations(oTeam &team) const { parser.removeSymbol("SplitTimesAccumulated"); parser.removeSymbol("LegTimeDeviation"); parser.removeSymbol("BirthYear"); - + parser.removeSymbol("Age"); + parser.addSymbol("RunnerOutputNumbers", runnerOutputNumbers); parser.addSymbol("RunnerOutputTimes", runnerOutputTimes); @@ -1150,13 +1276,13 @@ void DynamicResult::prepareCalculations(oTeam &team) const { pClass cls = team.getClassRef(true); if (cls) { int nl = max(1, cls->getNumStages() - 1); - parser.addSymbol("ShortestClassTime", cls->getTotalLegLeaderTime(nl, false)); + parser.addSymbol("ShortestClassTime", cls->getTotalLegLeaderTime(nl, false, false)); } } -void DynamicResult::prepareCalculations(oRunner &runner) const { - GeneralResult::prepareCalculations(runner); - prepareCommon(runner); +void DynamicResult::prepareCalculations(oRunner &runner, bool classResult) const { + GeneralResult::prepareCalculations(runner, classResult); + prepareCommon(runner, classResult); pCard pc = runner.getCard(); if (pc) { vector punches; @@ -1263,7 +1389,7 @@ void DynamicResult::prepareCalculations(oRunner &runner) const { pClass cls = runner.getClassRef(true); if (cls) { int nl = runner.getLegNumber(); - parser.addSymbol("ShortestClassTime", cls->getBestLegTime(nl)); + parser.addSymbol("ShortestClassTime", cls->getBestLegTime(nl, false)); } vector delta; vector place; @@ -1277,6 +1403,11 @@ void DynamicResult::prepareCalculations(oRunner &runner) const { parser.addSymbol("LegPlace", place); parser.addSymbol("Leg", runner.getLegNumber()); parser.addSymbol("BirthYear", runner.getBirthYear()); + int ba = runner.getBirthAge(); + parser.addSymbol("Age", ba); + parser.addSymbol("AgeLowLimit", lowAgeLimit); + parser.addSymbol("AgeHighLimit", highAgeLimit); + parser.addSymbol("CheckTime", runner.getCheckTime()); } @@ -1298,12 +1429,17 @@ long long DynamicResult::getHashCode() const { string DynamicResult::undecorateTag(const string &inputTag) { int ix = inputTag.rfind("-v"); - if (ix > 0 && ix != inputTag.npos) + int limit = inputTag.length() - 4; + if (ix > 0 && ix != inputTag.npos && ix >= limit) return inputTag.substr(0, ix); else return inputTag; } +bool DynamicResult::isRogaining() const { + return !methods[DynamicMethods::MDeduceRPoints].source.empty(); +} + wstring DynamicResult::getName(bool withAnnotation) const { if (annotation.empty() || !withAnnotation) return name; @@ -1346,7 +1482,11 @@ void GeneralResult::calculateIndividualResults(vector &runners, controlId.first == oPunch::PunchStart) { if (!totalResults) { - oe.calculateResults(set(), oEvent::ResultType::ClassResult, true); + set clsId; + for (pRunner r : runners) { + clsId.insert(r->getClassId(true)); + } + oe.calculateResults(clsId, oEvent::ResultType::ClassResult, true); for (pRunner r : runners) { ri.status = r->getStatus(); if (ri.status == StatusUnknown) { @@ -1368,8 +1508,8 @@ void GeneralResult::calculateIndividualResults(vector &runners, else ri.place = 0; - ri.score = r->getRogainingPoints(false); - ri.time = r->getRunningTime(); + ri.score = r->getRogainingPoints(true, false); + ri.time = r->getRunningTime(true); ri.src = r; results.push_back(ri); } @@ -1397,7 +1537,7 @@ void GeneralResult::calculateIndividualResults(vector &runners, else ri.place = 0; - ri.score = r->getRogainingPoints(true); + ri.score = r->getRogainingPoints(true, true); ri.time = r->getTotalRunningTime(); ri.src = r; results.push_back(ri); @@ -1432,14 +1572,14 @@ void GeneralResult::calculateIndividualResults(vector &runners, } else { wstring srcDMY; - GeneralResult *gResult = 0; + GeneralResult *gResult = nullptr; shared_ptr specialInstance; oListParam param; param.useControlIdResultTo = controlId.second; param.useControlIdResultFrom = controlId.first; if (!resTag.empty()) - gResult = &oe.getGeneralResult(resTag, srcDMY); + gResult = oe.getGeneralResult(resTag, srcDMY).get(); else { if (controlId.second == oPunch::PunchFinish && controlId.first == oPunch::PunchStart && !totalResults) @@ -1452,7 +1592,7 @@ void GeneralResult::calculateIndividualResults(vector &runners, gResult = specialInstance.get(); } gResult->setContext(¶m); - gResult->calculateIndividualResults(runners, resType, true, inputNumber); + gResult->calculateIndividualResults(runners, false, resType, true, inputNumber); gResult->clearContext(); map mapHasCourse; GeneralResultInfo ri; @@ -1510,7 +1650,7 @@ GeneralResult::calculateTeamResults(vector &teams, if (resTag.empty()) { out->useModule = false; if (controlId.second == oPunch::PunchFinish) { - oe.calculateTeamResults(false); + oe.calculateTeamResults(teams, oEvent::ResultType::ClassResult); GeneralResultInfo ri; for (pTeam r : teams) { ri.status = r->getStatus(); @@ -1524,9 +1664,9 @@ GeneralResult::calculateTeamResults(vector &teams, else ri.place = 0; - ri.score = r->getRogainingPoints(false); + ri.score = r->getRogainingPoints(true, false); ri.status = r->getStatus(); - ri.time = r->getRunningTime(); + ri.time = r->getRunningTime(true); ri.src = r; results.push_back(ri); } @@ -1555,7 +1695,7 @@ GeneralResult::calculateTeamResults(vector &teams, out->useModule = true; wstring srcDMY; auto &gResult = oe.getGeneralResult(resTag, srcDMY); - gResult.calculateTeamResults(teams, resType, true, inputNumber); + gResult->calculateTeamResults(teams, false, resType, true, inputNumber); GeneralResultInfo ri; for (pTeam r : teams) { const auto &tmp = r->getTempResult(0); @@ -1623,9 +1763,9 @@ bool GeneralResult::GeneralResultInfo::getSubResult(const BaseResultContext &con } else { if (context.controlId.second == oPunch::PunchFinish) { - out.score = r->getRogainingPoints(false); + out.score = r->getRogainingPoints(true, false); out.status = r->getStatus(); - out.time = r->getRunningTime(); + out.time = r->getRunningTime(true); } else { out.score = 0; // Undefined diff --git a/code/generalresult.h b/code/generalresult.h index 7e80ca1..9668275 100644 --- a/code/generalresult.h +++ b/code/generalresult.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -37,24 +37,25 @@ class GeneralResult { private: const oListParam *context; + mutable bool lockPrepare = false; protected: enum PrincipalSort {None, ClassWise, CourseWise}; - virtual int score(oTeam &team, RunnerStatus st, int time, int points) const; + virtual pair score(oTeam &team, RunnerStatus st, int time, int points) const; virtual RunnerStatus deduceStatus(oTeam &team) const; virtual int deduceTime(oTeam &team) const; virtual int deducePoints(oTeam &team) const; - virtual int score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const; + virtual pair score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const; virtual RunnerStatus deduceStatus(oRunner &runner) const; virtual int deduceTime(oRunner &runner, int startTime) const; virtual int deducePoints(oRunner &runner) const; - virtual void prepareCalculations(oEvent &oe, bool prepareForTeam, int inputNumber) const; - virtual void prepareCalculations(oTeam &team) const; - virtual void prepareCalculations(oRunner &runner) const; + virtual void prepareCalculations(oEvent &oe, bool classResult, const set &clsSelection, vector &runners, vector &teams, int inputNumber) const; + virtual void prepareCalculations(oTeam &team, bool classResult) const; + virtual void prepareCalculations(oRunner &runner, bool classResult) const; virtual void storeOutput(vector ×, vector &numbers) const; int getListParamTimeToControl() const; @@ -62,6 +63,10 @@ protected: public: + virtual const string &getTimeStamp() const; + + virtual bool isRogaining() const; + struct BaseResultContext { private: int leg; @@ -142,10 +147,22 @@ public: void setContext(const oListParam *context); void clearContext(); - void calculateTeamResults(vector &teams, oListInfo::ResultType resType, bool sortTeams, int inputNumber) const; - void calculateIndividualResults(vector &runners, oListInfo::ResultType resType, bool sortRunners, int inputNumber) const; + void calculateTeamResults(vector &teams, + bool classResult, + oListInfo::ResultType resType, + bool sortTeams, + int inputNumber) const; + void calculateIndividualResults(vector &runners, + bool classResult, + oListInfo::ResultType resType, + bool sortRunners, + int inputNumber) const; void sortTeamMembers(vector &runners) const; + virtual bool isDynamic() const { + return false; + } + template void sort(vector &rt, SortOrder so) const; GeneralResult(void); @@ -154,22 +171,22 @@ public: class ResultAtControl : public GeneralResult { protected: - int score(oTeam &team, RunnerStatus st, int time, int points) const; - RunnerStatus deduceStatus(oTeam &team) const; - int deduceTime(oTeam &team) const; - int deducePoints(oTeam &team) const; + pair score(oTeam &team, RunnerStatus st, int time, int points) const override; + RunnerStatus deduceStatus(oTeam &team) const override; + int deduceTime(oTeam &team) const override; + int deducePoints(oTeam &team) const override; - int score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const; - RunnerStatus deduceStatus(oRunner &runner) const; - int deduceTime(oRunner &runner, int startTime) const; - int deducePoints(oRunner &runner) const; + pair score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const override; + RunnerStatus deduceStatus(oRunner &runner) const override; + int deduceTime(oRunner &runner, int startTime) const override; + int deducePoints(oRunner &runner) const override; }; class TotalResultAtControl : public ResultAtControl { protected: - int deduceTime(oRunner &runner, int startTime) const; - RunnerStatus deduceStatus(oRunner &runner) const; - int score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const; + int deduceTime(oRunner &runner, int startTime) const override; + RunnerStatus deduceStatus(oRunner &runner) const override; + pair score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const override; }; class DynamicResult : public GeneralResult { @@ -189,11 +206,14 @@ public: }; private: - + bool allowRetag = true; static map symb2Method; static map > method2SymbName; static int instanceCount; + mutable int lowAgeLimit = -1; + mutable int highAgeLimit = 1000; + class MethodInfo { string source; mutable ParseNode *pn; @@ -221,16 +241,24 @@ private: void addSymbol(DynamicMethods method, const char *symb, const char *name); RunnerStatus toStatus(int status) const; - void prepareCommon(oAbstractRunner &runner) const; + void prepareCommon(oAbstractRunner &runner, bool classResult) const; - static string getInternalPath(const string &tag); public: + bool isDynamic() const override { + return true; + } + + bool retaggable() const { return allowRetag; } + void retaggable(bool r) { allowRetag = r; } + void setReadOnly() const {readOnly = true;} bool isReadOnly() const {return readOnly;} - const string &getTimeStamp() const {return timeStamp;} + const string &getTimeStamp() const override {return timeStamp;} + + bool isRogaining() const override; static string undecorateTag(const string &inputTag); @@ -241,20 +269,26 @@ public: void declareSymbols(DynamicMethods m, bool clear) const; - void prepareCalculations(oEvent &oe, bool prepareForTeam, int inputNumber) const; - void prepareCalculations(oTeam &team) const; - void prepareCalculations(oRunner &runner) const; + void prepareCalculations(oEvent &oe, + bool classResult, + const set &clsSelection, + vector &runners, + vector &teams, + int inputNumber) const override; + void prepareCalculations(oTeam &team, bool classResult) const override; + void prepareCalculations(oRunner &runner, bool classResult) const override; + void storeOutput(vector ×, vector &numbers) const; - int score(oTeam &team, RunnerStatus st, int time, int points) const; - RunnerStatus deduceStatus(oTeam &team) const; - int deduceTime(oTeam &team) const; - int deducePoints(oTeam &team) const; + pair score(oTeam &team, RunnerStatus st, int time, int points) const override; + RunnerStatus deduceStatus(oTeam &team) const override; + int deduceTime(oTeam &team) const override; + int deducePoints(oTeam &team) const override; - int score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const; - RunnerStatus deduceStatus(oRunner &runner) const; - int deduceTime(oRunner &runner, int startTime) const; - int deducePoints(oRunner &runner) const; + pair score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const override; + RunnerStatus deduceStatus(oRunner &runner) const override; + int deduceTime(oRunner &runner, int startTime) const override; + int deducePoints(oRunner &runner) const override; DynamicResult(); DynamicResult(const DynamicResult &resIn); @@ -294,3 +328,25 @@ public: void clear(); }; +struct GeneralResultCtr { + wstring name; + string tag; + wstring fileSource; + + bool isDynamic() const; + bool operator<(const GeneralResultCtr &c) const; + shared_ptr ptr; + + // True if implicitly loaded (form list or by class results) + bool isImplicit() const { + return fileSource == L"*"; + } + + GeneralResultCtr(const char *tag, const wstring &name, const shared_ptr &ptr); + GeneralResultCtr(wstring &file, const shared_ptr &ptr); + GeneralResultCtr() {} + + ~GeneralResultCtr(); + GeneralResultCtr(const GeneralResultCtr &ctr); + void operator=(const GeneralResultCtr &ctr); +}; diff --git a/code/guihandler.h b/code/guihandler.h index 55bec24..cc50c65 100644 --- a/code/guihandler.h +++ b/code/guihandler.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/html1.htm b/code/html1.htm index a2d5f9f..76a685a 100644 --- a/code/html1.htm +++ b/code/html1.htm @@ -429,13 +429,16 @@ This query is suitable for auto complete functionality.

  • card Card number.
  • -Returns: -Status. +

    Returns: +Status.

    + +Note:

    If the card number is registered as a rental card, it will be set as such and + hiredCard will be set in the Fee attribute. The returned fee includes any rental card fee.

     *Answer>
       *Status>OK*/Status>
    -  *Fee>130*/Fee>
    +  *Fee hiredCard="true">130*/Fee>
       *Info>Open Long, Rudolf Minowski (OK Tisaren)*/Info>
     */Answer>
     
    @@ -454,7 +457,6 @@ Status. Returns: Installed template file with the specified tag. -

    Image

    Syntax:
    /meos?image=*image>
    diff --git a/code/image.cpp b/code/image.cpp index 2472aef..512b431 100644 --- a/code/image.cpp +++ b/code/image.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/image.h b/code/image.h index c390f02..1bdff5c 100644 --- a/code/image.h +++ b/code/image.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/importformats.cpp b/code/importformats.cpp index 7ef0b26..2d597ac 100644 --- a/code/importformats.cpp +++ b/code/importformats.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/infoserver.cpp b/code/infoserver.cpp index 1d41f41..bb179db 100644 --- a/code/infoserver.cpp +++ b/code/infoserver.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -488,10 +488,14 @@ bool InfoBaseCompetitor::synchronizeBase(oAbstractRunner &bc) { ch = true; } - int s = bc.getStatus(); - int rt = bc.getRunningTime() * 10; - if (rt > 0 && s == RunnerStatus::StatusUnknown) - s = RunnerStatus::StatusOK; + RunnerStatus s = bc.getStatus(); + int rt = bc.getRunningTime(true) * 10; + if (rt > 0) { + if (s == RunnerStatus::StatusUnknown) + s = RunnerStatus::StatusOK; + } + else if (isPossibleResultStatus(s)) + s = StatusUnknown; if (status != s) { status = s; @@ -547,8 +551,8 @@ bool InfoCompetitor::synchronize(bool useTotalResults, bool useCourse, oRunner & s = r.getTotalStatusInput(); } else if (t && !isQF && r.getLegNumber() > 0) { - legInput = t->getLegRunningTime(r.getLegNumber() - 1, false) * 10; - s = t->getLegStatus(r.getLegNumber() - 1, false); + legInput = t->getLegRunningTime(r.getLegNumber() - 1, true, false) * 10; + s = t->getLegStatus(r.getLegNumber() - 1, true, false); } if (totalStatus != s) { @@ -569,9 +573,29 @@ bool InfoCompetitor::synchronize(bool useTotalResults, bool useCourse, oRunner & bool InfoCompetitor::synchronize(const InfoCompetition &cmp, oRunner &r) { bool useTotalResults = cmp.includeTotalResults(); bool inludeCourse = cmp.includeCourse(); - bool ch = synchronize(useTotalResults, inludeCourse, r); + int cno = r.getCardNo(); + if (cno != cardNo) { + cardNo = cno; + changeCard = true; + ch = true; + } + + bool nr; + if ((r.getStatus() == StatusUnknown || r.getStatus() == StatusOutOfCompetition || r.getStatus() == StatusNoTiming) && r.getFinishTime() <= 0) { + vector pv; + r.getEvent()->getPunchesForRunner(r.getId(), false, pv); + nr = pv.size() > 0; + } + else { + nr = false; + } + if (isRunning != nr) { + isRunning = nr; + ch = true; + } + vector newRT; if (r.getClassId(false) > 0) { const vector &radios = cmp.getControls(r.getClassId(true), r.getLegNumber()); @@ -601,7 +625,16 @@ bool InfoCompetitor::synchronize(const InfoCompetition &cmp, oRunner &r) { void InfoCompetitor::serialize(xmlbuffer &xml, bool diffOnly) const { vector< pair > sprop; - sprop.push_back(make_pair("id", itow(getId()))); + sprop.reserve(3); + sprop.emplace_back("id", itow(getId())); + + if (changeCard || !diffOnly) { + sprop.emplace_back("card", itow(cardNo)); + changeCard = false; + } + if (isRunning) + sprop.emplace_back("competing", L"true"); + xmlbuffer &subTag = xml.startTag("cmp", sprop); InfoBaseCompetitor::serialize(subTag, diffOnly, course); @@ -627,7 +660,6 @@ void InfoCompetitor::serialize(xmlbuffer &xml, bool diffOnly) const { xml.endTag(); } - bool InfoTeam::synchronize(oTeam &t) { bool ch = synchronizeBase(t); diff --git a/code/infoserver.h b/code/infoserver.h index 5392176..b804aa3 100644 --- a/code/infoserver.h +++ b/code/infoserver.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -182,9 +182,12 @@ class InfoCompetitor : public InfoBaseCompetitor { int inputTime; int totalStatus; int course; + int cardNo = 0; + bool isRunning = false; bool synchronize(const InfoCompetition &cmp, oRunner &c); bool changeTotalSt; bool changeRadio; + mutable bool changeCard = false; public: bool synchronize(bool useTotalResults, bool useCourse, oRunner &c); void serialize(xmlbuffer &xml, bool diffOnly) const; diff --git a/code/inthashmap.h b/code/inthashmap.h index 5b01b25..22c487c 100644 --- a/code/inthashmap.h +++ b/code/inthashmap.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/intkeymap.hpp b/code/intkeymap.hpp index 1db8275..07a4982 100644 --- a/code/intkeymap.hpp +++ b/code/intkeymap.hpp @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/intkeymapimpl.hpp b/code/intkeymapimpl.hpp index 73c0156..a821069 100644 --- a/code/intkeymapimpl.hpp +++ b/code/intkeymapimpl.hpp @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/iof30interface.cpp b/code/iof30interface.cpp index be54c42..69b5fac 100644 --- a/code/iof30interface.cpp +++ b/code/iof30interface.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -1236,7 +1236,8 @@ void IOF30Interface::readEvent(gdioutput &gdi, const xmlobject &xo, wstring name; xo.getObjectString("Name", name); - oe.setName(name); + if (!oe.hasFlag(oEvent::TransferFlags::FlagManualName)) + oe.setName(name, false); int id = xo.getObjectInt("Id"); if (id>0) { @@ -1264,11 +1265,13 @@ void IOF30Interface::readEvent(gdioutput &gdi, const xmlobject &xo, int zt = t - 3600; if (zt < 0) zt += 3600*24; - oe.setZeroTime(formatTimeHMS(zt)); + + if (!oe.hasFlag(oEvent::TransferFlags::FlagManualDateTime)) + oe.setZeroTime(formatTimeHMS(zt), false); } } - - oe.setDate(dateStr); + if (!oe.hasFlag(oEvent::TransferFlags::FlagManualDateTime)) + oe.setDate(dateStr, false); //oe.setZeroTime(...); } @@ -1425,16 +1428,16 @@ pTeam IOF30Interface::readTeamEntry(gdioutput &gdi, xmlobject &xTeam, if (!matchStageFilter(stageFilter, races)) return 0; - bool newTeam; - pTeam t = getCreateTeam(gdi, xTeam, newTeam); - - if (!t) - return 0; - // Class map > localTeamClassConfig; pClass pc = readClass(xTeam.getObject("Class"), localTeamClassConfig); + bool newTeam; + pTeam t = getCreateTeam(gdi, xTeam, pc ? pc->getId() : 0, newTeam); + + if (!t) + return 0; + if (pc && (t->getClassId(false) == 0 || !t->hasFlag(oAbstractRunner::FlagUpdateClass)) ) { t->setClassId(pc->getId(), false); } @@ -1443,11 +1446,11 @@ pTeam IOF30Interface::readTeamEntry(gdioutput &gdi, xmlobject &xTeam, wchar_t pat[32]; int no = oClass::extractBibPattern(bib, pat); if (no > 0 && t->getBib().empty()) - t->setBib(bib, no, true, false); + t->setBib(bib, no, true); else if (newTeam) { pair autoBib = pc->getNextBib(bibPatterns); if (autoBib.first > 0) { - t->setBib(autoBib.second, autoBib.first, true, false); + t->setBib(autoBib.second, autoBib.first, true); } } @@ -1486,8 +1489,8 @@ pTeam IOF30Interface::readTeamEntry(gdioutput &gdi, xmlobject &xTeam, for (size_t k = 0; ksynchronize(); + t->applyBibs(); + t->evaluate(oBase::ChangeType::Update); return t; } @@ -1495,7 +1498,7 @@ pTeam IOF30Interface::readTeamStart(gdioutput &gdi, pClass pc, xmlobject &xTeam, map > &bibPatterns, const map > &teamClassConfig) { bool newTeam; - pTeam t = getCreateTeam(gdi, xTeam, newTeam); + pTeam t = getCreateTeam(gdi, xTeam, pc ? pc->getId() : 0, newTeam); if (!t) return 0; @@ -1509,11 +1512,11 @@ pTeam IOF30Interface::readTeamStart(gdioutput &gdi, pClass pc, xmlobject &xTeam, wchar_t pat[32]; int no = oClass::extractBibPattern(bib, pat); if (no > 0 && t->getBib().empty()) - t->setBib(bib, no, true, false); + t->setBib(bib, no, true); else if (newTeam){ pair autoBib = pc->getNextBib(bibPatterns); if (autoBib.first > 0) { - t->setBib(autoBib.second, autoBib.first, true, false); + t->setBib(autoBib.second, autoBib.first, true); } } xmlList xEntries; @@ -1522,12 +1525,12 @@ pTeam IOF30Interface::readTeamStart(gdioutput &gdi, pClass pc, xmlobject &xTeam, for (size_t k = 0; ksynchronize(); + t->applyBibs(); + t->evaluate(oBase::ChangeType::Update); return t; } -pTeam IOF30Interface::getCreateTeam(gdioutput &gdi, const xmlobject &xTeam, bool &newTeam) { +pTeam IOF30Interface::getCreateTeam(gdioutput &gdi, const xmlobject &xTeam, int expectedClassId, bool &newTeam) { newTeam = false; wstring name; xTeam.getObjectString("Name", name); @@ -1542,7 +1545,8 @@ pTeam IOF30Interface::getCreateTeam(gdioutput &gdi, const xmlobject &xTeam, bool t = oe.getTeam(id); else { t = oe.getTeamByName(name); - // XXX Check class + if (t && expectedClassId > 0 && t->getClassId(false) != expectedClassId) + t = nullptr; } if (!t) { if (id > 0) { @@ -1825,7 +1829,7 @@ pRunner IOF30Interface::readPersonStart(gdioutput &gdi, pClass pc, xmlobject &xo starts[k].getObjectString("BibNumber", bib); rRace->getDI().setString("Bib", bib); - rRace->setStartTime(parseISO8601Time(startTime), true, false); + rRace->setStartTime(parseISO8601Time(startTime), true, oBase::ChangeType::Update); } } @@ -2301,14 +2305,15 @@ pClass IOF30Interface::readClass(const xmlobject &xclass, pc = oe.addClass(name); oDataInterface DI = pc->getDI(); - - if (!longName.empty()) { - pc->setName(name); - DI.setString("LongName", longName); - } - else { - if (pc->getName() != name && DI.getString("LongName") != name) - pc->setName(name); + if (!pc->hasFlag(oClass::TransferFlags::FlagManualName)) { + if (!longName.empty()) { + pc->setName(name, false); + DI.setString("LongName", longName); + } + else { + if (pc->getName() != name && DI.getString("LongName") != name) + pc->setName(name, false); + } } xmlList legs; xclass.getObjects("Leg", legs); @@ -2666,46 +2671,8 @@ void IOF30Interface::writeResultList(xmlparser &xml, const set &classes, oe.getClasses(c, false); for (size_t k = 0; k < c.size(); k++) { -// bool indRel = c[k]->getClassType() == oClassIndividRelay; if (classes.empty() || classes.count(c[k]->getId())) { - /* oe.getRunners(c[k]->getId(), r, false); - vector rToUse; - rToUse.reserve(r.size()); - - for (size_t j = 0; j < r.size(); j++) { - if (leg == -1 || leg == r[j]->getLegNumber()) { - if (leg == -1 && indRel && r[j]->getLegNumber() != 0) - continue; // Skip all but leg 0 for individual relay - - if (leg == -1 && !indRel && r[j]->getTeam()) - continue; // For teams, skip presonal results, unless individual relay - - if (r[j]->getStatus() == StatusUnknown) - continue; - - rToUse.push_back(r[j]); - } - } - - vector tToUse; - - if (leg == -1) { - oe.getTeams(c[k]->getId(), t, false); - tToUse.reserve(t.size()); - - for (size_t j = 0; j < t.size(); j++) { - for (int n = 0; n < t[j]->getNumRunners(); n++) { - pRunner tr = t[j]->getRunner(n); - if (tr && tr->getStatus() != StatusUnknown) { - tToUse.push_back(t[j]); - break; - } - } - } - - } - */ vector rToUse; vector tToUse; getRunnersToUse(c[k], rToUse, tToUse, leg, false); @@ -2716,7 +2683,6 @@ void IOF30Interface::writeResultList(xmlparser &xml, const set &classes, } } - xml.endTag(); } @@ -2801,8 +2767,11 @@ void IOF30Interface::writeCourseInfo(xmlparser &xml, const oCourse &c) { } -wstring formatStatus(RunnerStatus st) { +wstring formatStatus(RunnerStatus st, bool hasTime) { switch (st) { + case StatusNoTiming: + if (!hasTime) + break; case StatusOK: return L"OK"; case StatusDNS: @@ -2817,11 +2786,13 @@ wstring formatStatus(RunnerStatus st) { return L"Disqualified"; case StatusMAX: return L"OverTime"; + case StatusOutOfCompetition: + if (!hasTime) + break; case StatusNotCompetiting: return L"NotCompeting"; - default: - return L"Inactive"; } + return L"Inactive"; } void IOF30Interface::writePersonResult(xmlparser &xml, const oRunner &r, @@ -2890,6 +2861,8 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o if (teamMember) writeLegOrder(xml, rPerson.getClassRef(false), rPerson.getLegNumber()); + bool patrolResult = r.getTeam() && r.getClassRef(false)->getClassType() == oClassPatrol && !teamsAsIndividual; + wstring bib = rPerson.getBib(); if (!bib.empty()) xml.write("BibNumber", bib); @@ -2897,21 +2870,47 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o if (r.getStartTime() > 0) xml.write("StartTime", oe.getAbsDateTimeISO(r.getStartTime(), true, useGMT)); + int finishTime, runningTime, place, after; + RunnerStatus status; + if (!patrolResult) { + place = r.getPlace(); + finishTime = r.getFinishTimeAdjusted(); + runningTime = r.getRunningTime(true); + after = r.getTimeAfter(); + status = r.getStatusComputed(); + } + else { + int pl = r.getParResultLeg(); + place = r.getTeam()->getLegPlace(pl, false); + runningTime = r.getTeam()->getLegRunningTime(pl, true, false); + if (runningTime > 0) + finishTime = r.getStartTime() + runningTime; + else + finishTime = 0; + + after = r.getTeam()->getTimeAfter(pl); + status = r.getTeam()->getLegStatus(pl, true, false); + } + + if ((r.getClassRef(false) && r.getClassRef(true)->getNoTiming()) || + r.getStatusComputed() == StatusNoTiming) { + after = -1; + runningTime = 0; + } + if (r.getFinishTime() > 0) - xml.write("FinishTime", oe.getAbsDateTimeISO(r.getFinishTimeAdjusted(), true, useGMT)); + xml.write("FinishTime", oe.getAbsDateTimeISO(finishTime, true, useGMT)); - if (r.getRunningTime() > 0) - xml.write("Time", r.getRunningTime()); + if (runningTime > 0) + xml.write("Time", runningTime); - int after = r.getTimeAfter(); if (after >= 0) { if (teamMember) { xml.write("TimeBehind", "type", L"Leg", itow(after)); - after = r.getTimeAfterCourse(); - if (after >= 0) - xml.write("TimeBehind", "type", L"Course", itow(after)); - + int afterCourse = r.getTimeAfterCourse(); + if (afterCourse >= 0) + xml.write("TimeBehind", "type", L"Course", itow(afterCourse)); } else xml.write("TimeBehind", after); @@ -2919,27 +2918,26 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o if (r.getClassRef(false)) { - if (r.statusOK() && r.getClassRef(false)->getNoTiming() == false) { - if (!teamMember && r.getPlace() > 0 && r.getPlace() < 50000) { - xml.write("Position", r.getPlace()); + if (r.statusOK(true) && r.getClassRef(false)->getNoTiming() == false) { + if (!teamMember && place > 0 && place < 50000) { + xml.write("Position", place); } else if (teamMember) { - int pos = r.getPlace(); - if (pos > 0 && pos < 50000) - xml.write("Position", "type", L"Leg", itow(pos)); + if (place > 0 && place < 50000) + xml.write("Position", "type", L"Leg", itow(place)); - pos = r.getCoursePlace(); - if (pos > 0) - xml.write("Position", "type", L"Course", itow(pos)); + int placeCourse = r.getCoursePlace(true); + if (placeCourse > 0) + xml.write("Position", "type", L"Course", itow(placeCourse)); } } - xml.write("Status", formatStatus(r.getStatus())); + xml.write("Status", formatStatus(status, r.getFinishTime()>0)); - int rg = r.getRogainingPoints(false); + int rg = r.getRogainingPoints(true, false); if (rg > 0) { xml.write("Score", "type", L"Score", itow(rg)); - xml.write("Score", "type", L"Penalty", itow(r.getRogainingReduction())); + xml.write("Score", "type", L"Penalty", itow(r.getRogainingReduction(true))); } if ( (r.getTeam() && r.getClassRef(false)->getClassType() != oClassPatrol && !teamsAsIndividual) || hasInputTime) { xml.startTag("OverallResult"); @@ -2953,7 +2951,7 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o int tleg = r.getLegNumber() >= 0 ? r.getLegNumber() : 0; if (stat == StatusOK && hasTiming) { - int after = r.getTotalRunningTime() - r.getClassRef(true)->getTotalLegLeaderTime(tleg, true); + int after = r.getTotalRunningTime() - r.getClassRef(true)->getTotalLegLeaderTime(tleg, true, true); if (after >= 0) xml.write("TimeBehind", after); } @@ -2961,7 +2959,7 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o if (stat == StatusOK && hasTiming) xml.write("Position", r.getTotalPlace()); - xml.write("Status", formatStatus(stat)); + xml.write("Status", formatStatus(stat, r.getFinishTime() > 0)); xml.endTag(); } @@ -2972,9 +2970,10 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o writeCourse(xml, *crs); const vector &sp = r.getSplitTimes(doUnroll); - if (r.getStatus()>0 && r.getStatus() != StatusDNS && - r.getStatus() != StatusCANCEL && - r.getStatus() != StatusNotCompetiting) { + RunnerStatus st = r.getStatusComputed(); + if (r.getStatus()>0 && st != StatusDNS && + st != StatusCANCEL && + st != StatusNotCompetiting) { int nc = crs->getNumControls(); bool hasRogaining = crs->hasRogaining(); int firstControl = crs->useFirstAsStart() ? 1 : 0; @@ -3264,7 +3263,7 @@ void IOF30Interface::getRunnersToUse(const pClass cls, vector &rToUse, if (leg == -1 && !indRel && r[j]->getTeam()) continue; // For teams, skip presonal results, unless individual relay - if (!includeUnknown && r[j]->getStatus() == StatusUnknown) + if (!includeUnknown && !r[j]->hasResult()) continue; } rToUse.push_back(r[j]); @@ -3281,7 +3280,7 @@ void IOF30Interface::getRunnersToUse(const pClass cls, vector &rToUse, else { for (int n = 0; n < t[j]->getNumRunners(); n++) { pRunner tr = t[j]->getRunner(n); - if (tr && tr->getStatus() != StatusUnknown) { + if (tr && tr->hasResult()) { tToUse.push_back(t[j]); break; } diff --git a/code/iof30interface.h b/code/iof30interface.h index 637785e..4f07eda 100644 --- a/code/iof30interface.h +++ b/code/iof30interface.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -163,7 +163,7 @@ class IOF30Interface { map > &bibPatterns, const map > &teamClassConfig); - pTeam getCreateTeam(gdioutput &gdi, const xmlobject &xTeam, bool &newTeam); + pTeam getCreateTeam(gdioutput &gdi, const xmlobject &xTeam, int expectedClassId, bool &newTeam); static int getIndexFromLegPos(int leg, int legorder, const vector &setup); diff --git a/code/license.txt b/code/license.txt index 5f4e616..6754e03 100644 --- a/code/license.txt +++ b/code/license.txt @@ -7,7 +7,7 @@ Third Party Code. Additional copyright notices and license terms applicable to p All trademarks and registered trademarks mentioned herein are the property of their respective owners. ------------------------------------ -Copyright 2007-2019 Melin Software HB. +Copyright 2007-2020 Melin Software HB. ------------------------------------ diff --git a/code/listeditor.cpp b/code/listeditor.cpp index 381c0f1..3697e46 100644 --- a/code/listeditor.cpp +++ b/code/listeditor.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -34,6 +34,7 @@ #include "oEvent.h" #include "tabbase.h" #include "CommDlg.h" +#include "generalresult.h" ListEditor::ListEditor(oEvent *oe_) { oe = oe_; @@ -42,7 +43,7 @@ ListEditor::ListEditor(oEvent *oe_) { dirtyExt = false; dirtyInt = false; lastSaved = NotSaved; - oe->loadGeneralResults(false); + oe->loadGeneralResults(false, true); } ListEditor::~ListEditor() { @@ -78,6 +79,13 @@ void ListEditor::load(const MetaListContainer &mlc, int index) { savedFileName.clear(); } +void ListEditor::show(TabBase *dst, gdioutput &gdi) { + + gdi.clearPage(false); + origin = dst; + show(gdi); +} + void ListEditor::show(gdioutput &gdi) { gdi.setRestorePoint("BeginListEdit"); @@ -452,7 +460,7 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) { else mlp.setLeg(-1); - if (gdi.hasField("UseResultModule") && gdi.isChecked("UseResultModule")) + if (gdi.hasWidget("UseResultModule") && gdi.isChecked("UseResultModule")) mlp.setResultModule(currentList->getResultModule()); else mlp.setResultModule(""); @@ -604,7 +612,7 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) { lastSaved = SavedFile; savedFileName = fileName; - oe->loadGeneralResults(true); + oe->loadGeneralResults(true, true); makeDirty(gdi, ClearDirty, ClearDirty); show(gdi); } @@ -700,7 +708,12 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) { makeDirty(gdi, ClearDirty, ClearDirty); currentIndex = -1; savedFileName.clear(); - gdi.getTabs().get(TListTab)->loadPage(gdi); + + if (origin) { + auto oc = origin; + origin = nullptr; + oc->loadPage(gdi); + } return 0; } /*else if (bi.id == "BrowseFont") { @@ -732,9 +745,12 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) { if (type == lResultModuleNumber || type == lResultModuleTime || type == lResultModuleNumberTeam || type == lResultModuleTimeTeam) { gdi.check("UseLeg", true); - gdi.check("UseResultModule", true); - gdi.disableInput("UseResultModule"); gdi.disableInput("UseLeg"); + + if (gdi.hasWidget("UseResultModule")) { + gdi.check("UseResultModule", true); + gdi.disableInput("UseResultModule"); + } gdi.enableInput("Leg"); if (gdi.getText("Leg").empty()) gdi.setText("Leg", L"0"); @@ -887,9 +903,11 @@ void ListEditor::editListPost(gdioutput &gdi, const MetaListPost &mlp, int id) { if (storedType == lResultModuleNumber || storedType == lResultModuleTime || storedType == lResultModuleTimeTeam || storedType == lResultModuleNumberTeam) { gdi.check("UseLeg", true); - gdi.check("UseResultModule", true); - gdi.disableInput("UseResultModule"); gdi.disableInput("UseLeg"); + if (gdi.hasWidget("UseResultModule")) { + gdi.check("UseResultModule", true); + gdi.disableInput("UseResultModule"); + } } gdi.dropLine(2); @@ -1174,11 +1192,11 @@ void ListEditor::makeDirty(gdioutput &gdi, DirtyFlag inside, DirtyFlag outside) else if (outside == ClearDirty) dirtyExt = false; - if (gdi.hasField("SaveInside")) { + if (gdi.hasWidget("SaveInside")) { gdi.setInputStatus("SaveInside", dirtyInt || lastSaved != SavedInside); } - if (gdi.hasField("SaveFile")) { + if (gdi.hasWidget("SaveFile")) { gdi.setInputStatus("SaveFile", dirtyExt || lastSaved != SavedFile); } } diff --git a/code/listeditor.h b/code/listeditor.h index d004258..aeabdb9 100644 --- a/code/listeditor.h +++ b/code/listeditor.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -30,6 +30,8 @@ class BaseInfo; class ButtonInfo; class oEvent; enum EPostType; +class TabBase; + #include class ListEditor { @@ -65,6 +67,10 @@ private: void enableOpen(gdioutput &gdi); void makeDirty(gdioutput &gdi, DirtyFlag inside, DirtyFlag outside); + + TabBase *origin = nullptr; + void show(gdioutput &gdi); + public: ListEditor(oEvent *oe); virtual ~ListEditor(); @@ -72,7 +78,9 @@ public: //void load(MetaList *list); void load(const MetaListContainer &mlc, int index); - void show(gdioutput &gdi); + void show(TabBase *dst, gdioutput &gdi); + + bool isShown(TabBase *tab) const { return origin == tab; } MetaList *getCurrentList() const {return currentList;}; diff --git a/code/liveresult.cpp b/code/liveresult.cpp index 50fab00..f77d8d3 100644 --- a/code/liveresult.cpp +++ b/code/liveresult.cpp @@ -1,6 +1,6 @@ /********************i**************************************************** MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -284,7 +284,7 @@ void LiveResult::handle(gdioutput &gdi, BaseInfo &bu, GuiEventType type) { pRunner r = oe->getRunner(rToWatch[i], 0); if (r) { r->synchronize(); - r->setStartTime(st, true, false, true); + r->setStartTime(st, true, oBase::ChangeType::Update, true); r->synchronize(false); } } diff --git a/code/liveresult.h b/code/liveresult.h index f20d847..dc88b07 100644 --- a/code/liveresult.h +++ b/code/liveresult.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/localizer.cpp b/code/localizer.cpp index a344784..d86c6e4 100644 --- a/code/localizer.cpp +++ b/code/localizer.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/localizer.h b/code/localizer.h index 5b00ad8..85aab30 100644 --- a/code/localizer.h +++ b/code/localizer.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/meos.cpp b/code/meos.cpp index 63bb34d..4fb53eb 100644 --- a/code/meos.cpp +++ b/code/meos.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -67,6 +67,7 @@ #include "autocomplete.h" #include "image.h" #include "csvparser.h" +#include "generalresult.h" int defaultCodePage = 1252; @@ -221,17 +222,26 @@ int APIENTRY WinMain(HINSTANCE hInstance, RunnerStatusOrderMap[k] = 0; } RunnerStatusOrderMap[StatusOK] = 0; - RunnerStatusOrderMap[StatusMAX] = 1; - RunnerStatusOrderMap[StatusMP] = 2; - RunnerStatusOrderMap[StatusDNF] = 3; - RunnerStatusOrderMap[StatusDQ] = 4; - RunnerStatusOrderMap[StatusCANCEL] = 5; - RunnerStatusOrderMap[StatusDNS] = 6; - RunnerStatusOrderMap[StatusUnknown] = 7; - RunnerStatusOrderMap[StatusNotCompetiting] = 8; + RunnerStatusOrderMap[StatusNoTiming] = 1; + RunnerStatusOrderMap[StatusOutOfCompetition] = 2; + + RunnerStatusOrderMap[StatusMAX] = 3; + RunnerStatusOrderMap[StatusMP] = 4; + RunnerStatusOrderMap[StatusDNF] = 5; + RunnerStatusOrderMap[StatusDQ] = 6; + RunnerStatusOrderMap[StatusCANCEL] = 7; + RunnerStatusOrderMap[StatusDNS] = 8; + RunnerStatusOrderMap[StatusUnknown] = 9; + RunnerStatusOrderMap[StatusNotCompetiting] = 10; lang.init(); StringCache::getInstance().init(); + + for (RunnerStatus st : getAllRunnerStatus()) { + if (st != StatusOK) + assert(RunnerStatusOrderMap[st] > 0); + oAbstractRunner::encodeStatus(st); + } GetCurrentDirectory(MAX_PATH, programPath); bool utfRecode = false; @@ -469,14 +479,14 @@ int APIENTRY WinMain(HINSTANCE hInstance, } } } - + gdi_main = nullptr; gdi_extra.clear(); if (gEvent) gEvent->saveProperties(settings); delete gEvent; - gEvent = 0; + gEvent = nullptr; initMySQLCriticalSection(false); diff --git a/code/meos.rc b/code/meos.rc index d0cc44d..631bfdf 100644 --- a/code/meos.rc +++ b/code/meos.rc @@ -169,7 +169,7 @@ BEGIN VALUE "FileDescription", "meos" VALUE "FileVersion", "3.3.0.1" VALUE "InternalName", "meos" - VALUE "LegalCopyright", "Copyright © 2007-2019" + VALUE "LegalCopyright", "Copyright © 2007-2020" VALUE "OriginalFilename", "meos.exe" VALUE "ProductName", " meos" VALUE "ProductVersion", "3.4.0.1" diff --git a/code/meos_util.cpp b/code/meos_util.cpp index 7b28af5..bce3454 100644 --- a/code/meos_util.cpp +++ b/code/meos_util.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -2212,3 +2212,8 @@ void checkWriteAccess(const wstring &file) { } CloseHandle(h); } + +int compareStringIgnoreCase(const wstring &a, const wstring &b) { + return CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, a.c_str(), a.length(), + b.c_str(), b.length()) - CSTR_EQUAL; +} diff --git a/code/meos_util.h b/code/meos_util.h index 9b58a2d..4363cc0 100644 --- a/code/meos_util.h +++ b/code/meos_util.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -119,6 +119,9 @@ const vector &split(const string &line, const string &separators, vector const vector &split(const wstring &line, const wstring &separators, vector &split_vector); const wstring &unsplit(const vector &split_vector, const wstring &separators, wstring &line); +// Compare two strings, ignore case. 0 = equal, != zero compares as the integers. +int compareStringIgnoreCase(const wstring &a, const wstring &b); + const wstring &makeDash(const wstring &t); const wstring &makeDash(const wchar_t *t); diff --git a/code/meosdb/MeosSQL.cpp b/code/meosdb/MeosSQL.cpp index 53a3d6a..8a879fd 100644 --- a/code/meosdb/MeosSQL.cpp +++ b/code/meosdb/MeosSQL.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -37,6 +37,7 @@ #include "../metalist.h" #include "../MeOSFeatures.h" #include "../meosexception.h" +#include "../generalresult.h" using namespace mysqlpp; @@ -394,7 +395,7 @@ void MeosSQL::upgradeDB(const string &db, oDataContainer const * dc) { if (!eCol.count("InputTime")) { string sql = "ALTER TABLE " + db + " "; sql += "ADD COLUMN " + C_INT("InputTime"); - sql += "ADD COLUMN " + C_INT("InputStatus"); + sql += "ADD COLUMN InputStatus INT NOT NULL DEFAULT 1, "; sql += "ADD COLUMN " + C_INT("InputPoints"); sql += "ADD COLUMN " + C_INT("InputPlace"); sql = sql.substr(0, sql.length() - 2); @@ -715,7 +716,6 @@ OpFailStatus MeosSQL::SyncUpdate(oEvent *oe) << " WHERE Id=" << oe->Id; queryset.execute(); - //syncUpdate(queryset, "oEvent", oe, true); } catch (const mysqlpp::Exception& er){ setDefaultDB(); @@ -1126,7 +1126,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { } pw.setProgress(20); - + oe->sqlClubs.reset(); try { ResUse res = query.use("SELECT * FROM oClub WHERE Removed=0"); @@ -1139,9 +1139,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { oClub c(oe, row["Id"]); storeClub(row, c); oe->addClub(c); - - oe->sqlUpdateClubs = max(c.sqlUpdated, oe->sqlUpdateClubs); - oe->sqlCounterClubs = max(c.counter, oe->sqlCounterClubs); + c.update(oe->sqlClubs); } } } @@ -1154,7 +1152,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { pw.setProgress(30); - oe->sqlCounterControls=0; + oe->sqlControls.reset(); try { ResUse res = query.use("SELECT * FROM oControl WHERE Removed=0"); @@ -1166,9 +1164,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { oControl c(oe, row["Id"]); storeControl(row, c); oe->addControl(c); - - oe->sqlUpdateControls = max(c.sqlUpdated, oe->sqlUpdateControls); - oe->sqlCounterControls = max(c.counter, oe->sqlCounterControls); + c.update(oe->sqlControls); } } } @@ -1179,7 +1175,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { return opStatusFail; } - oe->sqlCounterCourses = 0; + oe->sqlCourses.reset(); pw.setProgress(40); try{ @@ -1193,9 +1189,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { oCourse c(oe, row["Id"]); storeCourse(row, c, tmp, false); oe->addCourse(c); - - oe->sqlUpdateCourses = max(c.sqlUpdated, oe->sqlUpdateCourses); - oe->sqlCounterCourses = max(c.counter, oe->sqlCounterCourses); + c.update(oe->sqlCourses); } } } @@ -1208,7 +1202,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { pw.setProgress(50); - oe->sqlCounterClasses = 0; + oe->sqlClasses.reset(); try{ ResUse res = query.use("SELECT * FROM oClass WHERE Removed=0"); @@ -1217,13 +1211,9 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { while (row = res.fetch_row()) { oClass c(oe, row["Id"]); storeClass(row, c, false, false); - c.changed = false; - c.reChanged = false; oe->addClass(c); - - oe->sqlUpdateClasses = max(c.sqlUpdated, oe->sqlUpdateClasses); - oe->sqlCounterClasses = max(c.counter, oe->sqlCounterClasses); + c.update(oe->sqlClasses); } } } @@ -1234,7 +1224,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { return opStatusFail; } - oe->sqlCounterCards=0; + oe->sqlCards.reset(); try{ ResUse res = query.use("SELECT * FROM oCard WHERE Removed=0"); @@ -1250,9 +1240,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { oe->addCard(c); assert(!c.changed); - oe->sqlCounterCards = max(c.counter, oe->sqlCounterCards); - oe->sqlUpdateCards = max(c.sqlUpdated, oe->sqlUpdateCards); - + c.update(oe->sqlCards); if (++counter % 100 == 50) pw.setProgress(pStart + (counter * pPart) / nCard); } @@ -1266,7 +1254,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { return opStatusFail; } - oe->sqlCounterRunners = 0; + oe->sqlRunners.reset(); try{ ResUse res = query.use("SELECT * FROM oRunner WHERE Removed=0"); int counter = 0; @@ -1280,9 +1268,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { storeRunner(row, r, false, false, false, false); assert(!r.changed); oe->addRunner(r, false); - - oe->sqlUpdateRunners = max(r.sqlUpdated, oe->sqlUpdateRunners); - oe->sqlCounterRunners = max(r.counter,oe->sqlCounterRunners); + r.update(oe->sqlRunners); if (++counter % 100 == 50) pw.setProgress(pStart + (counter * pPart) / nRunner); @@ -1296,7 +1282,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { return opStatusFail; } - oe->sqlCounterTeams=0; + oe->sqlTeams.reset(); try{ ResUse res = query.use("SELECT * FROM oTeam WHERE Removed=0"); @@ -1314,7 +1300,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { pTeam at = oe->addTeam(t, false); if (at) { - at->apply(false, 0, true); + at->apply(oBase::ChangeType::Quiet, nullptr); at->changed = false; for (size_t k = 0; kRunners.size(); k++) { if (at->Runners[k]) { @@ -1323,9 +1309,8 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { } } } - oe->sqlUpdateTeams = max(t.sqlUpdated, oe->sqlUpdateTeams); - oe->sqlCounterTeams = max(t.counter, oe->sqlCounterTeams); - + t.update(oe->sqlTeams); + if (++counter % 100 == 50) pw.setProgress(pStart + (counter * pPart) / nTeam); } @@ -1441,7 +1426,7 @@ void MeosSQL::storeClub(const Row &row, oClub &c) c.counter = row["Counter"]; c.Removed = row["Removed"]; - c.oe->sqlChangedClubs = true; + c.oe->sqlClubs.changed = true; c.changedObject(); synchronized(c); @@ -1459,7 +1444,7 @@ void MeosSQL::storeControl(const Row &row, oControl &c) c.counter = row["Counter"]; c.Removed = row["Removed"]; - c.oe->sqlChangedControls = true; + c.oe->sqlControls.changed = true; if (c.changed || oldStat != c.Status) { c.oe->dataRevision++; c.changed = false; @@ -1487,7 +1472,7 @@ void MeosSQL::storeCard(const Row &row, oCard &c) } c.changedObject(); synchronized(c); - c.oe->sqlChangedCards = true; + c.oe->sqlCards.changed = true; } void MeosSQL::storePunch(const Row &row, oFreePunch &p, bool rehash) @@ -1509,7 +1494,7 @@ void MeosSQL::storePunch(const Row &row, oFreePunch &p, bool rehash) p.changedObject(); synchronized(p); - p.oe->sqlChangedPunches = true; + p.oe->sqlPunches.changed = true; } OpFailStatus MeosSQL::storeClass(const Row &row, oClass &c, @@ -1570,7 +1555,7 @@ OpFailStatus MeosSQL::storeClass(const Row &row, oClass &c, c.changed = false; c.changedObject(); - c.oe->sqlChangedClasses = true; + c.oe->sqlClasses.changed = true; synchronized(c); return success; } @@ -1611,7 +1596,7 @@ OpFailStatus MeosSQL::storeCourse(const Row &row, oCourse &c, c.oe->dataRevision++; c.changed = false; c.changedObject(); - c.oe->sqlChangedCourses = true; + c.oe->sqlCourses.changed = true; synchronized(c); return success; } @@ -1633,7 +1618,7 @@ OpFailStatus MeosSQL::storeRunner(const Row &row, oRunner &r, const wstring &oldBib = r.getBib(); r.sName = fromUTF((string)row["Name"]); - oRunner::getRealName(r.sName, r.tRealName); + r.getRealName(r.sName, r.tRealName); r.setCardNo(row["CardNo"], false, true); r.StartNo = row["StartNo"]; r.tStartTime = r.startTime = row["StartTime"]; @@ -1675,6 +1660,8 @@ OpFailStatus MeosSQL::storeRunner(const Row &row, oRunner &r, } else r.Course=0; + pClass oldClass = r.Class; + if (int(row["Class"])!=0) { r.Class=oe->getClass(int(row["Class"])); @@ -1696,6 +1683,9 @@ OpFailStatus MeosSQL::storeRunner(const Row &row, oRunner &r, } else r.Class=0; + if (oldClass != r.Class) + oe->classIdToRunnerHash.reset(); + if (int(row["Club"])!=0){ r.Club = oe->getClub(int(row["Club"])); @@ -1776,7 +1766,7 @@ OpFailStatus MeosSQL::storeRunner(const Row &row, oRunner &r, // Mark new class as changed r.changedObject(); r.sqlChanged = true; - r.oe->sqlChangedRunners = true; + r.oe->sqlRunners.changed = true; synchronized(r); return success; @@ -1873,7 +1863,7 @@ OpFailStatus MeosSQL::storeTeam(const Row &row, oTeam &t, if (or.sName.empty()) { or.sName = L"@AutoCorrection"; - oRunner::getRealName(or.sName, or.tRealName); + or.getRealName(or.sName, or.tRealName); } if (!or.isRemoved()) { @@ -1894,7 +1884,7 @@ OpFailStatus MeosSQL::storeTeam(const Row &row, oTeam &t, t.Class->markSQLChanged(-1,-1); t.sqlChanged = true; - t.oe->sqlChangedTeams = true; + t.oe->sqlTeams.changed = true; synchronized(t); return success; } @@ -1951,7 +1941,6 @@ bool MeosSQL::Remove(oBase *ob) ResNSel res = updateCounter(oTable.c_str(), ob->Id, &query); ob->Removed = true; ob->changed = false; - ob->reChanged = false; } catch (const mysqlpp::Exception& er){ alert(string(er.what())+" [REMOVE " +oTable +"]"); @@ -2023,11 +2012,13 @@ OpFailStatus MeosSQL::syncRead(bool forceRead, oRunner *r, bool readClassClub, b if (!r || !con.connected()) return opStatusFail; - if (!forceRead && !r->existInDB()) - return syncUpdate(r, true); + if (!forceRead) { + if (!r->existInDB()) + return syncUpdate(r, true); - if (!r->changed && skipSynchronize(*r)) - return opStatusOK; + if (!r->changed && skipSynchronize(*r)) + return opStatusOKSkipped; + } try { Query query = con.query(); @@ -2049,7 +2040,7 @@ OpFailStatus MeosSQL::syncRead(bool forceRead, oRunner *r, bool readClassClub, b r->changed = false; vector mp; - r->evaluateCard(true, mp, 0, false); + r->evaluateCard(true, mp, 0, oBase::ChangeType::Quiet); //Forget evaluated changes. Not our buisness to update. r->changed = false; @@ -2074,9 +2065,8 @@ OpFailStatus MeosSQL::syncRead(bool forceRead, oRunner *r, bool readClassClub, b return syncUpdate(r, false); vector mp; - r->evaluateCard(true, mp, 0, false); + r->evaluateCard(true, mp, 0, oBase::ChangeType::Quiet); r->changed = false; - r->reChanged = false; return opStatusOK; } @@ -2116,11 +2106,13 @@ OpFailStatus MeosSQL::syncRead(bool forceRead, oCard *c) if (!c || !con.connected()) return opStatusFail; - if (!forceRead && !c->existInDB()) - return syncUpdate(c, true); + if (!forceRead) { + if (!c->existInDB()) + return syncUpdate(c, true); - if (!c->changed && skipSynchronize(*c)) - return opStatusOK; + if (!c->changed && skipSynchronize(*c)) + return opStatusOKSkipped; + } try{ Query query = con.query(); @@ -2207,13 +2199,15 @@ OpFailStatus MeosSQL::syncRead(bool forceRead, oTeam *t, bool readRecursive) if (!t || !con.connected()) return opStatusFail; - if (!forceRead && !t->existInDB()) - return syncUpdate(t, true); + if (!forceRead) { + if (!t->existInDB()) + return syncUpdate(t, true); - if (!t->changed && skipSynchronize(*t)) - return opStatusOK; + if (!t->changed && skipSynchronize(*t)) + return opStatusOK; + } - try{ + try { Query query = con.query(); query << "SELECT * FROM oTeam WHERE Id=" << t->Id << andWhereOld(t); Result res = query.store(); @@ -2230,7 +2224,6 @@ OpFailStatus MeosSQL::syncRead(bool forceRead, oTeam *t, bool readRecursive) t->oe->dataRevision++; t->Modified.update(); t->changed = false; - t->reChanged = false; return success; } else { @@ -2252,7 +2245,7 @@ OpFailStatus MeosSQL::syncRead(bool forceRead, oTeam *t, bool readRecursive) return success; } - return opStatusOK; + return opStatusOK; } catch (const mysqlpp::Exception& er){ alert(string(er.what())+" [SYNCREAD oTeam]"); @@ -2319,7 +2312,6 @@ OpFailStatus MeosSQL::syncRead(bool forceRead, oClass *c, bool readCourses) c->oe->dataRevision++; c->Modified.update(); c->changed = false; - c->reChanged = false; return opStatusOK; } else { @@ -2488,17 +2480,21 @@ OpFailStatus MeosSQL::syncRead(bool forceRead, oClub *c) if (!c || !con.connected()) return opStatusFail; - if (!forceRead && !c->existInDB()) - return syncUpdate(c, true); + if (!forceRead) { + if (!c->existInDB()) + return syncUpdate(c, true); - try{ + if (!c->changed && skipSynchronize(*c)) + return opStatusOKSkipped; + } + try { Query query = con.query(); query << "SELECT * FROM oClub WHERE Id=" << c->Id; Result res = query.store(); Row row; - if (!res.empty()){ - row=res.at(0); + if (!res.empty()) { + row = res.at(0); if (!c->changed || isOld(row["Counter"], string(row["Modified"]), c)) { OpFailStatus success = opStatusOK; @@ -2508,21 +2504,21 @@ OpFailStatus MeosSQL::syncRead(bool forceRead, oClub *c) storeClub(row, *c); c->Modified.update(); - c->changed=false; + c->changed = false; return success; } - else if (c->changed){ + else if (c->changed) { return syncUpdate(c, false); } } - else{ + else { //Something is wrong!? Deleted? return syncUpdate(c, true); } return opStatusOK; } - catch (const mysqlpp::Exception& er){ - alert(string(er.what())+" [SYNCREAD oClub]"); + catch (const mysqlpp::Exception& er) { + alert(string(er.what()) + " [SYNCREAD oClub]"); return opStatusFail; } @@ -2557,8 +2553,13 @@ OpFailStatus MeosSQL::syncRead(bool forceRead, oControl *c) if (!c || !con.connected()) return opStatusFail; - if (!forceRead && !c->existInDB()) - return syncUpdate(c, true); + if (!forceRead) { + if (!c->existInDB()) + return syncUpdate(c, true); + + if (!c->changed && skipSynchronize(*c)) + return opStatusOKSkipped; + } try{ Query query = con.query(); @@ -2638,11 +2639,13 @@ OpFailStatus MeosSQL::syncReadCourse(bool forceRead, oCourse *c, set &readC if (isTMP) return opStatusFail; - if (!forceRead && !c->existInDB()) - return syncUpdate(c, true); + if (!forceRead) { + if (!c->existInDB()) + return syncUpdate(c, true); - if (!c->changed && skipSynchronize(*c)) - return opStatusOK; // Skipped readout + if (!c->changed && skipSynchronize(*c)) + return opStatusOKSkipped; // Skipped readout + } try{ Query query = con.query(); @@ -2661,7 +2664,6 @@ OpFailStatus MeosSQL::syncReadCourse(bool forceRead, oCourse *c, set &readC c->oe->dataRevision++; c->Modified.update(); c->changed=false; - c->reChanged=false; return success; } else { @@ -2721,11 +2723,13 @@ OpFailStatus MeosSQL::syncRead(bool forceRead, oFreePunch *c, bool rehash) if (!c || !con.connected()) return opStatusFail; - if (!forceRead && !c->existInDB()) - return syncUpdate(c, true); + if (!forceRead) { + if (!c->existInDB()) + return syncUpdate(c, true); - if (!c->changed && skipSynchronize(*c)) - return opStatusOK; + if (!c->changed && skipSynchronize(*c)) + return opStatusOKSkipped; + } try{ Query query = con.query(); @@ -3079,23 +3083,27 @@ bool MeosSQL::syncListRunner(oEvent *oe) if (!con.connected()) return false; - + int maxCounterRunner = -1; try{ Query query = con.query(); /*query << "SELECT Id, Counter, Modified, Removed FROM oRunner"; query << " WHERE Counter > " << oe->sqlCounterRunners; query << " OR Modified > '" << oe->sqlUpdateRunners << "'";*/ - Result res = query.store(selectUpdated("oRunner", oe->sqlUpdateRunners, oe->sqlCounterRunners)); + Result res = query.store(selectUpdated("oRunner", oe->sqlRunners)); if (res) { - for (int i=0; igetRunner(Id, 0); if (r) { @@ -3108,24 +3116,26 @@ bool MeosSQL::syncListRunner(oEvent *oe) } r->changedObject(); - oe->sqlChangedRunners = true; + oe->sqlRunners.changed = true; } } else{ oRunner *r=oe->getRunner(Id, 0); - if (r){ + if (r) { if (isOld(counter, modified, r)) - syncRead(false, r); + st = syncRead(false, r); + else + st = opStatusOK; } else { oRunner or(oe, Id); - syncRead(true, &or, false, false); + or.setImplicitlyCreated(); + st = syncRead(true, &or, false, false); r = oe->addRunner(or, false); } } - oe->sqlCounterRunners = max(counter, oe->sqlCounterRunners); - oe->sqlUpdateRunners = max(modified, oe->sqlUpdateRunners); + updateCounters(st, counter, modified, oe->sqlRunners, maxCounterRunner); } } } @@ -3133,59 +3143,60 @@ bool MeosSQL::syncListRunner(oEvent *oe) alert(string(er.what())+" [SYNCLIST oRunner]"); return false; } + + if (maxCounterRunner > 0) + oe->sqlRunners.counter = maxCounterRunner - 1; + return true; } -bool MeosSQL::syncListClass(oEvent *oe) -{ +bool MeosSQL::syncListClass(oEvent *oe) { errorMessage.clear(); if (!con.connected()) return false; - try{ + int maxCounter = -1; + try { Query query = con.query(); - /* - query << "SELECT Id, Counter, Modified, Removed FROM oClass"; - query << " WHERE Counter > " << oe->sqlCounterClasses; - query << " OR Modified > '" << oe->sqlUpdateClasses << "'";*/ - - //Result res = query.store(); - Result res = query.store(selectUpdated("oClass", oe->sqlUpdateClasses, oe->sqlCounterClasses)); + Result res = query.store(selectUpdated("oClass", oe->sqlClasses)); if (res) { - - for (int i=0; igetClass(Id); + st = OpFailStatus::opStatusOK; + oClass *c = oe->getClass(Id); if (c) { c->changedObject(); - c->Removed=true; + c->Removed = true; } } else { - oClass *c=oe->getClass(Id); + oClass *c = oe->getClass(Id); if (!c) { oClass oc(oe, Id); - syncRead(true, &oc, false); - c=oe->addClass(oc); - if (c!=0) { + st = syncRead(true, &oc, false); + c = oe->addClass(oc); + if (c != 0) { c->changed = false; - //syncRead(true, c, false); } } else if (isOld(counter, modified, c)) - syncRead(false, c, false); + st = syncRead(false, c, false); + else + st = opStatusOK; } - oe->sqlCounterClasses = max(counter, oe->sqlCounterClasses); - oe->sqlUpdateClasses = max(modified, oe->sqlUpdateClasses); + + updateCounters(st, counter, modified, oe->sqlClasses, maxCounter); } } } @@ -3193,119 +3204,127 @@ bool MeosSQL::syncListClass(oEvent *oe) alert(string(er.what())+" [SYNCLIST oClass]"); return false; } + + if (maxCounter > 0) + oe->sqlClasses.counter = maxCounter - 1; + return true; } - bool MeosSQL::syncListClub(oEvent *oe) { errorMessage.clear(); if (!con.connected()) return false; + int maxCounter = -1; - try{ + try { Query query = con.query(); - /*query << "SELECT Id, Counter, Modified, Removed FROM oClub"; - query << " WHERE Counter > " << oe->sqlCounterClubs; - query << " OR Modified > '" << oe->sqlUpdateClubs << "'";*/ - Result res = query.store(selectUpdated("oClub", oe->sqlUpdateClubs, oe->sqlCounterClubs)); + Result res = query.store(selectUpdated("oClub", oe->sqlClubs)); if (res) { - for(int i=0; igetClub(Id); + if (int(row["Removed"])) { + st = opStatusOK; + oClub *c = oe->getClub(Id); if (c) { - c->Removed=true; + c->Removed = true; c->changedObject(); } } else { - oClub *c=oe->getClub(Id); + oClub *c = oe->getClub(Id); - if (c==0) { + if (c == 0) { oClub oc(oe, Id); - syncRead(true, &oc); + oc.setImplicitlyCreated(); + st = syncRead(true, &oc); oe->addClub(oc); } else if (isOld(counter, modified, c)) - syncRead(false, c); + st = syncRead(false, c); + else + st = opStatusOK; } - oe->sqlCounterClubs = max(counter, oe->sqlCounterClubs); - oe->sqlUpdateClubs = max(modified, oe->sqlUpdateClubs); + updateCounters(st, counter, modified, oe->sqlClubs, maxCounter); } } } - catch (const mysqlpp::Exception& er){ - alert(string(er.what())+" [SYNCLIST oClub]"); + catch (const mysqlpp::Exception& er) { + alert(string(er.what()) + " [SYNCLIST oClub]"); return false; } + if (maxCounter > 0) + oe->sqlClubs.counter = maxCounter - 1; + return true; } - -bool MeosSQL::syncListCourse(oEvent *oe) -{ +bool MeosSQL::syncListCourse(oEvent *oe) { errorMessage.clear(); if (!con.connected()) return false; - - try{ + int maxCounter = -1; + try { Query query = con.query(); - /* - query << "SELECT Id, Counter, Modified, Removed FROM oCourse"; - query << " WHERE Counter > " << oe->sqlCounterCourses; - query << " OR Modified > '" << oe->sqlUpdateCourses << "'"; - */ - Result res = query.store(selectUpdated("oCourse", oe->sqlUpdateCourses, oe->sqlCounterCourses)); - + Result res = query.store(selectUpdated("oCourse", oe->sqlCourses)); if (res) { set tmp; - for(int i=0; igetCourse(Id); + if (int(row["Removed"])) { + st = opStatusOK; + oCourse *c = oe->getCourse(Id); if (c) { - c->Removed=true; + c->Removed = true; c->changedObject(); } } - else{ - oCourse *c=oe->getCourse(Id); + else { + oCourse *c = oe->getCourse(Id); - if (c==0) { + if (c == 0) { oCourse oc(oe, Id); - syncReadCourse(true, &oc, tmp); + oc.setImplicitlyCreated(); + st = syncReadCourse(true, &oc, tmp); oe->addCourse(oc); } else if (isOld(counter, modified, c)) - syncReadCourse(false, c, tmp); + st = syncReadCourse(false, c, tmp); + else + st = opStatusOK; } - oe->sqlCounterCourses = max(counter, oe->sqlCounterCourses); - oe->sqlUpdateCourses = max(modified, oe->sqlUpdateCourses); + updateCounters(st, counter, modified, oe->sqlCourses, maxCounter); } } } - catch (const mysqlpp::Exception& er){ - alert(string(er.what())+" [SYNCLIST oCourse]"); + catch (const mysqlpp::Exception& er) { + alert(string(er.what()) + " [SYNCLIST oCourse]"); return false; } + if (maxCounter > 0) + oe->sqlCourses.counter = maxCounter - 1; return true; } @@ -3315,112 +3334,115 @@ bool MeosSQL::syncListCard(oEvent *oe) if (!con.connected()) return false; + int maxCounter = -1; - try{ + try { Query query = con.query(); - - /*query << "SELECT Id, Counter, Modified, Removed FROM oCard"; - query << " WHERE Counter>0 AND (Counter>" << oe->sqlCounterCards; - query << " OR Modified>'" << oe->sqlUpdateCards << "')";*/ - - Result res = query.store(selectUpdated("oCard", oe->sqlUpdateCards, oe->sqlCounterCards)); + Result res = query.store(selectUpdated("oCard", oe->sqlCards)); if (res) { - for (int i=0; igetCard(Id); + st = opStatusOK; + oCard *c = oe->getCard(Id); if (c) { c->changedObject(); - c->Removed=true; + c->Removed = true; } } else { - oCard *c=oe->getCard(Id); + oCard *c = oe->getCard(Id); if (c) { if (isOld(counter, modified, c)) - syncRead(false, c); + st = syncRead(false, c); + else + st = opStatusOK; } else { oCard oc(oe, Id); + oc.setImplicitlyCreated(); c = oe->addCard(oc); - if (c!=0) - syncRead(true, c); + if (c != 0) + st = syncRead(true, c); } } - oe->sqlCounterCards = max(counter, oe->sqlCounterCards); - oe->sqlUpdateCards = max(modified, oe->sqlUpdateCards); + updateCounters(st, counter, modified, oe->sqlCards, maxCounter); } } } - catch (const mysqlpp::Exception& er){ - alert(string(er.what())+" [SYNCLIST oCard]"); + catch (const mysqlpp::Exception& er) { + alert(string(er.what()) + " [SYNCLIST oCard]"); return false; } + if (maxCounter > 0) + oe->sqlCards.counter = maxCounter - 1; + return true; } -bool MeosSQL::syncListControl(oEvent *oe) -{ +bool MeosSQL::syncListControl(oEvent *oe) { errorMessage.clear(); if (!con.connected()) return false; + int maxCounter = -1; - try{ + try { Query query = con.query(); - - /*query << "SELECT Id, Counter, Modified, Removed FROM oControl"; - query << " WHERE Counter > " << oe->sqlCounterControls; - query << " OR Modified > '" << oe->sqlUpdateControls << "'";*/ - - Result res = query.store(selectUpdated("oControl", oe->sqlUpdateControls, oe->sqlCounterControls)); - - //Result res = query.store(); + Result res = query.store(selectUpdated("oControl", oe->sqlControls)); if (res) { - for(int i=0; igetControl(Id, false); + if (int(row["Removed"])) { + st = opStatusOK; + oControl *c = oe->getControl(Id, false); if (c) { - c->Removed=true; + c->Removed = true; c->changedObject(); } } else { - oControl *c=oe->getControl(Id, false); + oControl *c = oe->getControl(Id, false); if (c) { if (isOld(counter, modified, c)) - syncRead(false, c); + st = syncRead(false, c); + else + st = opStatusOK; } else { oControl oc(oe, Id); - syncRead(true, &oc); + oc.setImplicitlyCreated(); + st = syncRead(true, &oc); c = oe->addControl(oc); -// if (c!=0) -// syncRead(true, c); } } - oe->sqlCounterControls = max(counter, oe->sqlCounterControls); - oe->sqlUpdateControls = max(modified, oe->sqlUpdateControls); + updateCounters(st, counter, modified, oe->sqlControls, maxCounter); } } } - catch (const mysqlpp::Exception& er){ - alert(string(er.what())+" [SYNCLIST oControl]"); + catch (const mysqlpp::Exception& er) { + alert(string(er.what()) + " [SYNCLIST oControl]"); return false; } + if (maxCounter > 0) + oe->sqlControls.counter = maxCounter - 1; + return true; } @@ -3430,24 +3452,24 @@ bool MeosSQL::syncListPunch(oEvent *oe) if (!con.connected()) return false; - + int maxCounter = -1; try{ Query query = con.query(); - /*query << "SELECT Id, Counter, Modified, Removed FROM oPunch"; - query << " WHERE Counter > " << oe->sqlCounterPunches; - query << " OR Modified > '" << oe->sqlUpdatePunches << "' ORDER BY Id";*/ - Result res = query.store(selectUpdated("oPunch", oe->sqlUpdatePunches, oe->sqlCounterPunches) + " ORDER BY Id"); - //Result res = query.store(); + Result res = query.store(selectUpdated("oPunch", oe->sqlPunches) + " ORDER BY Id"); if (res) { - for(int i=0; igetPunch(Id); if (c) { c->Removed=true; @@ -3463,16 +3485,19 @@ bool MeosSQL::syncListPunch(oEvent *oe) if (c) { if (isOld(counter, modified, c)) - syncRead(false, c, true); + st = syncRead(false, c, true); + else + st = opStatusOK; } else { oFreePunch p(oe, Id); - syncRead(true, &p, false); + p.setImplicitlyCreated(); + st = syncRead(true, &p, false); oe->addFreePunch(p); } } - oe->sqlCounterPunches = max(counter, oe->sqlCounterPunches); - oe->sqlUpdatePunches = max(modified, oe->sqlUpdatePunches); + + updateCounters(st, counter, modified, oe->sqlPunches, maxCounter); } } } @@ -3480,60 +3505,65 @@ bool MeosSQL::syncListPunch(oEvent *oe) alert(string(er.what())+" [SYNCLIST oPunch]"); return false; } + + if (maxCounter > 0) { + oe->sqlPunches.counter = maxCounter - 1; + } return true; } -bool MeosSQL::syncListTeam(oEvent *oe) -{ +bool MeosSQL::syncListTeam(oEvent *oe) { errorMessage.clear(); if (!con.connected()) return false; + int maxCounterTeam = -1; - try{ + try { Query query = con.query(); - /* - query << "SELECT Id, Counter, Modified, Removed FROM oTeam"; - query << " WHERE Counter > " << oe->sqlCounterTeams; - query << " OR Modified > '" << oe->sqlUpdateTeams << "'";*/ - - Result res = query.store(selectUpdated("oTeam", oe->sqlUpdateTeams, oe->sqlCounterTeams)); - + Result res = query.store(selectUpdated("oTeam", oe->sqlTeams)); + if (res) { - for (int i=0; igetTeam(Id); + if (int(row["Removed"])) { + st = OpFailStatus::opStatusOK; + oTeam *t = oe->getTeam(Id); if (t) { t->changedObject(); t->prepareRemove(); - t->Removed=true; - oe->sqlChangedTeams = true; + t->Removed = true; + oe->sqlTeams.changed = true; } } else { - oTeam *t=oe->getTeam(Id); + oTeam *t = oe->getTeam(Id); if (t) { if (isOld(counter, modified, t)) - syncRead(false, t, false); + st = syncRead(false, t, false); + else + st = opStatusOK; } - else{ + else { oTeam ot(oe, Id); + ot.setImplicitlyCreated(); t = oe->addTeam(ot, false); if (t) { - syncRead(true, t, false); - t->apply(false, 0, false); + st = syncRead(true, t, false); + t->apply(oBase::ChangeType::Quiet, nullptr); t->changed = false; } } } - oe->sqlCounterTeams = max(counter, oe->sqlCounterTeams); - oe->sqlUpdateTeams = max(modified, oe->sqlUpdateTeams); + updateCounters(st, counter, modified, oe->sqlTeams, maxCounterTeam); } } } @@ -3541,14 +3571,18 @@ bool MeosSQL::syncListTeam(oEvent *oe) alert(string(er.what())+" [SYNCLIST oTeam]"); return false; } + + if (maxCounterTeam > 0) { + oe->sqlTeams.counter = maxCounterTeam - 1; + } return true; } -string MeosSQL::selectUpdated(const char *oTable, const string &updated, int counter) { +string MeosSQL::selectUpdated(const char *oTable, const SqlUpdated &updated) { string p1 = string("SELECT Id, Counter, Modified, Removed FROM ") + oTable; - string q = "(" + p1 + " WHERE Counter>" + itos(counter) + ") UNION ALL ("+ - p1 + " WHERE Modified>'" + updated + "' AND Counter<" + itos(counter) + " AND Counter>0)"; + string q = "(" + p1 + " WHERE Counter>" + itos(updated.counter) + ") UNION ALL ("+ + p1 + " WHERE Modified>'" + updated.updated + "' AND Counter<=" + itos(updated.counter) + ")"; return q; } @@ -3790,21 +3824,21 @@ int MeosSQL::getModifiedMask(oEvent &oe) { int t = r["oTeam"]; int e = r["oEvent"]; - if (ctrl > oe.sqlCounterControls) + if (ctrl > oe.sqlControls.counter) res |= encode(oListId::oLControlId); - if (crs > oe.sqlCounterCourses) + if (crs > oe.sqlCourses.counter) res |= encode(oListId::oLCourseId); - if (cls > oe.sqlCounterClasses) + if (cls > oe.sqlClasses.counter) res |= encode(oListId::oLClassId); - if (card > oe.sqlCounterCards) + if (card > oe.sqlCards.counter) res |= encode(oListId::oLCardId); - if (club > oe.sqlCounterClubs) + if (club > oe.sqlClubs.counter) res |= encode(oListId::oLClubId); - if (punch > oe.sqlCounterPunches) + if (punch > oe.sqlPunches.counter) res |= encode(oListId::oLPunchId); - if (runner > oe.sqlCounterRunners) + if (runner > oe.sqlRunners.counter) res |= encode(oListId::oLRunnerId); - if (t > oe.sqlCounterTeams) + if (t > oe.sqlTeams.counter) res |= encode(oListId::oLTeamId); if (e > oe.counter) res |= encode(oListId::oLEventId); @@ -3850,7 +3884,7 @@ void MeosSQL::processMissingObjects() { { oClass *cls = dynamic_cast(obj); if (cls && cls->getName().empty()) { - cls->setName(L"@AutoCorrection"); + cls->setName(L"@AutoCorrection", false); syncUpdate(cls, false); } } @@ -3910,3 +3944,20 @@ OpFailStatus MeosSQL::syncRead(bool forceRead, oBase *obj) { return ret; } + +void MeosSQL::updateCounters(OpFailStatus st, + int counter, + const string &modified, + SqlUpdated &update, + int &maxCounter) { + if (st == OpFailStatus::opStatusOK || st == OpFailStatus::opStatusWarning) { + update.counter = max(counter, update.counter); + update.updated = max(modified, update.updated); + } + else if (st == opStatusOKSkipped) { + if (maxCounter < 0) + maxCounter = counter; + else + maxCounter = min(counter, maxCounter); + } +} diff --git a/code/meosdb/MeosSQL.h b/code/meosdb/MeosSQL.h index 98ec413..f528fc1 100644 --- a/code/meosdb/MeosSQL.h +++ b/code/meosdb/MeosSQL.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -40,6 +40,7 @@ class oFreePunch; class oDataInterface; class oTeam; class oDataContainer; +struct SqlUpdated; namespace mysqlpp { class Query; @@ -118,17 +119,22 @@ protected: bool checkOldVersion(oEvent *oe, mysqlpp::Row &row); map, DWORD> readTimes; - void clearReadTimes(); void synchronized(const oBase &entity); bool skipSynchronize(const oBase &entity) const; mysqlpp::ResNSel updateCounter(const char *oTable, int id, mysqlpp::Query *updateqry); - string selectUpdated(const char *oTable, const string &updated, int counter); + string selectUpdated(const char *oTable, const SqlUpdated &updated); void addedFromDatabase(oBase *object); - + void updateCounters(OpFailStatus st, + int counter, + const string &modified, + SqlUpdated &update, int &maxCounter); + public: + void clearReadTimes(); + bool dropDatabase(oEvent *oe); bool checkConnection(oEvent *oe); void processMissingObjects(); diff --git a/code/meosdb/dllmain.cpp b/code/meosdb/dllmain.cpp deleted file mode 100644 index 5832cba..0000000 --- a/code/meosdb/dllmain.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// dllmain.cpp : Defines the entry point for the DLL application. -#include "stdafx.h" - -BOOL APIENTRY DllMain( HMODULE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved - ) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} - diff --git a/code/meosdb/meosdb.cpp b/code/meosdb/meosdb.cpp index d5d83b2..c820922 100644 --- a/code/meosdb/meosdb.cpp +++ b/code/meosdb/meosdb.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -69,6 +69,10 @@ int getListMask(oEvent &oe) { return msql.getModifiedMask(oe); } +void resetSynchTimes() { + msql.clearReadTimes(); +} + bool MEOSDB_API msSynchronizeList(oEvent *oe, oListId lid) { nSynchList++; diff --git a/code/meosdb/meosdb.h b/code/meosdb/meosdb.h deleted file mode 100644 index 9d67b9a..0000000 --- a/code/meosdb/meosdb.h +++ /dev/null @@ -1,29 +0,0 @@ -// The following ifdef block is the standard way of creating macros which make exporting -// from a DLL simpler. All files within this DLL are compiled with the MEOSDB_EXPORTS -// symbol defined on the command line. this symbol should not be defined on any project -// that uses this DLL. This way any other project whose source files include this file see -// MEOSDB_API functions as being imported from a DLL, whereas this DLL sees symbols -// defined with this macro as being exported. -#ifdef MEOSDB_EXPORTS -#define MEOSDB_API __declspec(dllexport) __cdecl -#else -#define MEOSDB_API __declspec(dllimport) __cdecl -#endif - -#include -#include - -/* -extern "C"{ -// This class is exported from the meosdb.dll -class Cmeosdb { -public: - Cmeosdb(void); - // TODO: add your methods here. -}; - -//extern MEOSDB_API int nmeosdb; - - -MEOSDB_API int fnmeosdb(void); -}*/ diff --git a/code/meosdb/sqltypes.h b/code/meosdb/sqltypes.h index 3188a0c..b8331a9 100644 --- a/code/meosdb/sqltypes.h +++ b/code/meosdb/sqltypes.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -22,6 +22,7 @@ ************************************************************************/ enum OpFailStatus { + opStatusOKSkipped = 3, opStatusOK = 2, opStatusFail = 0, opStatusWarning = 1, @@ -33,10 +34,8 @@ enum OpFailStatus { class oEvent; class oBase; - #define MEOSDB_API int MEOSDB_API getMeosVersion(); -// bool MEOSDB_API msSynchronizeList(oEvent *, oListId lid); int MEOSDB_API msSynchronizeUpdate(oBase *); int MEOSDB_API msSynchronizeRead(oBase *obj); int MEOSDB_API msRemove(oBase *obj); @@ -50,6 +49,7 @@ class oBase; bool MEOSDB_API msReConnect(); int MEOSDB_API msListCompetitions(oEvent *oe); + void resetSynchTimes(); int getListMask(oEvent &oe); diff --git a/code/meosdb/targetver.h b/code/meosdb/targetver.h index 55afdfb..329ca65 100644 --- a/code/meosdb/targetver.h +++ b/code/meosdb/targetver.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/meosexception.h b/code/meosexception.h index b6cddcd..228f93e 100644 --- a/code/meosexception.h +++ b/code/meosexception.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/meosvc15.vcxproj b/code/meosvc15.vcxproj index b9770ba..53575e1 100644 --- a/code/meosvc15.vcxproj +++ b/code/meosvc15.vcxproj @@ -428,6 +428,7 @@ + @@ -459,7 +460,6 @@ - diff --git a/code/meosversion.cpp b/code/meosversion.cpp index d5cbd59..d77aebd 100644 --- a/code/meosversion.cpp +++ b/code/meosversion.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -30,7 +30,7 @@ //V35: abcdef //V36: abcdef int getMeosBuild() { - string revision("$Rev: 935 $"); + string revision("$Rev: 972 $"); return 174 + atoi(revision.substr(5, string::npos).c_str()); } @@ -42,16 +42,16 @@ int getMeosBuild() { //V33: abcdefghij //V34: abcdfge wstring getMeosDate() { - wstring date(L"$Date: 2019-10-05 21:53:12 +0200 (lö, 05 okt 2019) $"); + wstring date(L"$Date: 2020-01-18 15:14:04 +0100 (lö, 18 jan 2020) $"); return date.substr(7,10); } wstring getBuildType() { - return L"Update 3"; // No parantheses (...) + return L"Beta 1"; // No parantheses (...) } wstring getMajorVersion() { - return L"3.6"; + return L"3.7"; } wstring getMeosFullVersion() { @@ -103,7 +103,6 @@ void getSupporters(vector &supp, vector &developSupp) supp.emplace_back(L"Hans Carlstedt, Sävedalens AIK"); supp.emplace_back(L"IFK Mora OK"); supp.emplace_back(L"Attunda OK"); - supp.emplace_back(L"OK Tyr, Karlstad"); supp.emplace_back(L"Siguldas Takas, Latvia"); supp.emplace_back(L"Eric Teutsch, Ottawa Orienteering Club, Canada"); supp.emplace_back(L"Silkeborg OK, Denmark"); @@ -142,6 +141,13 @@ void getSupporters(vector &supp, vector &developSupp) supp.emplace_back(L"TJ Slávia Farmaceut Bratislava"); supp.emplace_back(L"OK Tyr, Karlstad"); supp.emplace_back(L"Magnus Thornell, Surahammars SOK"); - + supp.emplace_back(L"Mariager Fjord OK"); + supp.emplace_back(L"Nässjö OK"); + supp.emplace_back(L"Ringsjö OK"); + supp.emplace_back(L"Big Foot Orienteers"); + supp.emplace_back(L"Bay Area Orienteering Club"); + supp.emplace_back(L"FinspÃ¥ngs SOK"); + supp.emplace_back(L"OK Gorm, Denmark"); + supp.emplace_back(L"Nyköpings OK"); reverse(supp.begin(), supp.end()); } diff --git a/code/metalist.cpp b/code/metalist.cpp index e68efd2..0dd2111 100644 --- a/code/metalist.cpp +++ b/code/metalist.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -95,7 +95,7 @@ void oListParam::serialize(xmlparser &xml, xml.writeBool("Large", useLargeSize); xml.writeBool("PageBreak", pageBreak); xml.writeBool("HideHeader", !showHeader); - + xml.write("AgeFilter", int(ageFilter)); xml.writeBool("ShowNamedSplits", showInterTimes); xml.writeBool("ShowInterTitle", showInterTitle); xml.writeBool("ShowSplits", showSplitTimes); @@ -162,7 +162,7 @@ void oListParam::deserialize(const xmlobject &xml, const MetaListContainer &cont showInterTitle = xml.getObjectBool("ShowInterTitle"); inputNumber = xml.getObjectInt("InputNumber"); nextList = xml.getObjectInt("NextList"); - + ageFilter = AgeFilter(xml.getObjectInt("AgeFilter")); xmlobject bg = xml.getObject("BGColor"); if (bg) bgColor = bg.getInt(); @@ -366,7 +366,7 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par li.lp = par; gdiFonts normal, header, small, italic; double s_factor; - + oe->calculateResults({}, oEvent::ResultType::ClassResult, false); map, int> fontHeight; for (size_t k = 0; k < fontFaces.size(); k++) { @@ -405,6 +405,8 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par li.rogainingResults = false; li.calculateLiveResults = false; li.calcCourseClassResults = false; + li.calcCourseResults = false; + if (par.useControlIdResultFrom > 0 || par.useControlIdResultTo > 0) li.needPunches = oListInfo::PunchMode::SpecificPunch; const bool isPunchList = mList.listSubType == oListInfo::EBaseTypeCoursePunches || @@ -449,7 +451,7 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par if (mp.type == lTeamPlace || mp.type == lRunnerPlace || mp.type == lRunnerGeneralPlace) { if (!li.calcResults) { oe->calculateResults(set(), oEvent::ResultType::ClassResult); - oe->calculateTeamResults(false); + oe->calculateTeamResults(set(), oEvent::ResultType::ClassResult); } li.calcResults = true; } @@ -458,7 +460,7 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par || mp.type == lRunnerGeneralPlace) { if (!li.calcTotalResults) { oe->calculateResults(set(), oEvent::ResultType::TotalResult); - oe->calculateTeamResults(true); + oe->calculateTeamResults(set(), oEvent::ResultType::TotalResult); } li.calcTotalResults = true; } @@ -472,6 +474,12 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par li.calcCourseClassResults = true; } + else if (mp.type == lRunnerCoursePlace) { + if (!li.calcCourseResults) + oe->calculateResults(set(), oEvent::ResultType::ClassCourseResult); + + li.calcCourseResults = true; + } else if (mp.type == lRunnerTempTimeAfter || mp.type == lRunnerTempTimeStatus) { if (li.needPunches == oListInfo::PunchMode::NoPunch) li.needPunches = oListInfo::PunchMode::SpecificPunch; @@ -791,8 +799,18 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par li.supportFrom = supportFromControl; li.supportTo = supportToControl; li.resType = getResultType(); - if (!resultModule.empty() || li.calcResults || li.calcCourseClassResults || li.calcTotalResults) + if (!resultModule.empty() || li.calcResults || li.calcCourseClassResults + || li.calcTotalResults || li.calcCourseResults) hasResults_ = true; + + if (li.sortOrder == SortOrder::CourseResult) { + li.replaceType(EPostType::lRunnerPlace, EPostType::lRunnerCoursePlace, true); + } + + if (li.sortOrder == SortOrder::ClassCourseResult) { + li.replaceType(EPostType::lRunnerPlace, EPostType::lRunnerClassCoursePlace, true); + } + } void Position::indent(int ind) { @@ -1080,7 +1098,7 @@ void MetaList::save(xmlparser &xml, const oEvent *oe) const { xml.write("ResultModule", ttw); try { wstring srcFile; - GeneralResult &gr = oe->getGeneralResult(resultModule, srcFile); + GeneralResult &gr = *oe->getGeneralResult(resultModule, srcFile); DynamicResult &dr = dynamic_cast(gr); if (!dr.isBuiltIn()) { string ot = dr.getTag(); @@ -1158,7 +1176,7 @@ void MetaList::load(const xmlobject &xDef) { // string db = "Loaded res mod: " + dr.getTag() + ", h=" + itos(dr.getHashCode())+ "\n"; // OutputDebugString(db.c_str()); wstring file = L"*"; - dynamicResults[k] = GeneralResultCtr(file, new DynamicResult(dr)); + dynamicResults[k] = GeneralResultCtr(file, make_shared(dr)); } supportFromControl = xDef.getObjectBool("SupportFrom"); supportToControl = xDef.getObjectBool("SupportTo"); @@ -1302,7 +1320,7 @@ void MetaList::load(const xmlobject &xDef) { void MetaList::getDynamicResults(vector &resultModules) const { resultModules.resize(dynamicResults.size()); for (size_t k = 0; k < dynamicResults.size(); k++) { - resultModules[k].res = dynamic_cast(dynamicResults[k].ptr); + resultModules[k].res = dynamic_pointer_cast(dynamicResults[k].ptr); resultModules[k].ctr = const_cast(this); } } @@ -1320,7 +1338,7 @@ const wstring &MetaList::getListInfo(const oEvent &oe) const { if (!resultModule.empty()) { wstring f; try { - GeneralResult &res = oe.getGeneralResult(resultModule, f); + GeneralResult &res = *oe.getGeneralResult(resultModule, f); DynamicResult &dres = dynamic_cast(res); return dres.getDescription(); } @@ -1349,7 +1367,7 @@ void MetaList::retagResultModule(const string &newTag, bool retagStoredModule) { if (retagStoredModule) { for (size_t k = 0; k < dynamicResults.size(); k++) { - DynamicResult *res = dynamic_cast(dynamicResults[k].ptr); + DynamicResult *res = dynamic_cast(dynamicResults[k].ptr.get()); if (res && res->getTag() == oldTag) { res->setTag(newTag); } @@ -1359,7 +1377,7 @@ void MetaList::retagResultModule(const string &newTag, bool retagStoredModule) { bool MetaList::updateResultModule(const DynamicResult &dr, bool updateSimilar) { for (size_t k = 0; k < dynamicResults.size(); k++) { - DynamicResult *res = dynamic_cast(dynamicResults[k].ptr); + DynamicResult *res = dynamic_cast(dynamicResults[k].ptr.get()); if (res) { const string &tag1 = res->getTag(); const string &tag2 = dr.getTag(); @@ -1387,6 +1405,12 @@ bool MetaListContainer::updateResultModule(const DynamicResult &dr, bool updateS } } + auto f = freeResultModules.find(dr.getTag()); + if (f != freeResultModules.end()) { + f->second.ptr = make_shared(dr); + changed = true; + } + if (changed && owner) owner->updateChanged(); @@ -1685,6 +1709,7 @@ void MetaList::initSymbols() { typeToSymbol[lRunnerLostTime] = L"RunnerTimeLost"; typeToSymbol[lRunnerPlace] = L"RunnerPlace"; typeToSymbol[lRunnerClassCoursePlace] = L"RunnerClassCoursePlace"; + typeToSymbol[lRunnerCoursePlace] = L"RunnerCoursePlace"; typeToSymbol[lRunnerStart] = L"RunnerStart"; typeToSymbol[lRunnerCheck] = L"RunnerCheck"; typeToSymbol[lRunnerStartCond] = L"RunnerStartCond"; @@ -1848,6 +1873,7 @@ void MetaList::initSymbols() { orderToSymbol[ClassStartTime] = "ClassStartTime"; orderToSymbol[ClassStartTimeClub] = "ClassStartTimeClub"; orderToSymbol[ClassResult] = "ClassResult"; + orderToSymbol[ClassDefaultResult] = "ClassDefaultResult"; orderToSymbol[ClassCourseResult] = "ClassCourseResult"; orderToSymbol[SortByName] = "SortNameOnly"; orderToSymbol[SortByLastName] = "SortLastNameOnly"; @@ -2005,6 +2031,13 @@ void MetaListContainer::save(MetaListType type, xmlparser &xml, const oEvent *oe it->second.serialize(xml, *this, id2Ix); } } + + for (auto res : freeResultModules) { + DynamicResult *dr = dynamic_cast(res.second.ptr.get()); + if (dr) { + dr->save(xml); + } + } } bool MetaListContainer::load(MetaListType type, const xmlobject &xDef, bool ignoreOld) { @@ -2014,9 +2047,11 @@ bool MetaListContainer::load(MetaListType type, const xmlobject &xDef, bool igno xDef.getObjects("MeOSListDefinition", xList); wstring majVer = getMajorVersion(); bool hasSkipped = false; - - if (xList.empty() && strcmp(xDef.getName(), "MeOSListDefinition") == 0) + bool pushLevel = false; + if (xList.empty() && strcmp(xDef.getName(), "MeOSListDefinition") == 0) { xList.push_back(xDef); + pushLevel = true; + } wstring err; for (size_t k = 0; ksecond.nextList = 0; // Clear relation } } + if (!pushLevel) { + xDef.getObjects("MeOSResultCalculationSet", xList); + decltype(freeResultModules) copy; + freeResultModules.swap(copy); + for (auto res : xList) { + GeneralResultCtr ctr; + auto dr = make_shared(); + dr->load(res); + if (copy.count(dr->getTag())) { + DynamicResult &oldResult = dynamic_cast(*copy[dr->getTag()].ptr); + oldResult = *dr; + dr = dynamic_pointer_cast(copy[dr->getTag()].ptr); + } - if (owner) - owner->updateChanged(); + freeResultModules.emplace(dr->getTag(), GeneralResultCtr(dr->getTag().c_str(), dr->getName(false), dr)); + } + + if (owner) + owner->updateChanged(); + } if (!err.empty()) throw meosException(err); @@ -2769,3 +2821,53 @@ void MetaListContainer::synchronizeTo(MetaListContainer &dst) const { } } } + +void MetaListContainer::getGeneralResults(vector &rmAll) { + for (int k = 0; k < getNumLists(); k++) { + vector rm; + getList(k).getDynamicResults(rm); + + if (!isExternal(k)) { + for (size_t j = 0; j < rm.size(); j++) { + if (rm[j].res) + rm[j].res->setReadOnly(); + } + } + rmAll.insert(rmAll.end(), rm.begin(), rm.end()); + } + + for (auto &res : freeResultModules) { + auto dynRes = dynamic_pointer_cast(res.second.ptr); + dynRes->retaggable(false); + if (dynRes) { + rmAll.emplace_back(dynRes, nullptr); + } + } +} + +wstring DynamicResultRef::getAnnotation() const { + if (ctr) + return ctr->getListName(); + else + return L""; +} + +void MetaListContainer::updateGeneralResult(string tag, const shared_ptr &res) { + bool changed = false; + if (!res) { + changed = freeResultModules.count(tag) > 0; + freeResultModules.erase(tag); + } + else { + auto rm = freeResultModules.find(tag); + if (rm == freeResultModules.end()) + changed = true; + else + changed = rm->second.ptr.get() != res.get(); + assert(tag == res->getTag()); + freeResultModules.emplace(tag, GeneralResultCtr(tag.c_str(), res->getName(false), res)); + } + + if (changed && owner) + owner->updateChanged(); +} diff --git a/code/metalist.h b/code/metalist.h index c4bc0ca..8450d09 100644 --- a/code/metalist.h +++ b/code/metalist.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -143,10 +143,12 @@ public: }; struct DynamicResultRef { - DynamicResultRef(const DynamicResult *resIn, MetaList *ctrIn) : res(resIn), ctr(ctrIn) {} - DynamicResultRef() : res(0), ctr(0) {} - const DynamicResult *res; + DynamicResultRef(const shared_ptr &resIn, MetaList *ctrIn) : res(resIn), ctr(ctrIn) {} + DynamicResultRef() : ctr(0) {} + shared_ptr res; MetaList *ctr; + + wstring getAnnotation() const; }; class MetaList { @@ -277,8 +279,6 @@ public: return *this; } - void getExistingTypes(vector< pair > &types) const; - const wstring &getListName() const {return listName;} oListInfo::EBaseType getListType() const; @@ -353,8 +353,10 @@ private: mutable map globalIndex; mutable map tagIndex; mutable map uniqueIndex; - map listParam; + + map freeResultModules; + oEvent *owner; public: @@ -399,6 +401,9 @@ public: bool isInternal(int index) const {return data[index].first == InternalList;} bool isExternal(int index) const {return data[index].first == ExternalList;} + void updateGeneralResult(string tag, const shared_ptr &res); + void getGeneralResults(vector &resMod); + void save(MetaListType type, xmlparser &xml, const oEvent *oe) const; /** Returns true if all lists where loaded, false if some list was in a unnsupported version and ignoreOld was set. Throws if some list was incorrect. */ diff --git a/code/methodeditor.cpp b/code/methodeditor.cpp index 144ca4c..1b86202 100644 --- a/code/methodeditor.cpp +++ b/code/methodeditor.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -66,13 +66,23 @@ int methodCB(gdioutput *gdi, int type, void *data) { throw meosException("Unexpected error"); } +void MethodEditor::show(TabBase *dst, gdioutput &gdi) { + gdi.clearPage(false); + origin = dst; + show(gdi); +} + void MethodEditor::show(gdioutput &gdi) { - oe->loadGeneralResults(true); + oe->loadGeneralResults(true, true); gdi.setRestorePoint("BeginMethodEdit"); gdi.pushX(); gdi.setCX(gdi.getCX() + gdi.scaleLength(6)); + + int bx = gdi.getCX(); + int by = gdi.getCY(); + if (currentResult) gdi.addString("", boldLarge, makeDash(L"Result Module - X#") + currentResult->getName(true)); else @@ -100,10 +110,23 @@ void MethodEditor::show(gdioutput &gdi) { #ifdef _DEBUG gdi.addButton("WriteDoc", "#WriteDoc", methodCB); #endif - gdi.popX(); gdi.dropLine(2); gdi.fillDown(); - + + int dx = gdi.getCX(); + int dy = gdi.getCY(); + + RECT rc; + int off = gdi.scaleLength(6); + rc.left = bx - 2 * off; + rc.right = dx + 2 * off; + + rc.top = by - off; + rc.bottom = dy + off; + + gdi.addRectangle(rc, colorWindowBar); + gdi.popX(); + if (currentResult) { gdi.dropLine(0.5); if (currentResult->getTag().empty()) @@ -205,6 +228,11 @@ string MethodEditor::uniqueTag(const string &tag) const { return tag + un + itos(iter); } +void makeScore(wstring &str, const pair &score) { + int64_t v = int64_t(score.first) * (1LL << 32) + int64_t(score.second); + str = itow(v); +} + int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { if (type == GUI_BUTTON) { ButtonInfo bi = dynamic_cast(data); @@ -271,7 +299,7 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { wstring path = fileNameSource.empty() ? getInternalPath(currentResult->getTag()) : fileNameSource; currentResult->save(path); fileNameSource = path; - oe->loadGeneralResults(true); + oe->loadGeneralResults(true, true); makeDirty(gdi, ClearDirty); currentResult->save(fileName); @@ -308,7 +336,7 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { oe->synchronize(false); oe->getListContainer().updateResultModule(*currentResult, doUpdate); - oe->loadGeneralResults(true); + oe->loadGeneralResults(true, true); oe->synchronize(true); @@ -324,7 +352,7 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { wstring rm = path + L".removed"; DeleteFile(rm.c_str()); _wrename(path.c_str(), rm.c_str()); - oe->loadGeneralResults(true); + oe->loadGeneralResults(true, true); makeDirty(gdi, ClearDirty); setCurrentResult(0, L""); gdi.clearPage(false); @@ -373,6 +401,19 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { //oe->getListContainer().getLists(lists); vector< pair > > tagNameList; oe->getGeneralResults(true, tagNameList, true); + map tag2ClassUsage; + vector cls; + oe->getClasses(cls, false); + for (pClass c : cls) { + string rt = c->getResultModuleTag(); + if (!rt.empty()) { + wstring &n = tag2ClassUsage[rt]; + if (n.empty()) + n = L" | " + c->getName(); + else + n += L", \x2026"; + } + } for (size_t k = 0; k < tagNameList.size(); k++) { string tag = tagNameList[k].second.first; string utag = DynamicResult::undecorateTag(tag); @@ -383,6 +424,8 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { if (listIx.size() > 0) { n += L" *"; } + else if (tag2ClassUsage.count(tag)) + n += tag2ClassUsage[tag]; lists.push_back(make_pair(n, tagNameList[k].first)); } @@ -400,7 +443,7 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { if (!lists.empty()) { wstring srcFile; - bool ro = dynamic_cast(oe->getGeneralResult(tagNameList.front().second.first, srcFile)).isReadOnly(); + bool ro = dynamic_cast(*oe->getGeneralResult(tagNameList.front().second.first, srcFile)).isReadOnly(); gdi.setInputStatus("DoOpen", !ro); } else { @@ -427,8 +470,9 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { } if (ix < tagNameList.size()) { - wstring srcFile; - DynamicResult &drIn = dynamic_cast(oe->getGeneralResult(tagNameList[ix].second.first, srcFile)); + dr = load(gdi, tagNameList[ix].second.first, bi.id == "DoOpenCopy"); + /*wstring srcFile; + DynamicResult &drIn = dynamic_cast(*oe->getGeneralResult(tagNameList[ix].second.first, srcFile)); wasLoadedBuiltIn = drIn.isReadOnly(); dr = new DynamicResult(drIn); if (bi.id == "DoOpenCopy") { @@ -437,17 +481,8 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { setCurrentResult(dr, L""); } else - setCurrentResult(dr, srcFile); + setCurrentResult(dr, srcFile);*/ } - - if (bi.id == "DoOpen") - makeDirty(gdi, ClearDirty); - else - makeDirty(gdi, MakeDirty); - - gdi.clearPage(false); - show(gdi); - if (dr) dr->compile(true); } } else if (bi.id == "CancelReload") { @@ -461,7 +496,11 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { setCurrentResult(0, L""); makeDirty(gdi, ClearDirty); currentIndex = -1; - gdi.getTabs().get(TListTab)->loadPage(gdi); + if (origin) { + auto oc = origin; + origin = nullptr; + oc->loadPage(gdi); + } return 0; } else if (bi.id == "SaveSource") { @@ -519,16 +558,19 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { int xp = gdi.getCX(); int yp = gdi.getCY(); int diff = gdi.scaleLength(3); - const int w[5] = {200, 70, 70, 70, 85}; + int w[5] = {200, 70, 70, 70, 135}; + for (int i = 0; i < 5; i++) + w[i] = gdi.scaleLength(w[i]); + set errors; - currentResult->prepareCalculations(*oe, tr.size()>0, inputNumber); + currentResult->prepareCalculations(*oe, false, {}, rr, tr, inputNumber); for (size_t k = 0; k < rr.size(); k++) { int txp = xp; int wi = 0; gdi.addStringUT(yp, txp, 0, rr[k]->getCompleteIdentification(), w[wi]-diff); txp += w[wi++]; - currentResult->prepareCalculations(*rr[k]); + currentResult->prepareCalculations(*rr[k], false); int rt = 0, pt = 0; RunnerStatus st = StatusUnknown; { @@ -595,8 +637,8 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { wstring err; wstring str; try { - int score = currentResult->score(*rr[k], st, rt, pt, false); - str = itow(score); + pair score = currentResult->score(*rr[k], st, rt, pt, false); + makeScore(str, score); } catch (meosException &ex) { err = ex.wwhat(); @@ -630,7 +672,7 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { int wi = 0; gdi.addStringUT(yp, txp, 0, tr[k]->getName(), w[wi]-diff); txp += w[wi++]; - currentResult->prepareCalculations(*tr[k]); + currentResult->prepareCalculations(*tr[k], false); int rt = 0, pt = 0; RunnerStatus st = StatusUnknown; { @@ -696,8 +738,8 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { wstring err; wstring str; try { - int score = currentResult->score(*tr[k], st, rt, pt); - str = itow(score); + auto score = currentResult->score(*tr[k], st, rt, pt); + makeScore(str, score); } catch (meosException &ex) { err = ex.wwhat(); @@ -732,7 +774,7 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { gdi.enableInput("CancelSource"); gdi.getBaseInfo("SaveSource").setExtra(lbi.data); - if (!gdi.hasField("Source")) { + if (!gdi.hasWidget("Source")) { gdi.fillRight(); gdi.restore("TestRule", false); gdi.pushX(); @@ -742,8 +784,8 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { methodCB, L"Source code:").setFont(gdi, monoText); gdi.fillDown(); gdi.setCX(gdi.getCX() + gdi.getLineHeight()); - gdi.addListBox("Symbols", 450, 300-20, methodCB, L"Available symbols:"); - gdi.setTabStops("Symbols", 180); + gdi.addListBox("Symbols", 600, 300-20, methodCB, L"Available symbols:"); + gdi.setTabStops("Symbols", 250); gdi.addString("SymbInfo", gdi.getCY(), gdi.getCX(), 0, "", 350, 0); gdi.popX(); gdi.setCY(gdi.getHeight()); @@ -776,7 +818,7 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { } wstring srcFile; if (ix < tagNameList.size()) { - bool ro = dynamic_cast(oe->getGeneralResult(tagNameList[ix].second.first, srcFile)).isReadOnly(); + bool ro = dynamic_cast(*oe->getGeneralResult(tagNameList[ix].second.first, srcFile)).isReadOnly(); gdi.setInputStatus("DoOpen", !ro); } else @@ -866,7 +908,7 @@ void MethodEditor::makeDirty(gdioutput &gdi, DirtyFlag inside) { else if (inside == ClearDirty) dirtyInt = false; - if (gdi.hasField("SaveInside")) { + if (gdi.hasWidget("SaveInside")) { gdi.setInputStatus("SaveInside", dirtyInt); gdi.setInputStatus("Remove", resultIsInstalled()); } @@ -888,7 +930,7 @@ bool MethodEditor::checkSave(gdioutput &gdi) { } void MethodEditor::checkChangedSave(gdioutput &gdi) { - if (gdi.hasField("Source")) { + if (gdi.hasWidget("Source")) { gdi.getText("Source"); if (dynamic_cast(gdi.getBaseInfo("Source")).changed() && gdi.ask(L"Save changes in rule code?")) { @@ -952,11 +994,18 @@ void MethodEditor::debug(gdioutput &gdi_in, int id, bool isTeam) { gdi_new->clearPage(false); gdi_new->setData("MethodEditorClz", this); gdioutput &gdi = *gdi_new; - currentResult->prepareCalculations(*oe, isTeam, inputNumber); + + vector rr; + vector tt; + oe->getRunners(art->getClassId(true), 0, rr); + if (isTeam) + oe->getTeams(art->getClassId(true), tt, false); + + currentResult->prepareCalculations(*oe, false, {art->getClassId(true)}, rr, tt, inputNumber); gdi_new->addString("", fontMediumPlus, "Debug Output"); if (!isTeam) { oRunner &r = *pRunner(art); - currentResult->prepareCalculations(r); + currentResult->prepareCalculations(r, false); int rt = 0, pt = 0; RunnerStatus st = StatusUnknown; gdi.dropLine(); @@ -994,11 +1043,13 @@ void MethodEditor::debug(gdioutput &gdi_in, int id, bool isTeam) { gdi.addString("", 0, L"Points Calculation Failed: X#" + err).setColor(colorRed); } if (currentResult->hasMethod(DynamicResult::MDeduceRPoints)) - currentResult->debugDumpVariables(gdi, true); + currentResult->debugDumpVariables(gdi, false); try { - int score = currentResult->score(r, st, rt, pt, false); - gdi.addStringUT(1, "ComputedScore: " + itos(score)).setColor(colorGreen); + auto score = currentResult->score(r, st, rt, pt, false); + wstring str; + makeScore(str, score); + gdi.addStringUT(1, L"ComputedScore: " + str).setColor(colorGreen); } catch (meosException &ex) { currentResult->debugDumpVariables(gdi, true); @@ -1010,7 +1061,7 @@ void MethodEditor::debug(gdioutput &gdi_in, int id, bool isTeam) { } else { oTeam &t = *pTeam(art); - currentResult->prepareCalculations(t); + currentResult->prepareCalculations(t, false); int rt = 0, pt = 0; RunnerStatus st = StatusUnknown; gdi.dropLine(); @@ -1048,11 +1099,13 @@ void MethodEditor::debug(gdioutput &gdi_in, int id, bool isTeam) { gdi.addString("", 0, L"Points Calculation Failed: X#" + err).setColor(colorRed); } if (currentResult->hasMethod(DynamicResult::MDeduceTPoints)) - currentResult->debugDumpVariables(gdi, true); + currentResult->debugDumpVariables(gdi, false); try { - int score = currentResult->score(t, st, rt, pt); - gdi.addStringUT(1, "ComputedScore:" + itos(score)).setColor(colorGreen); + auto score = currentResult->score(t, st, rt, pt); + wstring str; + makeScore(str, score); + gdi.addStringUT(1, L"ComputedScore:" + str).setColor(colorGreen); } catch (meosException &ex) { currentResult->debugDumpVariables(gdi, true); @@ -1066,3 +1119,31 @@ void MethodEditor::debug(gdioutput &gdi_in, int id, bool isTeam) { gdi.addButton("CloseWindow", "Stäng", methodCB); gdi.refresh(); } + + +DynamicResult *MethodEditor::load(gdioutput &gdi, const string &tag, bool forceLoadCopy) { + DynamicResult *dr; + wstring srcFile; + DynamicResult &drIn = dynamic_cast(*oe->getGeneralResult(tag, srcFile)); + wasLoadedBuiltIn = drIn.isReadOnly(); + dr = new DynamicResult(drIn); + if (forceLoadCopy || wasLoadedBuiltIn) { + dr->setTag(uniqueTag("result")); + dr->setName(lang.tl("Copy of ") + dr->getName(false)); + setCurrentResult(dr, L""); + } + else + setCurrentResult(dr, srcFile); + + if (forceLoadCopy || wasLoadedBuiltIn) + makeDirty(gdi, MakeDirty); + else + makeDirty(gdi, ClearDirty); + + + gdi.clearPage(false); + show(gdi); + if (dr) dr->compile(true); + + return dr; +} \ No newline at end of file diff --git a/code/methodeditor.h b/code/methodeditor.h index a8a91d5..0a03029 100644 --- a/code/methodeditor.h +++ b/code/methodeditor.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -30,6 +30,8 @@ class oEvent; #include +class TabBase; + class MethodEditor { private: enum SaveType {NotSaved, SavedInside, SavedFile}; @@ -41,7 +43,7 @@ private: bool dirtyInt; bool wasLoadedBuiltIn; enum DirtyFlag {MakeDirty, ClearDirty, NoTouch}; - + TabBase *origin = nullptr; /// Check (and autosave) if there are unsaved changes in a dialog box void checkUnsaved(gdioutput &gdi); @@ -65,11 +67,17 @@ private: int inputNumber; void debug(gdioutput &gdi, int id, bool isTeam); + void show(gdioutput &gdi); + public: MethodEditor(oEvent *oe); virtual ~MethodEditor(); - void show(gdioutput &gdi); + void show(TabBase *dst, gdioutput &gdi); + + bool isShown(TabBase *tab) const { return origin == tab; } + + DynamicResult *load(gdioutput &gdi, const string &tag, bool forceLoadCopy); friend int methodCB(gdioutput*, int, void *); }; diff --git a/code/mysqldaemon.cpp b/code/mysqldaemon.cpp index bac1c8b..d999875 100644 --- a/code/mysqldaemon.cpp +++ b/code/mysqldaemon.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/newcompetition.cpp b/code/newcompetition.cpp index edecc3b..e9fb75d 100644 --- a/code/newcompetition.cpp +++ b/code/newcompetition.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -99,7 +99,7 @@ int TabCompetition::newGuideCB(gdioutput &gdi, int type, void *data) loadPage(gdi); } else if (bi.id == "FAll") { - if (gdi.hasField("Name")) + if (gdi.hasWidget("Name")) createCompetition(gdi); gdi.clearPage(true); gdi.fillRight(); @@ -111,7 +111,7 @@ int TabCompetition::newGuideCB(gdioutput &gdi, int type, void *data) loadPage(gdi); } else if (bi.id == "FBasic") { - if (gdi.hasField("Name")) + if (gdi.hasWidget("Name")) createCompetition(gdi); oe->getMeOSFeatures().clear(*oe); oe->getMeOSFeatures().useFeature(MeOSFeatures::Clubs, true, *oe); @@ -124,7 +124,7 @@ int TabCompetition::newGuideCB(gdioutput &gdi, int type, void *data) newCompetitionGuide(gdi, 3); } else if (bi.id == "StoreFeatures") { - if (gdi.hasField("Name")) + if (gdi.hasWidget("Name")) createCompetition(gdi); saveMeosFeatures(gdi, true); gdi.clearPage(true); @@ -136,7 +136,7 @@ int TabCompetition::newGuideCB(gdioutput &gdi, int type, void *data) loadPage(gdi); } else if (bi.id == "FIndividual") { - if (gdi.hasField("Name")) + if (gdi.hasWidget("Name")) createCompetition(gdi); oe->getMeOSFeatures().useFeature(MeOSFeatures::Speaker, true, *oe); oe->getMeOSFeatures().useFeature(MeOSFeatures::Economy, true, *oe); @@ -157,7 +157,7 @@ int TabCompetition::newGuideCB(gdioutput &gdi, int type, void *data) loadPage(gdi); } else if (bi.id == "FNoCourses" || bi.id == "FNoCoursesRelay") { - if (gdi.hasField("Name")) + if (gdi.hasWidget("Name")) createCompetition(gdi); oe->getMeOSFeatures().useFeature(MeOSFeatures::Speaker, true, *oe); oe->getMeOSFeatures().useFeature(MeOSFeatures::Economy, true, *oe); @@ -181,7 +181,7 @@ int TabCompetition::newGuideCB(gdioutput &gdi, int type, void *data) loadPage(gdi); } else if (bi.id == "FForked") { - if (gdi.hasField("Name")) + if (gdi.hasWidget("Name")) createCompetition(gdi); oe->getMeOSFeatures().useFeature(MeOSFeatures::Speaker, true, *oe); oe->getMeOSFeatures().useFeature(MeOSFeatures::Economy, true, *oe); @@ -203,7 +203,7 @@ int TabCompetition::newGuideCB(gdioutput &gdi, int type, void *data) loadPage(gdi); } else if (bi.id == "FTeam") { - if (gdi.hasField("Name")) + if (gdi.hasWidget("Name")) createCompetition(gdi); oe->getMeOSFeatures().useFeature(MeOSFeatures::Speaker, true, *oe); oe->getMeOSFeatures().useFeature(MeOSFeatures::Economy, true, *oe); @@ -435,13 +435,13 @@ void TabCompetition::createCompetition(gdioutput &gdi) { wstring start = gdi.getText("FirstStart"); oe->newCompetition(L"tmp"); - oe->setName(name); - oe->setDate(date); + oe->setName(name, true); + oe->setDate(date, true); int t = convertAbsoluteTimeHMS(start, -1); if (t > 0 && t < 3600*24) { t = max(0, t-3600); - oe->setZeroTime(formatTimeHMS(t)); + oe->setZeroTime(formatTimeHMS(t), true); } else throw meosException("Ogiltig tid"); diff --git a/code/oBase.cpp b/code/oBase.cpp index de8384f..c773d56 100644 --- a/code/oBase.cpp +++ b/code/oBase.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -42,14 +42,12 @@ static char THIS_FILE[]=__FILE__; // Construction/Destruction ////////////////////////////////////////////////////////////////////// -char RunnerStatusOrderMap[100]; oBase::oBase(oEvent *poe) { Removed = false; oe = poe; Id = 0; changed = false; - reChanged = false; counter = 0; Modified.update(); correctionNeeded = true; @@ -68,10 +66,11 @@ void oBase::remove() { bool oBase::synchronize(bool writeOnly) { - if (oe && changed) { + if (oe && (changed || transientChanged)) { changedObject(); oe->dataRevision++; } + transientChanged = false; if (oe && oe->HasDBConnection && (changed || !writeOnly)) { correctionNeeded = false; if (localObject) @@ -183,7 +182,20 @@ oDataConstInterface oBase::getDCI(void) const return dc.getConstInterface(data, getDISize(), this); } -void oBase::updateChanged() { +void oBase::updateChanged(ChangeType ct) { Modified.update(); - changed=true; + if (ct == ChangeType::Update) + changed = true; + else + transientChanged = true; +} + +void oBase::makeQuietChangePermanent() { + if (transientChanged) + changed = true; +} + +void oBase::update(SqlUpdated &info) const { + info.updated = max(sqlUpdated, info.updated); + info.counter = max(counter, info.counter); } diff --git a/code/oBase.h b/code/oBase.h index 2817016..1899dbd 100644 --- a/code/oBase.h +++ b/code/oBase.h @@ -11,7 +11,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -42,36 +42,8 @@ class oDataInterface; class oDataConstInterface; class oDataContainer; typedef void * pvoid; -typedef vector< vector > * pvectorstr; - -enum RunnerStatus {StatusOK=1, StatusDNS=20, StatusCANCEL = 21, StatusMP=3, - StatusDNF=4, StatusDQ=5, StatusMAX=6, - StatusUnknown=0, StatusNotCompetiting=99}; - -extern char RunnerStatusOrderMap[100]; - -enum SortOrder {ClassStartTime, - ClassTeamLeg, - ClassResult, - ClassCourseResult, - ClassTotalResult, - ClassTeamLegResult, - ClassFinishTime, - ClassStartTimeClub, - ClassPoints, - ClassLiveResult, - ClassKnockoutTotalResult, - SortByName, - SortByLastName, - SortByFinishTime, - SortByFinishTimeReverse, - SortByStartTime, - SortByStartTimeClass, - CourseResult, - CourseStartTime, - SortByEntryTime, - Custom, - SortEnumLastItem}; +typedef vector> * pvectorstr; +struct SqlUpdated; class oBase { public: @@ -85,11 +57,19 @@ public: friend class oBase; }; + + /** Indicate if a change is transient (quiet) or should be written to database. */ + enum class ChangeType { + Quiet, + Update + }; + private: - void storeChangeStatus() {reChanged = changed;} - void resetChangeStatus() {changed &= reChanged;} - bool reChanged; + // Changed in client, not yet sent to server bool changed; + // Changed in client, silent mode, should not be sent to server + bool transientChanged; + bool localObject; const static unsigned long long BaseGenStringFlag = 1ull << 63; const static unsigned long long Base36StringFlag = 1ull << 62; @@ -117,7 +97,7 @@ private: protected: /// Mark the object as changed (on client) and that it needs synchronize to server - virtual void updateChanged(); + void updateChanged(ChangeType ct = ChangeType::Update); /// Mark the object as "changed" (locally or remotely), eg lists and other views may need update virtual void changedObject() = 0; @@ -133,6 +113,8 @@ protected: public: + void update(SqlUpdated &info) const; + // Get a safe reference to this object const shared_ptr &getReference() { if (!myReference) { @@ -149,8 +131,10 @@ public: virtual wstring getInfo() const = 0; //Called (by a table) when user enters data in a cell - virtual bool inputData(int id, const wstring &input, int inputId, - wstring &output, bool noUpdate) {output=L""; return false;} + // Returned first is zero or a second table row to reload. + // Returned second is true to reload entire table + virtual pair inputData(int id, const wstring &input, int inputId, + wstring &output, bool noUpdate) {output=L""; return make_pair(0,false);} //Called (by a table) to fill a list box with contents in a table virtual void fillInput(int id, vector< pair > &elements, size_t &selected) @@ -162,7 +146,9 @@ public: bool isRemoved() const {return Removed;} int getAge() const {return Modified.getAge();} unsigned int getModificationTime() const {return Modified.getModificationTime();} - + // If there is a change marked as quiet, make it permanent. + void makeQuietChangePermanent(); + bool synchronize(bool writeOnly=false); wstring getTimeStamp() const; diff --git a/code/oCard.cpp b/code/oCard.cpp index 0b9ac90..157f5b2 100644 --- a/code/oCard.cpp +++ b/code/oCard.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -555,27 +555,25 @@ bool oEvent::isCardRead(const SICard &card) const return false; } +const shared_ptr
    &oCard::getTable(oEvent *oe) { + if (!oe->hasTable("cards")) { + auto table = make_shared
    (oe, 20, L"Brickor", "cards"); -Table *oEvent::getCardsTB() //Table mode -{ - oCardList::iterator it; + table->addColumn("Id", 70, true, true); + table->addColumn("Ändrad", 70, false); - Table *table=new Table(this, 20, L"Brickor", "cards"); + table->addColumn("Bricka", 120, true); + table->addColumn("Deltagare", 200, false); - table->addColumn("Id", 70, true, true); - table->addColumn("Ändrad", 70, false); + table->addColumn("Starttid", 70, false); + table->addColumn("Måltid", 70, false); + table->addColumn("Stämplingar", 70, true); - table->addColumn("Bricka", 120, true); - table->addColumn("Deltagare", 200, false); - - table->addColumn("Starttid", 70, false); - table->addColumn("Måltid", 70, false); - table->addColumn("Stämplingar", 70, true); - - table->setTableProp(Table::CAN_DELETE); - table->update(); - - return table; + table->setTableProp(Table::CAN_DELETE); + oe->setTable("cards", table); + } + + return oe->getTable("cards"); } void oEvent::generateCardTableData(Table &table, oCard *addCard) diff --git a/code/oCard.h b/code/oCard.h index bcc44d9..6780c90 100644 --- a/code/oCard.h +++ b/code/oCard.h @@ -11,7 +11,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -73,6 +73,8 @@ protected: public: + static const shared_ptr
    &getTable(oEvent *oe); + // Returns true if the card was constructed from punches. bool isConstructedFromPunches() {return ConstructedFromPunches == readId;} diff --git a/code/oClass.cpp b/code/oClass.cpp index d11ef9d..fff2d09 100644 --- a/code/oClass.cpp +++ b/code/oClass.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -45,6 +45,7 @@ #include "random.h" #include "qualification_final.h" #include "generalresult.h" +#include "metalist.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction @@ -480,7 +481,7 @@ void oClass::getNumResults(int leg, int &total, int &finished, int &dns) const { c.total++; - if (t.tStatus != StatusUnknown || t.getLegStatus(leg, false) != StatusUnknown) + if (t.tStatus != StatusUnknown || t.getLegStatus(leg, false, false) != StatusUnknown) c.finished++; } @@ -524,10 +525,12 @@ void oClass::setCourse(pCourse c) } } -void oClass::setName(const wstring &name) +void oClass::setName(const wstring &name, bool manualSet) { if (getName() != name) { - Name=name; + Name = name; + if (manualSet) + setFlag(TransferFlags::FlagManualName, true); updateChanged(); } } @@ -560,7 +563,7 @@ pClass oEvent::getClassCreate(int Id, const wstring &createName, set &e if (createName.empty() && Id>0) { oClass c(this, Id); - c.setName(getAutoClassName()); + c.setName(getAutoClassName(), false); return addClass(c); } else { @@ -1609,6 +1612,8 @@ void oClass::getCourses(int leg, vector &courses) const { //leg == -1 -> all courses courses.clear(); + set added; + if (leg <= 0 && Course) courses.push_back(Course); @@ -1617,7 +1622,7 @@ void oClass::getCourses(int leg, vector &courses) const { continue; const vector &mc = MultiCourse[cl]; for (size_t k = 0; k < mc.size(); k++) - if (find(courses.begin(), courses.end(), mc[k]) == courses.end()) + if (added.insert(mc[k]->Id).second) courses.push_back(mc[k]); } @@ -1626,7 +1631,7 @@ void oClass::getCourses(int leg, vector &courses) const { pCourse sht = courses[k]->getShorterVersion().second; int maxIter = 10; while (sht && --maxIter >= 0 ) { - if (find(courses.begin(), courses.end(), sht) == courses.end()) + if (added.insert(sht->Id).second) courses.push_back(sht); sht = sht->getShorterVersion().second; } @@ -1834,17 +1839,83 @@ void oClass::updateChangedCoursePool() { tCoursesChanged = false; } -int oClass::getBestLegTime(int leg) const { +oClass::LeaderInfo &oClass::getLeaderInfo(int leg) const { leg = mapLeg(leg); + if (leg < 0) + throw meosException(); + if (size_t(leg) >= tLeaderTime.size()) + tLeaderTime.resize(leg + 1); - if (unsigned(leg)>=tLeaderTime.size()) - return 0; - else - return tLeaderTime[leg].bestTimeOnLeg; + return tLeaderTime[leg]; } -int oClass::getBestTimeCourse(int courseId) const -{ +void oClass::LeaderInfo::updateComputed(int rt, Type t) { + switch (t) { + case Type::Leg: + if (bestTimeOnLegComputed == 0 || bestTimeOnLegComputed > rt) + bestTimeOnLegComputed = rt; + break; + + case Type::Total: + if (totalLeaderTimeComputed == 0 || totalLeaderTimeComputed > rt) + totalLeaderTimeComputed = rt; + break; + + case Type::TotalInput: + if (totalLeaderTimeInputComputed == 0 || totalLeaderTimeInputComputed > rt) + totalLeaderTimeInputComputed = rt; + break; + } +} + +void oClass::LeaderInfo::resetComputed(Type t) { + switch (t) { + case Type::Leg: + bestTimeOnLegComputed = 0; + break; + + case Type::Total: + totalLeaderTimeComputed = 0; + break; + + case Type::TotalInput: + totalLeaderTimeInputComputed = 0; + break; + } +} + +int oClass::LeaderInfo::getLeader(Type t, bool computed) const { + switch (t) { + case Type::Leg: + if (computed && bestTimeOnLegComputed > 0) + return bestTimeOnLegComputed; + else + return bestTimeOnLeg; + + case Type::Total: + if (computed && totalLeaderTimeComputed > 0) + return totalLeaderTimeComputed; + else + return totalLeaderTime; + + case Type::TotalInput: + if (computed && totalLeaderTimeInputComputed > 0) + return totalLeaderTimeInputComputed; + else + return totalLeaderTimeInput; + } + + return 0; +} + +int oClass::getBestLegTime(int leg, bool computedTime) const { + leg = mapLeg(leg); + if (unsigned(leg) >= tLeaderTime.size()) + return 0; + return getLeaderInfo(leg).getLeader(LeaderInfo::Type::Leg, computedTime); +} + +int oClass::getBestTimeCourse(int courseId) const { map::const_iterator res = tBestTimePerCourse.find(courseId); if (res == tBestTimePerCourse.end()) return 0; @@ -1852,8 +1923,7 @@ int oClass::getBestTimeCourse(int courseId) const return res->second; } -int oClass::getBestInputTime(int leg) const -{ +int oClass::getBestInputTime(int leg) const { leg = mapLeg(leg); if (unsigned(leg)>=tLeaderTime.size()) @@ -1862,18 +1932,15 @@ int oClass::getBestInputTime(int leg) const return tLeaderTime[leg].inputTime; } -int oClass::getTotalLegLeaderTime(int leg, bool includeInput) const -{ +int oClass::getTotalLegLeaderTime(int leg, bool computedTime, bool includeInput) const { leg = mapLeg(leg); - if (unsigned(leg)>=tLeaderTime.size()) return 0; - else { - if (includeInput) - return tLeaderTime[leg].totalLeaderTimeInput; - else - return tLeaderTime[leg].totalLeaderTime; - } + + if (includeInput) + return getLeaderInfo(leg).getLeader(LeaderInfo::Type::TotalInput, computedTime); + else + return getLeaderInfo(leg).getLeader(LeaderInfo::Type::Total, computedTime); } void oClass::mergeClass(int classIdSec) { @@ -1885,7 +1952,8 @@ void oClass::mergeClass(int classIdSec) { // Update teams oe->getTeams(classIdSec, t, true); - + oe->getRunners(classIdSec, 0, r, false); + for (size_t k = 0; k < t.size(); k++) { pTeam it = t[k]; it->Class = this; @@ -1899,7 +1967,6 @@ void oClass::mergeClass(int classIdSec) { it->synchronize(); //Synchronizes runners also } - oe->getRunners(classIdSec, 0, r, false); // Update runners for (size_t k = 0; k < r.size(); k++) { pRunner it = r[k]; @@ -1907,7 +1974,7 @@ void oClass::mergeClass(int classIdSec) { it->updateChanged(); it->synchronize(); } - + oe->classIdToRunnerHash.reset(); // Check heats int maxHeatThis = 0; @@ -2316,7 +2383,7 @@ void oClass::splitClass(ClassSplitMethod method, const vector &parts, vecto outClassId[k] = pcv[k]->getId(); } - setName(getName() + makeDash(L"-1")); + setName(getName() + makeDash(L"-1"), false); synchronize(); } @@ -2350,6 +2417,7 @@ void oClass::splitClass(ClassSplitMethod method, const vector &parts, vecto it->updateChanged(); it->synchronize(); } + oe->classIdToRunnerHash.reset(); } void oClass::getAgeLimit(int &low, int &high) const @@ -2748,22 +2816,23 @@ void oEvent::getStartBlocks(vector &blocks, vector &starts) const } } -Table *oEvent::getClassTB()//Table mode -{ - if (tables.count("class") == 0) { - Table *table=new Table(this, 20, L"Klasser", "classes"); +const shared_ptr
    &oClass::getTable(oEvent *oe) { + if (!oe->hasTable("class")) { + auto table = make_shared
    (oe, 20, L"Klasser", "classes"); table->addColumn("Id", 70, true, true); table->addColumn("Ändrad", 70, false); table->addColumn("Namn", 200, false); - oe->oClassData->buildTableCol(table); - tables["class"] = table; - table->addOwnership(); + + table->addColumn("Bana", 200, false); + table->addColumn("Anmälda", 70, true); + + oe->oClassData->buildTableCol(table.get()); + oe->setTable("class", table); } - tables["class"]->update(); - return tables["class"]; + return oe->getTable("class"); } void oEvent::generateClassTableData(Table &table, oClass *addClass) @@ -2785,18 +2854,51 @@ void oEvent::generateClassTableData(Table &table, oClass *addClass) void oClass::addTableRow(Table &table) const { pClass it = pClass(this); table.addRow(getId(), it); - + int row = 0; table.set(row++, *it, TID_ID, itow(getId()), false); table.set(row++, *it, TID_MODIFIED, getTimeStamp(), false); table.set(row++, *it, TID_CLASSNAME, getName(), true); + + wstring crsStr; + pCourse crs = getCourse(false); + bool canEdit = false; + if (crs) { + crsStr = crs->getName(); + canEdit = true; + } + else { + if (getNumStages() <= 1 && MultiCourse.empty()) { + crsStr = lang.tl(L"Ingen bana"); + canEdit = true; + } + else { + for (size_t leg = 0; leg < MultiCourse.size(); leg++) { + for (size_t j = 0; j < MultiCourse[leg].size(); j++) { + if (j >= 3) { + crsStr += L"…"; + break; + } + if (MultiCourse[leg][j]) { + if (!crsStr.empty()) + crsStr += L", "; + crsStr += MultiCourse[leg][j]->getName(); + } + } + if (!crsStr.empty()) + break; + } + } + } + table.set(row++, *it, TID_COURSE, crsStr, canEdit, canEdit ? CellType::cellSelection : CellType::cellEdit); + int numR = getNumRunners(true, false, false); + table.set(row++, *it, TID_NUM, itow(numR), false); + oe->oClassData->fillTableCol(*this, table, true); } - - -bool oClass::inputData(int id, const wstring &input, +pair oClass::inputData(int id, const wstring &input, int inputId, wstring &output, bool noUpdate) { synchronize(false); @@ -2807,20 +2909,27 @@ bool oClass::inputData(int id, const wstring &input, } switch(id) { case TID_CLASSNAME: - setName(input); + setName(input, true); synchronize(); output=getName(); - return true; break; + case TID_COURSE: { + pCourse c = nullptr; + if (inputId != 0) + c = oe->getCourse(inputId); + setCourse(c); + synchronize(); + output = input; + } } - return false; + return make_pair(0, false); } void oClass::fillInput(int id, vector< pair > &out, size_t &selected) { if (id>1000) { - oe->oClassData->fillInput(oData, id, 0, out, selected); + oe->oClassData->fillInput(this, id, 0, out, selected); return; } @@ -2828,12 +2937,11 @@ void oClass::fillInput(int id, vector< pair > &out, size_t &sel out.clear(); oe->fillCourses(out, true); out.push_back(make_pair(lang.tl(L"Ingen bana"), 0)); - //gdi.selectItemByData(controlId.c_str(), Course ? Course->getId() : 0); - selected = Course ? Course->getId() : 0; + pCourse c = getCourse(false); + selected = c ? c->getId() : 0; } } - void oClass::getStatistics(const set &feeLock, int &entries, int &started) const { oRunnerList::const_iterator it; @@ -3289,7 +3397,7 @@ void oClass::calculateSplits() { if (t>0) { splits[nc].push_back(t); tLegTimes[nc] = t; - if (it->statusOK() && (it->FinishTime - it->tStartTime) > 0) { + if (it->statusOK(true) && (it->FinishTime - it->tStartTime) > 0) { splitsAcc[nc].push_back(it->FinishTime - it->tStartTime); } } @@ -3392,14 +3500,24 @@ void oClass::calculateSplits() { } bool oClass::isRogaining() const { - if (Course) - return Course->getMaximumRogainingTime() > 0; + if (Course && Course->getMaximumRogainingTime() > 0) + return true; - for (size_t k = 0;kgetMaximumRogainingTime() > 0; + if (MultiCourse.size() > 0 && MultiCourse[0].size() > 0 && + MultiCourse[0][0] && MultiCourse[0][0]->getMaximumRogainingTime() > 0) + return true; + GeneralResult *gr = getResultModule(); + // Return true if result module makes point calculations and + // controls define rogaining points. Otherwize point calculations + // is assumed to be just a side effect. + if (gr && gr->isRogaining() && Course) { + for (int n = 0; n < Course->getNumControls(); n++) { + pControl ctrl = Course->getControl(n); + if (ctrl && ctrl->isRogaining(true)) + return true; + } + } return false; } @@ -3512,6 +3630,11 @@ oClass::ClassStatus oClass::getClassStatus() const { return tStatus; } +void oClass::fillClassStatus(vector> &statusClass) { + statusClass.push_back(make_pair(L"", L"OK")); + statusClass.push_back(make_pair(L"IR", L"Struken med återbetalning")); + statusClass.push_back(make_pair(L"I", L"Struken utan återbetalning")); +} void oClass::clearCache(bool recalculate) { if (recalculate) oe->reCalculateLeaderTimes(getId()); @@ -3637,6 +3760,8 @@ bool oClass::checkForking(vector< vector > &legOrder, bool valid = true; long long hash = 0; for (size_t j = 0; j < it->Runners.size(); j++) { + if (getLegType(j) == LTExtra || getLegType(j) == LTIgnore) + continue; pCourse crs; if (it->Runners[j] && (crs = it->Runners[j]->getCourse(false)) != 0) { if (it->Runners[j]->getNumShortening() > 0) { @@ -4274,7 +4399,7 @@ void oClass::drawSeeded(ClassSeedMethod seed, int leg, int firstStart, for (size_t k = 0; k < startOrder.size(); k++) { int kx = k/pairSize; - startOrder[k]->setStartTime(firstStart + interval * kx, true, false, true); + startOrder[k]->setStartTime(firstStart + interval * kx, true, oBase::ChangeType::Update, false); startOrder[k]->synchronize(true); } } @@ -4434,8 +4559,10 @@ const pClass oClass::getVirtualClass(int instance) const { if (parentClass) return parentClass->getVirtualClass(instance); - if (size_t(instance) < virtualClasses.size() && virtualClasses[instance]) + if (size_t(instance) < virtualClasses.size() && virtualClasses[instance]) { + virtualClasses[instance]->parentClass = pClass(this); return virtualClasses[instance]; + } if (instance >= getNumQualificationFinalClasses()) return pClass(this); // Invalid @@ -4510,7 +4637,7 @@ void oClass::loadQualificationFinalScheme(const wstring &fileName) { pTeam t = r.getTeam(); if (t == 0) { t = oe->addTeam(r.getName(), r.getClubId(), getId()); - t->setStartNo(r.getStartNo(), false); + t->setStartNo(r.getStartNo(), oBase::ChangeType::Update); t->setRunner(0, &r, true); } r.synchronizeAll(); @@ -4627,7 +4754,7 @@ void oClass::updateFinalClasses(oRunner *causingResult, bool updateStartNumbers) } } - gr.calculateIndividualResults(classSplit[i], oListInfo::Classwise, true, 0); + gr.calculateIndividualResults(classSplit[i], false, oListInfo::Classwise, true, 0); int lastPlace = 0, orderPlace = 1; int numEqual = 0; for (size_t k = 0; k < classSplit[i].size(); k++) { @@ -4676,7 +4803,7 @@ void oClass::updateFinalClasses(oRunner *causingResult, bool updateStartNumbers) di.setInt("Heat", heat); runnerToChange->classInstanceRev.first = -1; //oe->gdibase.addStringUT(0, L"HU:" + thisRunner.getName() + L" " + itow(oldHeat) + L"->" + itow(heat)); - runnerToChange->apply(false, nullptr, false); + runnerToChange->apply(ChangeType::Quiet, nullptr); runnerToChange->synchronize(); if (runnerToChange->getFinishTime() > 0) needIter = true; @@ -4743,3 +4870,67 @@ bool oEvent::hasAnyRestartTime() const { return false; } + +GeneralResult *oClass::getResultModule() const { + const string &tag = getResultModuleTag(); + if (tag.empty()) + return nullptr; + wstring sf; + return oe->getGeneralResult(tag, sf).get(); +} + +void oClass::setResultModule(const string &tag) { + string oldTag = getResultModuleTag(); + if (tag == oldTag) + return; + + vector cls; + oe->getClasses(cls, false); + bool inUse = false; + bool oldInUse = false; + + for (pClass c : cls) { + if (c == this) + continue; + + if (c->getResultModuleTag() == tag) { + inUse = true; + } + if (c->getResultModuleTag() == oldTag) { + oldInUse = true; + } + } + + if (!tag.empty()) { + wstring fn; + auto &ptr = oe->getGeneralResult(tag, fn); + if (ptr->isDynamic()) { + auto dptr = dynamic_pointer_cast(ptr); + oe->getListContainer().updateGeneralResult(tag, dptr); + } + } + if (!oldTag.empty() && !oldInUse) { + oe->getListContainer().updateGeneralResult(oldTag, nullptr); + } + oe->synchronize(true); + wstring wtag(tag.begin(), tag.end()); + getDI().setString("Result", wtag); +} + +const string &oClass::getResultModuleTag() const { + auto ws = getDCI().getString("Result"); + string &s = StringCache::getInstance().get(); + s.clear(); + s.insert(s.begin(), ws.begin(), ws.end()); + return s; +} + +bool oClass::hasFlag(TransferFlags flag) const { + return (getDCI().getInt("TransferFlags") & flag) != 0; +} + +void oClass::setFlag(TransferFlags flag, bool onoff) { + int cf = getDCI().getInt("TransferFlags"); + cf = onoff ? (cf | flag) : (cf & (~flag)); + getDI().setInt("TransferFlags", cf); +} diff --git a/code/oClass.h b/code/oClass.h index f2c153a..4249304 100644 --- a/code/oClass.h +++ b/code/oClass.h @@ -11,7 +11,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -39,6 +39,7 @@ class oClass; typedef oClass* pClass; class oDataInterface; +class GeneralResult; const int MaxClassId = 1000000; @@ -67,10 +68,11 @@ enum { nLegTypes = LT_max }; enum BibMode { - BibSame, - BibAdd, - BibFree, - BibLeg, + BibUndefined = -1, + BibSame = 0, + BibAdd = 1, + BibFree = 2, + BibLeg = 3, }; enum AutoBibType { @@ -166,15 +168,45 @@ protected: //First: best time on leg //Second: Total leader time (total leader) struct LeaderInfo { - LeaderInfo() {bestTimeOnLeg = 0; totalLeaderTime = 0; inputTime = 0; totalLeaderTimeInput = 0;} - void reset() {bestTimeOnLeg = 0; totalLeaderTime = 0; inputTime = 0; totalLeaderTimeInput = 0;} + LeaderInfo() { + reset(); + } + void reset() { + bestTimeOnLeg = 0; + bestTimeOnLegComputed = 0; + + totalLeaderTime = 0; + totalLeaderTimeComputed = 0; + + inputTime = 0; + totalLeaderTimeInput = 0; + totalLeaderTimeInputComputed = 0; + } + int bestTimeOnLeg; + int bestTimeOnLegComputed; // Computed be default result module + int totalLeaderTime; + int totalLeaderTimeComputed; // Computed be default result module + int totalLeaderTimeInput; //Team total including input + int totalLeaderTimeInputComputed; //Team total including input + int inputTime; + + enum class Type { + Leg, + Total, + TotalInput + }; + void resetComputed(Type t); + void updateComputed(int rt, Type t); + int getLeader(Type t, bool computed) const; }; - vector tLeaderTime; + LeaderInfo &getLeaderInfo(int leg) const; + + mutable vector tLeaderTime; map tBestTimePerCourse; int tSplitRevision; @@ -238,10 +270,10 @@ protected: void markSQLChanged(int leg, int control); void addTableRow(Table &table) const; - bool inputData(int id, const wstring &input, int inputId, - wstring &output, bool noUpdate); + pair inputData(int id, const wstring &input, int inputId, + wstring &output, bool noUpdate) override; - void fillInput(int id, vector< pair > &elements, size_t &selected); + void fillInput(int id, vector> &elements, size_t &selected) override; void exportIOFStart(xmlparser &xml); @@ -300,6 +332,16 @@ protected: void configureInstance(int instance, bool allowCreation) const; public: + static const shared_ptr
    &getTable(oEvent *oe); + + enum TransferFlags { + FlagManualName = 1, + FlagManualFees = 2, + }; + + bool hasFlag(TransferFlags flag) const; + void setFlag(TransferFlags flag, bool state); + /** The master class in a qualification/final scheme. */ const pClass getParentClass() const { return parentClass; } @@ -398,6 +440,7 @@ public: const pClass getVirtualClass(int instance) const; ClassStatus getClassStatus() const; + static void fillClassStatus(vector> &statusClass); ClassMetaType interpretClassType() const; @@ -433,10 +476,10 @@ public: void getStatistics(const set &feeLock, int &entries, int &started) const; int getBestInputTime(int leg) const; - int getBestLegTime(int leg) const; + int getBestLegTime(int leg, bool computedTime) const; int getBestTimeCourse(int courseId) const; - int getTotalLegLeaderTime(int leg, bool includeInput) const; + int getTotalLegLeaderTime(int leg, bool computedTime, bool includeInput) const; wstring getInfo() const; // Returns true if the class has a pool of courses @@ -565,7 +608,7 @@ public: int getNumberMaps(bool rawAttribute = false) const; const wstring &getName() const {return Name;} - void setName(const wstring &name); + void setName(const wstring &name, bool manualSet); void Set(const xmlobject *xo); bool Write(xmlparser &xml); @@ -642,11 +685,13 @@ public: void getParallelCourseGroup(int leg, int startNo, vector< pair > &group) const; // Returns 0 for no parallel selection (= normal mode) pCourse selectParallelCourse(const oRunner &r, const SICard &sic); - void getParallelRange(int leg, int &parLegRangeMin, int &parLegRangeMax) const; - bool hasAnyCourse(const set &crsId) const; + GeneralResult *getResultModule() const; + void setResultModule(const string &tag); + const string &getResultModuleTag() const; + oClass(oEvent *poe); oClass(oEvent *poe, int id); virtual ~oClass(); diff --git a/code/oClub.cpp b/code/oClub.cpp index 69f7191..8ea0cd6 100644 --- a/code/oClub.cpp +++ b/code/oClub.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -290,28 +290,24 @@ void oClub::buildTableCol(oEvent *oe, Table *table) { } #define TB_CLUBS "clubs" -Table *oEvent::getClubsTB()//Table mode -{ - if (tables.count("club") == 0) { - Table *table=new Table(this, 20, L"Klubbar", TB_CLUBS); +const shared_ptr
    &oClub::getTable(oEvent *oe) { + if (!oe->hasTable("club")) { + auto table = make_shared
    (oe, 20, L"Klubbar", TB_CLUBS); table->addColumn("Id", 70, true, true); table->addColumn("Ändrad", 70, false); table->addColumn("Namn", 200, false); - oe->oClubData->buildTableCol(table); + oe->oClubData->buildTableCol(table.get()); table->addColumn("Deltagare", 70, true); table->addColumn("Avgift", 70, true); table->addColumn("Betalat", 70, true); - tables["club"] = table; - table->addOwnership(); + oe->setTable("club", table); } - tables["club"]->update(); - return tables["club"]; - + return oe->getTable("club"); } void oEvent::generateClubTableData(Table &table, oClub *addClub) @@ -357,8 +353,8 @@ void oClub::addTableRow(Table &table) const { } } -bool oClub::inputData(int id, const wstring &input, - int inputId, wstring &output, bool noUpdate) +pair oClub::inputData(int id, const wstring &input, + int inputId, wstring &output, bool noUpdate) { synchronize(false); @@ -371,17 +367,16 @@ bool oClub::inputData(int id, const wstring &input, setName(input); synchronize(); output = getName(); - return true; break; } - return false; + return make_pair(0, false); } void oClub::fillInput(int id, vector< pair > &out, size_t &selected) { if (id>1000) { - oe->oClubData->fillInput(oData, id, 0, out, selected); + oe->oClubData->fillInput(this, id, 0, out, selected); return; } } @@ -513,23 +508,23 @@ void oClub::addRunnerInvoiceLine(const pRunner r, bool inTeam, if (type == oClassIndividRelay || type == oClassRelay) { int leg = r->getLegNumber(); - if (t->getLegStatus(leg, false) == StatusOK) - ts = t->getLegPlaceS(leg, false)+ L" (" + r->getRunningTimeS() + L")"; + if (t->getLegStatus(leg, true, false) == StatusOK) + ts = t->getLegPlaceS(leg, false)+ L" (" + r->getRunningTimeS(true) + L")"; else - ts = t->getLegStatusS(leg, false)+ L" (" + r->getRunningTimeS() +L")"; + ts = t->getLegStatusS(leg, true, false)+ L" (" + r->getRunningTimeS(true) +L")"; } else - ts = r->getPrintPlaceS(true)+ L" (" + r->getRunningTimeS() + L")"; + ts = r->getPrintPlaceS(true)+ L" (" + r->getRunningTimeS(true) + L")"; } else - ts = r->getStatusS(true); + ts = r->getStatusS(true, true); } else { if (r->getTotalStatus()==StatusOK) { ts = r->getPrintTotalPlaceS(true) + L" (" + r->getTotalRunningTimeS() + L")"; } else if (r->getTotalStatus()!=StatusNotCompetiting) - ts = r->getStatusS(true); + ts = r->getStatusS(true, true); else { ts = r->getInputStatusS(); } @@ -585,10 +580,10 @@ void oClub::addTeamInvoiceLine(const pTeam t, const map &definedPa ts = L"-"; else { if (t->getStatus()==StatusOK) { - ts = t->getPrintPlaceS(true) + L" (" + t->getRunningTimeS() + L")"; + ts = t->getPrintPlaceS(true) + L" (" + t->getRunningTimeS(true) + L")"; } else - ts = t->getStatusS(true); + ts = t->getStatusS(true, true); } @@ -817,7 +812,7 @@ void oEvent::printInvoices(gdioutput &gdi, InvoicePrintType type, oClub::assignInvoiceNumber(*this, false); oClubList::iterator it; - oe->calculateTeamResults(false); + oe->calculateTeamResults(set(), ResultType::ClassResult); oe->sortTeams(ClassStartTime, 0, true); oe->calculateResults(set(), ResultType::ClassResult); oe->sortRunners(ClassStartTime); diff --git a/code/oClub.h b/code/oClub.h index 9e8ffd3..3fa453f 100644 --- a/code/oClub.h +++ b/code/oClub.h @@ -11,7 +11,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -78,10 +78,10 @@ protected: virtual int getTableId() const; - bool inputData(int id, const wstring &input, int inputId, - wstring &output, bool noUpdate); + pair inputData(int id, const wstring &input, int inputId, + wstring &output, bool noUpdate) override; - void fillInput(int id, vector< pair > &elements, size_t &selected); + void fillInput(int id, vector< pair > &elements, size_t &selected) override; void exportIOFClub(xmlparser &xml, bool compact) const; @@ -141,6 +141,8 @@ protected: public: + static const shared_ptr
    &getTable(oEvent *oe); + /** Assign invoice numbers to all clubs. */ static void assignInvoiceNumber(oEvent &oe, bool reset); diff --git a/code/oControl.cpp b/code/oControl.cpp index 8a34360..4bc0057 100644 --- a/code/oControl.cpp +++ b/code/oControl.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -787,7 +787,7 @@ const vector< pair > &oEvent::fillControlStatus(vector< pairgetMeOSFeatures().hasFeature(MeOSFeatures::Rogaining)) + if (getMeOSFeatures().hasFeature(MeOSFeatures::Rogaining)) out.push_back(make_pair(lang.tl(L"Rogaining"), oControl::StatusRogaining)); out.push_back(make_pair(lang.tl(L"Utan tidtagning"), oControl::StatusNoTiming)); out.push_back(make_pair(lang.tl(L"Trasig"), oControl::StatusBad)); @@ -796,10 +796,10 @@ const vector< pair > &oEvent::fillControlStatus(vector< pair &oControl::getTable(oEvent *oe) { + if (!oe->hasTable("control")) { + auto table = make_shared
    (oe, 20, L"Kontroller", "controls"); table->addColumn("Id", 70, true, true); table->addColumn("Ändrad", 70, false); @@ -812,15 +812,13 @@ Table *oEvent::getControlTB()//Table mode table->addColumn("Bomtid (medel)", 70, true, true); table->addColumn("Bomtid (median)", 70, true, true); - oe->oControlData->buildTableCol(table); - tables["control"] = table; - table->addOwnership(); + oe->oControlData->buildTableCol(table.get()); + oe->setTable("control", table); table->setTableProp(Table::CAN_DELETE); } - tables["control"]->update(); - return tables["control"]; + return oe->getTable("control"); } void oEvent::generateControlTableData(Table &table, oControl *addControl) @@ -862,7 +860,7 @@ void oControl::addTableRow(Table &table) const { oe->oControlData->fillTableCol(it, table, true); } -bool oControl::inputData(int id, const wstring &input, +pair oControl::inputData(int id, const wstring &input, int inputId, wstring &output, bool noUpdate) { synchronize(false); @@ -875,27 +873,26 @@ bool oControl::inputData(int id, const wstring &input, setName(input); synchronize(); output=getName(); - return true; + break; case TID_STATUS: setStatus(ControlStatus(inputId)); synchronize(true); output = getStatusS(); - return true; + break; case TID_CODES: - bool stat = setNumbers(input); + setNumbers(input); synchronize(true); output = codeNumbers(); - return stat; - break; + break; } - return false; + return make_pair(0, false); } void oControl::fillInput(int id, vector< pair > &out, size_t &selected) { if (id>1000) { - oe->oControlData->fillInput(oData, id, 0, out, selected); + oe->oControlData->fillInput(this, id, 0, out, selected); return; } diff --git a/code/oControl.h b/code/oControl.h index 5a89743..c668b65 100644 --- a/code/oControl.h +++ b/code/oControl.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -81,11 +81,11 @@ protected: /// Table methods void addTableRow(Table &table) const; - bool inputData(int id, const wstring &input, - int inputId, wstring &output, bool noUpdate); + pair inputData(int id, const wstring &input, + int inputId, wstring &output, bool noUpdate) override; /// Table methods - void fillInput(int id, vector< pair > &elements, size_t &selected); + void fillInput(int id, vector< pair > &elements, size_t &selected) override; /** Get internal data buffers for DI */ oDataContainer &getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const; @@ -117,6 +117,8 @@ protected: void changedObject(); public: + static const shared_ptr
    &getTable(oEvent *oe); + static int getControlIdByName(const oEvent &oe, const string &name); // Returns true if controls is considered a radio control. diff --git a/code/oCourse.cpp b/code/oCourse.cpp index fb53c3b..2533f54 100644 --- a/code/oCourse.cpp +++ b/code/oCourse.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -34,6 +34,7 @@ #include "meosexception.h" #include #include "gdioutput.h" +#include "Table.h" #include ////////////////////////////////////////////////////////////////////// @@ -1436,3 +1437,120 @@ void oCourse::getClasses(vector &usageClass) const { } } +const shared_ptr
    &oCourse::getTable(oEvent *oe) { + oe->synchronizeList(oListId::oLCourseId); + oClassList::iterator it; + int numCtrl = 20; + vector crs; + oe->getCourses(crs); + for (pCourse pc : crs) + numCtrl = max(numCtrl, pc->getNumControls() + 5); + + static int generatedNumCtrl = 0; + + if (generatedNumCtrl != numCtrl || !oe->hasTable("course")) { + auto table = make_shared
    (oe, 20, L"Banor", "courses"); + + table->addColumn("Id", 70, true, true); + table->addColumn("Ändrad", 70, false); + table->addColumn("Namn", 200, false); + table->addColumn("Startande", 70, true); + table->addColumn("Klasser", 200, false); + + for (int i = 0; i < numCtrl; i++) + table->addColumn("#C" + itos(i + 1), 40, true, true); + + generatedNumCtrl = numCtrl; + oe->oCourseData->buildTableCol(table.get()); + oe->setTable("course", table); + } + + return oe->getTable("course"); +} + +void oCourse::generateTableData(oEvent *oe, Table &table, oCourse *add) { + if (add) { + add->addTableRow(table); + return; + } + + oe->synchronizeList(oListId::oLCourseId); + oClassList::iterator it; + + vector crs; + oe->getCourses(crs); + for (pCourse pc : crs) + pc->addTableRow(table); +} + +void oCourse::addTableRow(Table &table) const { + pCourse it = pCourse(this); + table.addRow(getId(), it); + + int row = 0; + table.set(row++, *it, TID_ID, itow(getId()), false); + table.set(row++, *it, TID_MODIFIED, getTimeStamp(), false); + table.set(row++, *it, TID_NAME, getName(), true); + + table.set(row++, *it, TID_NUM, itow(getNumUsedMaps(true)), false); + vector cls; + getClasses(cls); + sort(cls.begin(), cls.end(), [](const pClass &a, const pClass &b) {return *a < *b; }); + wstring clsStr; + for (pClass c : cls) { + if (!clsStr.empty()) + clsStr += L", "; + clsStr += c->getName(); + } + table.set(row++, *it, TID_CLASSNAME, clsStr, false); + + for (int i = 0; i < getNumControls(); i++) { + table.set(row++, *it, 100+i, Controls[i] ? itow(Controls[i]->getId()) : L"", true); + } + oe->oCourseData->fillTableCol(*this, table, true); +} + +pair oCourse::inputData(int id, const wstring &input, + int inputId, wstring &output, bool noUpdate) { + synchronize(false); + + if (id>1000) { + return oe->oCourseData->inputData(this, id, input, + inputId, output, noUpdate); + } + switch (id) { + case TID_NAME: + setName(input); + synchronize(); + output = getName(); + break; + } + + if (id >= 100) { + int cix = id - 100; + int v = _wtoi(input.c_str()); + pControl ctrl = oe->getControl(v); + if (!ctrl && v > 32 && v < 300) + ctrl = oe->addControl(v, v, L""); + + if (ctrl && (cix == 0 || cix <= getNumControls())) { + if (cix == getNumControls()) + addControl(ctrl->getId()); + else { + Controls[cix] = ctrl; + updateChanged(); + } + oe->reEvaluateCourse(getId(), false); + synchronize(true); + } + } + + return make_pair(0, false); +} + +void oCourse::fillInput(int id, vector< pair > &out, size_t &selected) { + if (id>1000) { + oe->oCourseData->fillInput(this, id, 0, out, selected); + return; + } +} diff --git a/code/oCourse.h b/code/oCourse.h index 7e69d28..7eed4cc 100644 --- a/code/oCourse.h +++ b/code/oCourse.h @@ -11,7 +11,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -91,8 +91,16 @@ protected: void changedObject(); + static void generateTableData(oEvent *oe, Table &table, oCourse *addControl); + void addTableRow(Table &table) const; + + pair inputData(int id, const wstring &input, + int inputId, wstring &output, bool noUpdate) override; + void fillInput(int id, vector> &out, size_t &selected) override; + public: + static const shared_ptr
    &getTable(oEvent *oe); // Get an identity sum based on controls int getIdSum(int nControls); diff --git a/code/oDataContainer.cpp b/code/oDataContainer.cpp index c272f01..6a92652 100644 --- a/code/oDataContainer.cpp +++ b/code/oDataContainer.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -42,6 +42,10 @@ oDataContainer::oDataContainer(int maxsize) { oDataContainer::~oDataContainer(void) { } +CellType oDataDefiner::getCellType() const { + return CellType::cellEdit; +} + oDataInfo::oDataInfo() { memset(Name, 0, sizeof(Name)); Index = 0; @@ -51,7 +55,6 @@ oDataInfo::oDataInfo() { tableIndex = 0; decimalSize = 0; decimalScale = 1; - dataDefiner = 0; zeroSortPadding = 0; memset(Description, 0, sizeof(Description)); } @@ -60,8 +63,8 @@ oDataInfo::~oDataInfo() { } oDataInfo &oDataContainer::addVariableInt(const char *name, - oIntSize isize, const char *description, - const oDataDefiner *dataDef) { + oIntSize isize, const char *description, + const shared_ptr &dataDef) { oDataInfo odi; odi.dataDefiner = dataDef; odi.Index=dataPointer; @@ -95,12 +98,12 @@ oDataInfo &oDataContainer::addVariableDecimal(const char *name, const char *desc return odi; } -oDataInfo &oDataContainer::addVariableString(const char *name, const char *descr, const oDataDefiner *dataDef) { +oDataInfo &oDataContainer::addVariableString(const char *name, const char *descr, const shared_ptr &dataDef) { return addVariableString(name, -1, descr, dataDef); } oDataInfo &oDataContainer::addVariableString(const char *name, int maxChar, - const char *descr, const oDataDefiner *dataDef) + const char *descr, const shared_ptr &dataDef) { oDataInfo odi; odi.dataDefiner = dataDef; @@ -129,7 +132,7 @@ oDataInfo &oDataContainer::addVariableString(const char *name, int maxChar, } oDataInfo &oDataContainer::addVariableEnum(const char *name, int maxChar, const char *descr, - const vector< pair > enumValues) { + const vector< pair > enumValues) { oDataInfo &odi = addVariableString(name, maxChar, descr); odi.SubType = oSSEnum; for (size_t k = 0; k 0 || stringArrayIndexPointer>2) { - vector< vector > &str = *strptr; + vector> &str = *strptr; str.clear(); str.resize(stringArrayIndexPointer); str[0].resize(stringIndexPointer); @@ -598,7 +601,7 @@ bool oDataContainer::saveDataFields(oBase *ob, gdioutput &gdi) { string Id=di.Name+string("_odc"); - if (!gdi.hasField(Id)) { + if (!gdi.hasWidget(Id)) { continue; } if (di.Type==oDTInt){ @@ -1011,7 +1014,7 @@ void oDataContainer::buildTableCol(Table *table) oDataInfo &di=ordered[kk]; if (di.dataDefiner) { - table->addDataDefiner(di.Name, di.dataDefiner); + table->addDataDefiner(di.Name, di.dataDefiner.get()); int w = strlen(di.Description)*6; di.tableIndex = di.dataDefiner->addTableColumn(table, di.Description, w); } @@ -1106,8 +1109,12 @@ int oDataContainer::fillTableCol(const oBase &owner, Table &table, bool canEdit) oBase &ob = *(oBase *)&owner; for (size_t kk = 0; kk < ordered.size(); kk++) { const oDataInfo &di=ordered[kk]; - if (di.dataDefiner != 0) { - table.set(di.tableIndex, ob, 1000+di.tableIndex, di.dataDefiner->formatData(&ob), canEdit); + if (di.dataDefiner != nullptr) { + if (di.tableIndex >= 0) { + table.set(di.tableIndex, ob, 1000 + di.tableIndex, + di.dataDefiner->formatData(&ob), canEdit && di.dataDefiner->canEdit(), + di.dataDefiner->getCellType()); + } } else if (di.Type==oDTInt) { LPBYTE vd=LPBYTE(data)+di.Index; @@ -1150,33 +1157,34 @@ int oDataContainer::fillTableCol(const oBase &owner, Table &table, bool canEdit) const wstring &str = (*strptr)[0][di.Index]; table.set(di.tableIndex, *((oBase*)&owner), 1000+di.tableIndex, str, canEdit, cellEdit); } - nextIndex = di.tableIndex + 1; + if (di.tableIndex >= 0) + nextIndex = di.tableIndex + 1; } return nextIndex; } -bool oDataContainer::inputData(oBase *ob, int id, - const wstring &input, int inputId, - wstring &output, bool noUpdate) +pair oDataContainer::inputData(oBase *ob, int id, + const wstring &input, int inputId, + wstring &output, bool noUpdate) { void *data, *oldData; vector< vector > *strptr; ob->getDataBuffers(data, oldData, strptr); for (size_t kk = 0; kk < ordered.size(); kk++) { - const oDataInfo &di=ordered[kk]; + const oDataInfo &di = ordered[kk]; - if (di.tableIndex+1000==id) { + if (di.tableIndex + 1000 == id) { if (di.dataDefiner) { const wstring &src = di.dataDefiner->formatData(ob); - output = di.dataDefiner->setData(ob, input); + auto ret = di.dataDefiner->setData(ob, input, output, inputId); bool ch = output != src; if (ch && noUpdate == false) ob->synchronize(true); - return ch; + return ret; } - else if (di.Type==oDTInt) { - LPBYTE vd=LPBYTE(data)+di.Index; + else if (di.Type == oDTInt) { + LPBYTE vd = LPBYTE(data) + di.Index; int no = 0; if (di.SubType == oISCurrency) { @@ -1219,7 +1227,7 @@ bool oDataContainer::inputData(oBase *ob, int id, oBase::converExtIdentifierString(out64, outbf); output = outbf; - return k64 != no64; + return make_pair(0, false); } else no = _wtoi(input.c_str()); @@ -1240,28 +1248,27 @@ bool oDataContainer::inputData(oBase *ob, int id, formatNumber(outN, di, bf); output = bf; - return k != no; } - else if (di.Type==oDTString) { - LPBYTE vd=LPBYTE(data)+di.Index; + else if (di.Type == oDTString) { + LPBYTE vd = LPBYTE(data) + di.Index; const wchar_t *str = input.c_str(); if (di.SubType == oSSEnum) { - size_t ix = inputId-1; + size_t ix = inputId - 1; if (ix < di.enumDescription.size()) { str = di.enumDescription[ix].first.c_str(); } } - if (wcscmp((wchar_t *)vd, str)!=0) { - wcsncpy_s((wchar_t *)vd, di.Size/sizeof(wchar_t), str, (di.Size-1)/sizeof(wchar_t)); + if (wcscmp((wchar_t *)vd, str) != 0) { + wcsncpy_s((wchar_t *)vd, di.Size / sizeof(wchar_t), str, (di.Size - 1) / sizeof(wchar_t)); ob->updateChanged(); if (noUpdate == false) ob->synchronize(true); if (di.SubType == oSSEnum) { - size_t ix = inputId-1; + size_t ix = inputId - 1; if (ix < di.enumDescription.size()) { output = lang.tl(di.enumDescription[ix].second); // This might be incorrect if data was changed on server, @@ -1269,16 +1276,15 @@ bool oDataContainer::inputData(oBase *ob, int id, // Anyway, the row will typically be reloaded } else - output=(wchar_t *)vd; + output = (wchar_t *)vd; } else - output=(wchar_t *)vd; - return true; + output = (wchar_t *)vd; + return make_pair(0, false); } else - output=input; + output = input; - return false; } else if (di.Type == oDTStringDynamic) { wstring &vd = (*strptr)[0][di.Index]; @@ -1290,28 +1296,28 @@ bool oDataContainer::inputData(oBase *ob, int id, ob->synchronize(true); output = (*strptr)[0][di.Index]; - return true; } else - output=input; - - return false; + output = input; } } } - return true; + return make_pair(0, false); } -void oDataContainer::fillInput(const void *data, int id, const char *name, +void oDataContainer::fillInput(const oBase *obj, int id, const char *name, vector< pair > &out, size_t &selected) const { + void *data, *olddata; + pvectorstr strData; + obj->getDataBuffers(data, olddata, strData); const oDataInfo * info = findVariable(name); if (!info) { for (size_t kk = 0; kk < ordered.size(); kk++) { const oDataInfo &di=ordered[kk]; - if (di.tableIndex+1000==id && di.Type == oDTString && di.SubType == oSSEnum) { + if (di.tableIndex+1000 == id) { info = &di; break; } @@ -1328,6 +1334,9 @@ void oDataContainer::fillInput(const void *data, int id, const char *name, selected = k+1; } } + else if (info && info->dataDefiner) { + info->dataDefiner->fillInput(obj, out, selected); + } else throw meosException("Invalid enum"); } diff --git a/code/oDataContainer.h b/code/oDataContainer.h index 8d22b36..18701cc 100644 --- a/code/oDataContainer.h +++ b/code/oDataContainer.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -30,15 +30,24 @@ #include "inthashmap.h" class Table; +enum CellType; class oDataDefiner { public: virtual ~oDataDefiner() {} virtual const wstring &formatData(const oBase *obj) const = 0; - virtual wstring setData(oBase *obj, const wstring &input) const = 0; + virtual pair setData(oBase *obj, const wstring &input, wstring &output, int inputId) const = 0; + virtual void fillInput(const oBase *obj, vector> &out, size_t &selected) const {} + /** Used to define/add the table column in the table*/ virtual int addTableColumn(Table *table, const string &description, int minWidth) const = 0; virtual void prepare(oEvent *oe) const {} + + // Return false to be cell read-only + virtual bool canEdit() const { return true; } + + // Return the desired cell type + virtual CellType getCellType() const; }; struct oDataInfo { @@ -51,8 +60,8 @@ struct oDataInfo { char Description[48]; int decimalSize; int decimalScale; - vector< pair > enumDescription; - const oDataDefiner *dataDefiner; + vector> enumDescription; + shared_ptr dataDefiner; int zeroSortPadding; oDataInfo(); ~oDataInfo(); @@ -141,12 +150,12 @@ public: oDataConstInterface getConstInterface(const void *data, int datasize, const oBase *ob) const; - oDataInfo &addVariableInt(const char *name, oIntSize isize, const char *descr, const oDataDefiner *dataDef = 0); + oDataInfo &addVariableInt(const char *name, oIntSize isize, const char *descr, const shared_ptr &dataDef = nullptr); oDataInfo &addVariableDecimal(const char *name, const char *descr, int fixedDeci); oDataInfo &addVariableDate(const char *name, const char *descr){return addVariableInt(name, oISDate, descr);} oDataInfo &addVariableCurrency(const char *name, const char *descr){return addVariableInt(name, oISCurrency, descr);} - oDataInfo &addVariableString(const char *name, int maxChar, const char *descr, const oDataDefiner *dataDef = 0); - oDataInfo &addVariableString(const char *name, const char *descr, const oDataDefiner *dataDef = 0); + oDataInfo &addVariableString(const char *name, int maxChar, const char *descr, const shared_ptr &dataDef = nullptr); + oDataInfo &addVariableString(const char *name, const char *descr, const shared_ptr &dataDef = nullptr); oDataInfo &addVariableEnum(const char *name, int maxChar, const char *descr, const vector< pair > enumValues); @@ -180,10 +189,10 @@ public: int fillTableCol(const oBase &owner, Table &table, bool canEdit) const; void buildTableCol(Table *table); - bool inputData(oBase *ob, int id, const wstring &input, int inputId, wstring &output, bool noUpdate); + pair inputData(oBase *ob, int id, const wstring &input, int inputId, wstring &output, bool noUpdate); // Use id (table internal) or name - void fillInput(const void *data, int id, const char *name, vector< pair > &out, size_t &selected) const; + void fillInput(const oBase *ob, int id, const char *name, vector< pair > &out, size_t &selected) const; bool setEnum(oBase *ob, const char *name, int selectedIndex); @@ -298,7 +307,7 @@ public: {oDC->set(oB, xo);} void fillInput(const char *name, vector< pair > &out, size_t &selected) const { - oDC->fillInput(Data, -1, name, out, selected); + oDC->fillInput(oB, -1, name, out, selected); } bool setEnum(const char *name, int selectedIndex) { @@ -380,7 +389,7 @@ public: {return oDC->getDataAmountMeasure(Data);} void fillInput(const char *name, vector< pair > &out, size_t &selected) const { - oDC->fillInput(Data, -1, name, out, selected); + oDC->fillInput(oB, -1, name, out, selected); } oDataConstInterface(const oDataContainer *odc, const void *data, const oBase *ob); diff --git a/code/oEvent.cpp b/code/oEvent.cpp index cf03603..4b8ede5 100644 --- a/code/oEvent.cpp +++ b/code/oEvent.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -68,44 +68,46 @@ int oEvent::dbVersion = 83; class RelativeTimeFormatter : public oDataDefiner { string name; - public: - RelativeTimeFormatter(const char *n) : name(n) {} +public: + RelativeTimeFormatter(const char *n) : name(n) {} - const wstring &formatData(const oBase *obj) const { - int t = obj->getDCI().getInt(name); - if (t <= 0) - return makeDash(L"-"); - return obj->getEvent()->getAbsTime(t); - } - wstring setData(oBase *obj, const wstring &input) const { - int t = obj->getEvent()->getRelativeTime(input); - obj->getDI().setInt(name.c_str(), t); - return formatData(obj); - } - int addTableColumn(Table *table, const string &description, int minWidth) const { - return table->addColumn(description, max(minWidth, 90), false, true); - } + 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) {} +public: + AbsoluteTimeFormatter(const char *n) : name(n) {} - const wstring &formatData(const oBase *obj) const { - int t = obj->getDCI().getInt(name); - return formatTime(t); - } - wstring setData(oBase *obj, const wstring &input) const { - int t = convertAbsoluteTimeMS(input); - if (t == NOTIME) - t = 0; - obj->getDI().setInt(name.c_str(), t); - return formatData(obj); - } - int addTableColumn(Table *table, const string &description, int minWidth) const { - return table->addColumn(description, max(minWidth, 90), false, true); - } + 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 { @@ -122,7 +124,7 @@ public: } } - const wstring &formatData(const oBase *ob) const { + const wstring &formatData(const oBase *ob) const override { if (ob->getEvent()->getRevision() != rev) prepare(ob->getEvent()); int p = ob->getDCI().getInt("Paid"); @@ -138,38 +140,85 @@ public: } } - wstring setData(oBase *ob, const wstring &input) const { + 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); } - return formatData(ob); + output = formatData(ob); + return make_pair(0, false); } - int addTableColumn(Table *table, const string &description, int minWidth) const { + int addTableColumn(Table *table, const string &description, int minWidth) const override { return table->addColumn(description, max(minWidth, 90), true, true); } }; +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); + } +}; + oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) { readOnly = false; tLongTimesCached = -1; directSocket = 0; - hMod=0; ZeroTime=0; vacantId = 0; noClubId = 0; dataRevision = 0; - sqlCounterRunners=0; - sqlCounterClasses=0; - sqlCounterCourses=0; - sqlCounterControls=0; - sqlCounterClubs=0; - sqlCounterCards=0; - sqlCounterPunches=0; - sqlCounterTeams=0; - + disableRecalculate = false; initProperties(); @@ -292,7 +341,7 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oEventData->addVariableInt("BibGap", oDataContainer::oIS8U, "Nummerlappshopp"); oEventData->addVariableInt("LongTimes", oDataContainer::oIS8U, "Långa tider"); oEventData->addVariableString("PayModes", "Betalsätt"); - + oEventData->addVariableInt("TransferFlags", oDataContainer::oIS32, "Överföring"); oEventData->initData(this, dataSize); oClubData=new oDataContainer(oClub::dataSize); @@ -319,13 +368,11 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oClubData->addVariableEnum("Invoice", 1, "Faktura", eInvoice); oClubData->addVariableInt("InvoiceNo", oDataContainer::oIS16U, "Fakturanummer"); - static PayMethodFormatter paymentMethod; - oRunnerData=new oDataContainer(oRunner::dataSize); oRunnerData->addVariableCurrency("Fee", "Anm. avgift"); oRunnerData->addVariableCurrency("CardFee", "Brickhyra"); oRunnerData->addVariableCurrency("Paid", "Betalat"); - oRunnerData->addVariableInt("PayMode", oDataContainer::oIS8U, "Betalsätt", &paymentMethod); + oRunnerData->addVariableInt("PayMode", oDataContainer::oIS8U, "Betalsätt", make_shared()); oRunnerData->addVariableCurrency("Taxable", "Skattad avgift"); oRunnerData->addVariableInt("BirthYear", oDataContainer::oIS32, "Födelseår"); oRunnerData->addVariableString("Bib", 8, "Nummerlapp").zeroSortPadding = 5; @@ -333,8 +380,7 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) //oRunnerData->addVariableInt("VacRank", oDataContainer::oIS16U, "Vak. ranking"); oRunnerData->addVariableDate("EntryDate", "Anm. datum"); - static AbsoluteTimeFormatter atf("EntryTime"); - oRunnerData->addVariableInt("EntryTime", oDataContainer::oIS32, "Anm. tid", &atf); + oRunnerData->addVariableInt("EntryTime", oDataContainer::oIS32, "Anm. tid", make_shared("EntryTime")); vector< pair > sex; sex.push_back(make_pair(L"M", L"Man")); @@ -348,7 +394,7 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oRunnerData->addVariableInt("Priority", oDataContainer::oIS8U, "Prioritering"); oRunnerData->addVariableString("Phone", 20, "Telefon"); - oRunnerData->addVariableInt("RaceId", oDataContainer::oIS32, "Lopp-id", &oRunner::raceIdFormatter); + oRunnerData->addVariableInt("RaceId", oDataContainer::oIS32, "Lopp-id", make_shared()); oRunnerData->addVariableInt("TimeAdjust", oDataContainer::oIS16, "Tidsjustering"); oRunnerData->addVariableInt("PointAdjust", oDataContainer::oIS32, "Poängjustering"); @@ -356,8 +402,9 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oRunnerData->addVariableInt("Shorten", oDataContainer::oIS8U, "Avkortning"); oRunnerData->addVariableInt("EntrySource", oDataContainer::oIS32, "Källa"); oRunnerData->addVariableInt("Heat", oDataContainer::oIS8U, "Heat"); - oRunnerData->addVariableInt("Reference", oDataContainer::oIS32, "Referens"); - oRunnerData->addVariableInt("NoRestart", oDataContainer::oIS8U, "Ej omstart"); + oRunnerData->addVariableInt("Reference", oDataContainer::oIS32, "Referens", make_shared()); + oRunnerData->addVariableInt("NoRestart", oDataContainer::oIS8U, "Ej omstart", make_shared("NoRestart")); + oRunnerData->addVariableString("InputResult", "Tidigare resultat", make_shared()); oControlData=new oDataContainer(oControl::dataSize); oControlData->addVariableInt("TimeAdjust", oDataContainer::oIS32, "Tidsjustering"); @@ -374,15 +421,13 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oCourseData->addVariableInt("NumberMaps", oDataContainer::oIS16, "Kartor"); oCourseData->addVariableString("StartName", 16, "Start"); oCourseData->addVariableInt("Climb", oDataContainer::oIS16, "Stigning"); - oCourseData->addVariableInt("StartIndex", oDataContainer::oIS32, "Startindex"); - oCourseData->addVariableInt("FinishIndex", oDataContainer::oIS32, "Målindex"); oCourseData->addVariableInt("RPointLimit", oDataContainer::oIS32, "Poänggräns"); oCourseData->addVariableInt("RTimeLimit", oDataContainer::oIS32, "Tidsgräns"); oCourseData->addVariableInt("RReduction", oDataContainer::oIS32, "Poängreduktion"); oCourseData->addVariableInt("RReductionMethod", oDataContainer::oIS8U, "Reduktionsmetod"); - oCourseData->addVariableInt("FirstAsStart", oDataContainer::oIS8U, "Från första"); - oCourseData->addVariableInt("LastAsFinish", oDataContainer::oIS8U, "Till sista"); + oCourseData->addVariableInt("FirstAsStart", oDataContainer::oIS8U, "Från första", make_shared("FirstAsStart")); + oCourseData->addVariableInt("LastAsFinish", oDataContainer::oIS8U, "Till sista", make_shared("LastAsFinish")); oCourseData->addVariableInt("CControl", oDataContainer::oIS16U, "Varvningskontroll"); //Common control index oCourseData->addVariableInt("Shorten", oDataContainer::oIS32, "Avkortning"); @@ -392,8 +437,8 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oClassData->addVariableString("LongName", 32, "Långt namn"); oClassData->addVariableInt("LowAge", oDataContainer::oIS8U, "Undre ålder"); oClassData->addVariableInt("HighAge", oDataContainer::oIS8U, "Övre ålder"); - oClassData->addVariableInt("HasPool", oDataContainer::oIS8U, "Banpool"); - oClassData->addVariableInt("AllowQuickEntry", oDataContainer::oIS8U, "Direktanmälan"); + oClassData->addVariableInt("HasPool", oDataContainer::oIS8U, "Banpool", make_shared("HasPool")); + oClassData->addVariableInt("AllowQuickEntry", oDataContainer::oIS8U, "Direktanmälan", make_shared("AllowQuickEntry")); oClassData->addVariableString("ClassType", 40, "Klasstyp"); @@ -406,14 +451,12 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oClassData->addVariableEnum("Sex", 1, "Kön", sexClass); oClassData->addVariableString("StartName", 16, "Start"); oClassData->addVariableInt("StartBlock", oDataContainer::oIS8U, "Block"); - oClassData->addVariableInt("NoTiming", oDataContainer::oIS8U, "Ej tidtagning"); - oClassData->addVariableInt("FreeStart", oDataContainer::oIS8U, "Fri starttid"); - oClassData->addVariableInt("IgnoreStart", oDataContainer::oIS8U, "Ej startstämpling"); + oClassData->addVariableInt("NoTiming", oDataContainer::oIS8U, "Ej tidtagning", make_shared("NoTiming")); + oClassData->addVariableInt("FreeStart", oDataContainer::oIS8U, "Fri starttid", make_shared("FreeStart")); + oClassData->addVariableInt("IgnoreStart", oDataContainer::oIS8U, "Ej startstämpling", make_shared("IgnoreStart")); - firstStartDefiner = new RelativeTimeFormatter("FirstStart"); - oClassData->addVariableInt("FirstStart", oDataContainer::oIS32, "Första start", firstStartDefiner); - intervalDefiner = new AbsoluteTimeFormatter("StartInterval"); - oClassData->addVariableInt("StartInterval", oDataContainer::oIS16, "Intervall", intervalDefiner); + oClassData->addVariableInt("FirstStart", oDataContainer::oIS32, "Första start", make_shared("FirstStart")); + oClassData->addVariableInt("StartInterval", oDataContainer::oIS16, "Intervall", make_shared("StartInterval")); oClassData->addVariableInt("Vacant", oDataContainer::oIS8U, "Vakanser"); oClassData->addVariableInt("Reserved", oDataContainer::oIS16U, "Extraplatser"); @@ -425,13 +468,10 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oClassData->addVariableInt("SortIndex", oDataContainer::oIS32, "Sortering"); oClassData->addVariableInt("MaxTime", oDataContainer::oISTime, "Maxtid"); - vector< pair > statusClass; - statusClass.push_back(make_pair(L"", L"OK")); - statusClass.push_back(make_pair(L"IR", L"Struken med återbetalning")); - statusClass.push_back(make_pair(L"I", L"Struken utan återbetalning")); - + vector> statusClass; + oClass::fillClassStatus(statusClass); oClassData->addVariableEnum("Status", 2, "Status", statusClass); - oClassData->addVariableInt("DirectResult", oDataContainer::oIS8, "Resultat vid målstämpling"); + oClassData->addVariableInt("DirectResult", oDataContainer::oIS8, "Resultat vid målstämpling", make_shared("DirectResult")); oClassData->addVariableString("Bib", 8, "Nummerlapp"); vector< pair > bibMode; @@ -442,9 +482,11 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oClassData->addVariableEnum("BibMode", 1, "Nummerlappshantering", bibMode); oClassData->addVariableInt("Unordered", oDataContainer::oIS8U, "Oordnade parallella"); oClassData->addVariableInt("Heat", oDataContainer::oIS8U, "Heat"); - oClassData->addVariableInt("Locked", oDataContainer::oIS8U, "Låst gaffling"); + oClassData->addVariableInt("Locked", oDataContainer::oIS8U, "Låst gaffling", make_shared("Locked")); oClassData->addVariableString("Qualification", "Kvalschema"); oClassData->addVariableInt("NumberMaps", oDataContainer::oIS16, "Kartor"); + oClassData->addVariableString("Result", 24, "Result module", make_shared()); + oClassData->addVariableInt("TransferFlags", oDataContainer::oIS32, "Överföring", make_shared()); oTeamData = new oDataContainer(oTeam::dataSize); oTeamData->addVariableCurrency("Fee", "Anm. avgift"); @@ -452,7 +494,7 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oTeamData->addVariableInt("PayMode", oDataContainer::oIS8U, "Betalsätt"); oTeamData->addVariableCurrency("Taxable", "Skattad avgift"); oTeamData->addVariableDate("EntryDate", "Anm. datum"); - oTeamData->addVariableInt("EntryTime", oDataContainer::oIS32, "Anm. tid", &atf); + oTeamData->addVariableInt("EntryTime", oDataContainer::oIS32, "Anm. tid", make_shared("EntryTime")); oTeamData->addVariableString("Nationality", 3, "Nationalitet"); oTeamData->addVariableString("Country", 23, "Land"); oTeamData->addVariableString("Bib", 8, "Nummerlapp").zeroSortPadding = 5; @@ -465,9 +507,10 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oTeamData->addVariableInt("EntrySource", oDataContainer::oIS32, "Källa"); oTeamData->addVariableInt("Heat", oDataContainer::oIS8U, "Heat"); oTeamData->addVariableInt("NoRestart", oDataContainer::oIS8U, "Ej omstart"); + oTeamData->addVariableString("InputResult", "Tidigare resultat", make_shared()); - generalResults.push_back(GeneralResultCtr("atcontrol", L"Result at a control", new ResultAtControl())); - generalResults.push_back(GeneralResultCtr("totatcontrol", L"Total/team result at a control", new TotalResultAtControl())); + generalResults.push_back(GeneralResultCtr("atcontrol", L"Result at a control", make_shared())); + generalResults.push_back(GeneralResultCtr("totatcontrol", L"Total/team result at a control", make_shared())); currentClientCS = 0; memset(CurrentFile, 0, sizeof(CurrentFile)); @@ -482,12 +525,6 @@ oEvent::~oEvent() runnerDB = 0; meosFeatures = 0; - if (hMod) { - HasDBConnection=false; - FreeLibrary(hMod); - hMod=0; - } - delete oEventData; delete oRunnerData; delete oClubData; @@ -758,7 +795,7 @@ bool oEvent::writePunches(xmlparser &xml, ProgressWindow &pw) for (it=punches.begin(); it != punches.end(); ++it) { it->Write(xml); if (++k%300 == 200) - pw.setSubProgress( (1000*k)/ Runners.size()); + pw.setSubProgress( (1000*k)/ (punches.size())); } xml.endTag(); @@ -1295,14 +1332,14 @@ bool oEvent::open(const xmlparser &xml) { if (t.Id>0){ Teams.push_back(t); teamById[t.Id] = &Teams.back(); - Teams.back().apply(false, 0, true); + Teams.back().apply(ChangeType::Quiet, nullptr); } } } } for (oRunner &r : Runners) - r.apply(false, 0, true); + r.apply(ChangeType::Quiet, nullptr); toc("team"); @@ -1456,7 +1493,7 @@ pRunner oEvent::dbLookUpByCard(int cardNo) const RunnerWDBEntry *dbr = runnerDB->getRunnerByCard(cardNo); if (dbr != 0) { dbr->getName(sRunner.sName); - oRunner::getRealName(sRunner.sName, sRunner.tRealName); + sRunner.getRealName(sRunner.sName, sRunner.tRealName); sRunner.init(*dbr, false); sRunner.cardNumber = cardNo; return &sRunner; @@ -1598,7 +1635,7 @@ void oEvent::autoAddTeam(pRunner pr) pTeam t = addTeam(pr->getName(), pr->getClubId(), pc->getId()); if (pr->StartNo == 0) pr->StartNo = Teams.size(); - t->setStartNo(pr->StartNo, false); + t->setStartNo(pr->StartNo, ChangeType::Update); t->setRunner(0, pr, true); } } @@ -1652,7 +1689,7 @@ pRunner oEvent::addRunner(const wstring &name, int clubId, int classId, } oRunner r(this); r.sName = name; - oRunner::getRealName(r.sName, r.tRealName); + r.getRealName(r.sName, r.tRealName); r.Club = getClub(clubId); r.Class = getClass(classId); if (cardNo>0) @@ -1694,7 +1731,7 @@ pRunner oEvent::addRunnerFromDB(const pRunner db_r, { oRunner r(this); r.sName = db_r->sName; - oRunner::getRealName(r.sName, r.tRealName); + r.getRealName(r.sName, r.tRealName); r.cardNumber = db_r->cardNumber; if (db_r->Club) { @@ -1742,7 +1779,10 @@ pRunner oEvent::addRunner(const oRunner &r, bool updateStartNo) { if (cardToRunnerHash && r.getCardNo() != 0) { cardToRunnerHash->emplace(r.getCardNo(), pr); } - + if (classIdToRunnerHash && r.getClassId(false)) { + (*classIdToRunnerHash)[r.getClassId(true)].push_back(pr); + } + if (pr->StartNo == 0 && updateStartNo) { pr->StartNo = ++nextFreeStartNo; // Need not be unique } @@ -1774,7 +1814,7 @@ pRunner oEvent::addRunner(const oRunner &r, bool updateStartNo) { pRunner oEvent::addRunnerVacant(int classId) { pRunner r=addRunner(lang.tl(L"Vakant"), getVacantClub(false), classId, 0,0, true); if (r) { - r->apply(false, 0, false); + r->apply(ChangeType::Update, nullptr); r->synchronize(true); } return r; @@ -2074,232 +2114,12 @@ bool oEvent::sortRunners(SortOrder so, vector &runners) const { return true; } -bool oEvent::sortTeams(SortOrder so, int leg, bool linearLeg) { +bool oEvent::sortRunners(SortOrder so, vector &runners) const { reinitializeClasses(); - oTeamList::iterator it; - map classId2Linear; - if (so==ClassResult || so==ClassTotalResult || so == ClassTeamLegResult) { - bool totalResult = so == ClassTotalResult; - bool legResult = so == ClassTeamLegResult; - bool hasRunner = (leg == -1); - for (it=Teams.begin(); it != Teams.end(); ++it) { - int lg = leg; - int clsId = it->Class->getId(); - - if (leg>=0 && !linearLeg && it->Class) { - map::iterator res = classId2Linear.find(it->Class->getId()); - if (res == classId2Linear.end()) { - unsigned linLegBase = it->Class->getLegNumberLinear(leg, 0); - while (linLegBase + 1 < it->Class->getNumStages()) { - if (it->Class->isParallel(linLegBase+1) || it->Class->isOptional(linLegBase+1)) - linLegBase++; - else - break; - } - lg = linLegBase; - //lg = linLegBase + it->Class->getNumParallel(linLegBase) - 1; - classId2Linear[clsId] = lg; - } - else { - lg = res->second; - } - } - - const int lastIndex = it->Class ? it->Class->getLastStageIndex() : 0; - lg = min(lg, lastIndex); - - - if (lg >= leg) - hasRunner = true; - if (legResult) { - pRunner r = it->getRunner(lg); - if (r) { - it->_sortTime = r->getRunningTime(); - it->_cachedStatus = r->getStatus(); - } - else { - it->_sortTime = 0; - it->_cachedStatus = StatusUnknown; - } - } - else { - it->_sortTime=it->getLegRunningTime(lg, totalResult) + it->getNumShortening(lg)*3600*24*10; - it->_cachedStatus = it->getLegStatus(lg, totalResult); - - // Ensure number of restarts has effect on final result - if (lg == lastIndex) - it->_sortTime += it->tNumRestarts*24*3600; - } - unsigned rawStatus = it->_cachedStatus; - it->_sortStatus = RunnerStatusOrderMap[rawStatus < 100u ? rawStatus : 0]; - - } - - if (!hasRunner) - return false; - - Teams.sort(oTeam::compareResult); - } - else if (so==ClassStartTime || so == ClassStartTimeClub) { - if (leg == -1) - leg = 0; - for (it=Teams.begin(); it != Teams.end(); ++it) { - it->_cachedStatus = StatusUnknown; - it->_sortStatus=0; - it->_sortTime=it->getLegStartTime(leg); - if (it->_sortTime<=0) - it->_sortStatus=1; - } - if (so == ClassStartTime) - Teams.sort(oTeam::compareResult); - else - Teams.sort(oTeam::compareResultNoSno); - } - else if (so == ClassKnockoutTotalResult) { - for (it = Teams.begin(); it != Teams.end(); ++it) { - it->_cachedStatus = StatusUnknown; - it->_sortStatus = 0; - it->_sortTime = 0; - - // Count number of races with results - int numResult = 0; - int lastClassHeat = 0; - for (auto &r : it->Runners) { - if (r && (r->prelStatusOK() || - (r->tStatus != StatusUnknown && r->tStatus != StatusDNS && r->tStatus != StatusCANCEL))) { - - if (r->Class && r->tLeg > 0 && r->Class->isQualificationFinalBaseClass() && r->getClassRef(true) == r->Class) - continue; // Skip if not qualified. - - numResult++; - lastClassHeat = r->getDCI().getInt("Heat"); - it->_cachedStatus = r->tStatus; - it->_sortTime = r->getRunningTime(); - } - } - if (lastClassHeat > 50 || lastClassHeat < 0) - lastClassHeat = 0; - - unsigned rawStatus = it->_cachedStatus; - it->_sortStatus = RunnerStatusOrderMap[rawStatus < 100u ? rawStatus : 0] - (numResult * 100 + lastClassHeat) * 1000; - - } - Teams.sort(oTeam::compareResult); - } - return true; -} - -bool oEvent::sortTeams(SortOrder so, int leg, bool linearLeg, vector &teams) const { - reinitializeClasses(); - map classId2Linear; - if (so == ClassResult || so == ClassTotalResult || so == ClassTeamLegResult) { - bool totalResult = so == ClassTotalResult; - bool legResult = so == ClassTeamLegResult; - bool hasRunner = (leg == -1); - for (auto it : teams) { - int lg = leg; - int clsId = it->Class->getId(); - - if (leg >= 0 && !linearLeg && it->Class) { - map::iterator res = classId2Linear.find(it->Class->getId()); - if (res == classId2Linear.end()) { - unsigned linLegBase = it->Class->getLegNumberLinear(leg, 0); - while (linLegBase + 1 < it->Class->getNumStages()) { - if (it->Class->isParallel(linLegBase + 1) || it->Class->isOptional(linLegBase + 1)) - linLegBase++; - else - break; - } - lg = linLegBase; - //lg = linLegBase + it->Class->getNumParallel(linLegBase) - 1; - classId2Linear[clsId] = lg; - } - else { - lg = res->second; - } - } - - const int lastIndex = it->Class ? it->Class->getLastStageIndex() : 0; - lg = min(lg, lastIndex); - - - if (lg >= leg) - hasRunner = true; - if (legResult) { - pRunner r = it->getRunner(lg); - if (r) { - it->_sortTime = r->getRunningTime(); - it->_cachedStatus = r->getStatus(); - } - else { - it->_sortTime = 0; - it->_cachedStatus = StatusUnknown; - } - } - else { - it->_sortTime = it->getLegRunningTime(lg, totalResult) + it->getNumShortening(lg) * 3600 * 24 * 10; - it->_cachedStatus = it->getLegStatus(lg, totalResult); - - // Ensure number of restarts has effect on final result - if (lg == lastIndex) - it->_sortTime += it->tNumRestarts * 24 * 3600; - } - unsigned rawStatus = it->_cachedStatus; - it->_sortStatus = RunnerStatusOrderMap[rawStatus < 100u ? rawStatus : 0]; - - } - - if (!hasRunner) - return false; - - sort(teams.begin(), teams.end(), [](const oTeam * &a, const oTeam * &b)->bool {return oTeam::compareResult(*a, *b); }); - } - else if (so == ClassStartTime || so == ClassStartTimeClub) { - if (leg == -1) - leg = 0; - for (auto it : teams) { - it->_cachedStatus = StatusUnknown; - it->_sortStatus = 0; - it->_sortTime = it->getLegStartTime(leg); - if (it->_sortTime <= 0) - it->_sortStatus = 1; - } - if (so == ClassStartTime) - sort(teams.begin(), teams.end(), [](const oTeam * &a, const oTeam * &b)->bool {return oTeam::compareResult(*a, *b); }); - else - sort(teams.begin(), teams.end(), [](const oTeam * &a, const oTeam * &b)->bool {return oTeam::compareResultNoSno(*a, *b); }); - } - else if (so == ClassKnockoutTotalResult) { - for (auto it : teams) { - it->_cachedStatus = StatusUnknown; - it->_sortStatus = 0; - it->_sortTime = 0; - - // Count number of races with results - int numResult = 0; - int lastClassHeat = 0; - for (auto &r : it->Runners) { - if (r && (r->prelStatusOK() || - (r->tStatus != StatusUnknown && r->tStatus != StatusDNS && r->tStatus != StatusCANCEL))) { - - if (r->Class && r->tLeg > 0 && r->Class->isQualificationFinalBaseClass() && r->getClassRef(true) == r->Class) - continue; // Skip if not qualified. - - numResult++; - lastClassHeat = r->getDCI().getInt("Heat"); - it->_cachedStatus = r->tStatus; - it->_sortTime = r->getRunningTime(); - } - } - if (lastClassHeat > 50 || lastClassHeat < 0) - lastClassHeat = 0; - - unsigned rawStatus = it->_cachedStatus; - it->_sortStatus = RunnerStatusOrderMap[rawStatus < 100u ? rawStatus : 0] - (numResult * 100 + lastClassHeat) * 1000; - - } - sort(teams.begin(), teams.end(), [](const oTeam * &a, const oTeam * &b)->bool {return oTeam::compareResult(*a, *b); }); - } + auto oldSortOrder = CurrentSortOrder; + CurrentSortOrder = so; + sort(runners.begin(), runners.end(), [](pRunner &a, pRunner &b)->bool {return *a < *b; }); + CurrentSortOrder = oldSortOrder; return true; } @@ -2308,22 +2128,27 @@ wstring oEvent::getZeroTime() const return getAbsTime(0); } -void oEvent::setZeroTime(wstring m) +void oEvent::setZeroTime(wstring m, bool manualSet) { unsigned nZeroTime = convertAbsoluteTime(m); if (nZeroTime!=ZeroTime && nZeroTime != -1) { + if (manualSet) + setFlag(TransferFlags::FlagManualDateTime, true); + updateChanged(); ZeroTime=nZeroTime; } } -void oEvent::setName(const wstring &m) +void oEvent::setName(const wstring &m, bool manualSet) { wstring tn = trim(m); if (tn.empty()) throw meosException("Tomt namn är inte tillåtet."); if (tn != getName()) { + if (manualSet) + setFlag(TransferFlags::FlagManualName, true); Name = tn; updateChanged(); } @@ -2348,14 +2173,18 @@ wstring oEvent::getTitleName() const { return getName() + lang.tl(L" (lokalt)"); } -void oEvent::setDate(const wstring &m) +void oEvent::setDate(const wstring &m, bool manualSet) { if (m!=Date) { int d = convertDateYMS(m, true); if (d <= 0) throw meosException(L"Felaktigt datumformat 'X' (Använd ÅÅÅÅ-MM-DD).#" + m); - Date = formatDate(d, true); - updateChanged(); + wstring nDate = formatDate(d, true); + if (Date != nDate) { + if (manualSet) + setFlag(TransferFlags::FlagManualDateTime, true); + updateChanged(); + } } } @@ -2641,6 +2470,7 @@ int oEvent::getRelativeTime(const wstring &m) const { void oEvent::removeRunner(const vector &ids) { cardToRunnerHash.reset(); + classIdToRunnerHash.reset(); oRunnerList::iterator it; set toRemove; @@ -3719,10 +3549,7 @@ void oEvent::clear() destroyExtraWindows(); - while (!tables.empty()) { - tables.begin()->second->releaseOwnership(); - tables.erase(tables.begin()); - } + tables.clear(); Table::resetTableIds(); getRunnerDatabase().releaseTables(); @@ -3746,6 +3573,7 @@ void oEvent::clear() //Order of destruction is extreamly important... cardToRunnerHash.reset(); + classIdToRunnerHash.reset(); runnerById.clear(); bibStartNoToRunnerTeam.clear(); Runners.clear(); @@ -3772,24 +3600,15 @@ void oEvent::clear() currentNameId.clear(); wcscpy_s(CurrentFile, L""); - sqlCounterRunners=0; - sqlCounterClasses=0; - sqlCounterCourses=0; - sqlCounterControls=0; - sqlCounterClubs=0; - sqlCounterCards=0; - sqlCounterPunches=0; - sqlCounterTeams=0; - - sqlUpdateControls.clear(); - sqlUpdateCards.clear(); - sqlUpdateClubs.clear(); - sqlUpdateClasses.clear(); - sqlUpdateCourses.clear(); - sqlUpdateRunners.clear(); - sqlUpdatePunches.clear(); - sqlUpdateTeams.clear(); - + sqlRunners.reset(); + sqlClasses.reset(); + sqlCourses.reset(); + sqlControls.reset(); + sqlClubs.reset(); + sqlCards.reset(); + sqlPunches.reset(); + sqlTeams.reset(); + vacantId = 0; noClubId = 0; oEventData->initData(this, sizeof(oData)); @@ -3817,6 +3636,18 @@ void oEvent::clear() currentNameMode = (NameMode) getPropertyInt("NameMode", FirstLast); } +const shared_ptr
    &oEvent::getTable(const string &key) const { + if (tables.count(key)) { + tables.find(key)->second->update(); + return tables.find(key)->second; + } + throw meosException("Unknown table " + key); +} + +void oEvent::setTable(const string &key, const shared_ptr
    &table) { + tables[key] = table; +} + bool oEvent::deleteCompetition() { if (!empty() && !HasDBConnection) { @@ -3891,11 +3722,11 @@ void oEvent::newCompetition(const wstring &name) oe->updateTabs(); } -void oEvent::reEvaluateCourse(int CourseId, bool DoSync) +void oEvent::reEvaluateCourse(int CourseId, bool doSync) { oRunnerList::iterator it; - if (DoSync) + if (doSync) autoSynchronizeLists(false); vector mp; @@ -3906,7 +3737,7 @@ void oEvent::reEvaluateCourse(int CourseId, bool DoSync) } } - reEvaluateAll(classes, DoSync); + reEvaluateAll(classes, false); } void oEvent::reEvaluateAll(const set &cls, bool doSync) @@ -3926,15 +3757,8 @@ void oEvent::reEvaluateAll(const set &cls, bool doSync) if (!cls.empty() && cls.count(tit->getClassId(false)) == 0) continue; - tit->resetTmpStore(); - int nr = tit->getNumRunners(); - for (int k = 0; k < nr; k++) { - if (tit->Runners[k]) - tit->Runners[k]->resetTmpStore(); - } - if (!tit->isRemoved()) { - tit->apply(false, 0, true); + tit->apply(ChangeType::Quiet, nullptr); } } oRunnerList::iterator it; @@ -3944,8 +3768,7 @@ void oEvent::reEvaluateAll(const set &cls, bool doSync) continue; if (!it->tInTeam || it->Class != it->tInTeam->Class || (it->Class && it->Class->isQualificationFinalBaseClass())) { - it->resetTmpStore(); - it->apply(false, 0, true); + it->apply(ChangeType::Quiet, nullptr); } } @@ -3960,7 +3783,7 @@ void oEvent::reEvaluateAll(const set &cls, bool doSync) if (!it->isRemoved()) { if (it->tLeg == leg) { - it->evaluateCard(false, mp); // Must not sync! + it->evaluateCard(false, mp, 0, ChangeType::Quiet); // Must not sync! it->storeTimes(); } else if (it->tLeg>leg) @@ -3976,10 +3799,7 @@ void oEvent::reEvaluateAll(const set &cls, bool doSync) if (!cls.empty() && cls.count(tit->getClassId(true)) == 0) continue; - tit->apply(false, 0, true); - tit->setTmpStore(); - if (doSync) - tit->synchronize(true); + tit->apply(ChangeType::Quiet, nullptr); } } for (it=Runners.begin(); it != Runners.end(); ++it) { @@ -3988,8 +3808,7 @@ void oEvent::reEvaluateAll(const set &cls, bool doSync) continue; if (!it->tInTeam || it->Class != it->tInTeam->Class || (it->Class && (it->Class->isQualificationFinalBaseClass()))) - it->apply(false, 0, true); - it->setTmpStore(); + it->apply(ChangeType::Quiet, nullptr); it->storeTimes(); it->clearOnChangedRunningTime(); } @@ -3999,17 +3818,17 @@ void oEvent::reEvaluateAll(const set &cls, bool doSync) void oEvent::reEvaluateChanged() { - if (sqlChangedClasses || sqlChangedCourses || sqlChangedControls) { + if (sqlClasses.changed || sqlCourses.changed || sqlControls.changed) { reEvaluateAll(set(), false); globalModification = true; return; } - if (sqlChangedClubs) + if (sqlClubs.changed) globalModification = true; - if (!sqlChangedCards && !sqlChangedRunners && !sqlChangedTeams) + if (!sqlCards.changed && !sqlRunners.changed && !sqlTeams.changed) return; // Nothing to do map resetClasses; @@ -4029,14 +3848,8 @@ void oEvent::reEvaluateChanged() continue; addedTeams.insert(tit->getId()); - tit->resetTmpStore(); - - int nr = tit->getNumRunners(); - for (int k = 0; k < nr; k++) { - if (tit->Runners[k]) - tit->Runners[k]->resetTmpStore(); - } - tit->apply(false, 0, true); + + tit->apply(ChangeType::Quiet, nullptr); } oRunnerList::iterator it; @@ -4056,13 +3869,7 @@ void oEvent::reEvaluateChanged() pTeam t = it->tInTeam; if (t && !addedTeams.count(t->getId())) { addedTeams.insert(t->getId()); - t->resetTmpStore(); - int nr = t->getNumRunners(); - for (int k = 0; k < nr; k++) { - if (t->Runners[k]) - t->Runners[k]->resetTmpStore(); - } - t->apply(false, 0, true); + t->apply(ChangeType::Quiet, nullptr); } } } @@ -4082,8 +3889,7 @@ void oEvent::reEvaluateChanged() legRunners[leg].push_back(r); if (!r->tInTeam) { - r->resetTmpStore(); - r->apply(false, 0, true); + r->apply(ChangeType::Quiet, nullptr); } } else { @@ -4099,14 +3905,13 @@ void oEvent::reEvaluateChanged() for (size_t leg = 0; leg < legRunners.size(); leg++) { const vector &lr = legRunners[leg]; for (size_t k = 0; k < lr.size(); k++) { - lr[k]->evaluateCard(false, mp); // Must not sync! + lr[k]->evaluateCard(false, mp, 0, ChangeType::Quiet); // Must not sync! } } for(oTeamList::iterator tit=Teams.begin();tit!=Teams.end();++tit) { if (addedTeams.count(tit->getId())) { - tit->apply(false, 0, true); - tit->setTmpStore(); + tit->apply(ChangeType::Quiet, nullptr); } } @@ -4114,8 +3919,7 @@ void oEvent::reEvaluateChanged() const vector &lr = legRunners[leg]; for (size_t k = 0; k < lr.size(); k++) { if (!lr[k]->tInTeam) - lr[k]->apply(false, 0, true); - lr[k]->setTmpStore(); + lr[k]->apply(ChangeType::Quiet, nullptr); lr[k]->clearOnChangedRunningTime(); } } @@ -4438,7 +4242,7 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) { wchar_t bib[32]; swprintf_s(bib, pattern, num); pClass pc = it->getClassRef(true); - it->setBib(bib, num, pc ? !pc->lockedForking() : true, false); + it->setBib(bib, num, pc ? !pc->lockedForking() : true); num++; it->synchronize(); } @@ -4456,28 +4260,24 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) { } } else { - oTeamList::iterator it; - for (it=Teams.begin(); it != Teams.end(); ++it) - it->apply(false, 0, false); map teamStartNo; if (!firstNumber.empty()) { // Clear out start number temporarily, to not use it for sorting - for (it = Teams.begin(); it != Teams.end(); ++it) { + for (auto it = Teams.begin(); it != Teams.end(); ++it) { if (it->isRemoved()) continue; if (ClassId == 0 || it->getClassId(false) == ClassId) { if (it->getClassRef(false) && it->getClassRef(false)->getBibMode() != BibFree) { for (size_t i = 0; i < it->Runners.size(); i++) { if (it->Runners[i]) { - //runnerStartNo[it->Runners[i]->getId()] = it->Runners[i]->getStartNo(); - it->Runners[i]->setStartNo(0, false); - it->Runners[i]->setBib(L"", 0, false, false); + it->Runners[i]->setStartNo(0, ChangeType::Update); + it->Runners[i]->setBib(L"", 0, false); } } } teamStartNo[it->getId()] = it->getStartNo(); - it->setStartNo(0, false); + it->setStartNo(0, ChangeType::Update); } } } @@ -4488,7 +4288,7 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) { wchar_t pattern[32]; int num = oClass::extractBibPattern(firstNumber, pattern); - for (it=Teams.begin(); it != Teams.end(); ++it) { + for (auto it=Teams.begin(); it != Teams.end(); ++it) { if (it->isRemoved()) continue; @@ -4497,22 +4297,24 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) { swprintf_s(bib, pattern, num); bool lockedStartNo = it->Class && it->Class->lockedForking(); if (lockedStartNo) { - it->setBib(bib, num, false, false); - it->setStartNo(teamStartNo[it->getId()], false); + it->setBib(bib, num, false); + it->setStartNo(teamStartNo[it->getId()], ChangeType::Update); } else { - it->setBib(bib, num, true, false); + it->setBib(bib, num, true); } num++; - it->apply(true, 0, false); + it->applyBibs(); + it->evaluate(ChangeType::Update); } } } else { - for(it=Teams.begin(); it != Teams.end(); ++it){ - if (ClassId==0 || it->getClassId(false)==ClassId) { + for (auto it = Teams.begin(); it != Teams.end(); ++it) { + if (ClassId == 0 || it->getClassId(false) == ClassId) { it->getDI().setString("Bib", L""); //Update only bib - it->apply(true, 0, false); + it->applyBibs(); + it->evaluate(ChangeType::Update); } } } @@ -4532,12 +4334,6 @@ void oEvent::addAutoBib() { int number = 0; - - for (oTeamList::iterator tit=Teams.begin(); tit != Teams.end(); ++tit) { - if (!tit->skip()) - tit->apply(false, 0, false); - } - map teamStartNo; // Clear out start number temporarily, to not use it for sorting for (oTeamList::iterator tit = Teams.begin(); tit != Teams.end(); ++tit) { @@ -4560,15 +4356,15 @@ void oEvent::addAutoBib() { bool addBib = bibInfo != L"-"; if (addBib && teamAssign) - tit->setStartNo(0, false); + tit->setStartNo(0, ChangeType::Update); if (tit->getClassRef(false) && tit->getClassRef(false)->getBibMode() != BibFree) { for (size_t i = 0; i < tit->Runners.size(); i++) { if (tit->Runners[i]) { if (addBib && teamAssign) - tit->Runners[i]->setStartNo(0, false); + tit->Runners[i]->setStartNo(0, ChangeType::Update); if (!freeMode) - tit->Runners[i]->setBib(L"", 0, false, false); + tit->Runners[i]->setBib(L"", 0, false); } } } @@ -4647,7 +4443,8 @@ void oEvent::addAutoBib() { // Remove bib for (size_t k = 0; k < tl.size(); k++) { tl[k]->getDI().setString("Bib", L""); //Update only bib - tl[k]->apply(true, 0, false); + tl[k]->applyBibs(); + tl[k]->evaluate(ChangeType::Update); } } else { @@ -4657,16 +4454,18 @@ void oEvent::addAutoBib() { swprintf_s(buff, pattern, number); if (lockedForking) { - tl[k]->setBib(buff, number, false, false); - tl[k]->setStartNo(teamStartNo[tl[k]->getId()], false); + tl[k]->setBib(buff, number, false); + tl[k]->setStartNo(teamStartNo[tl[k]->getId()], ChangeType::Update); } else { - tl[k]->setBib(buff, number, true, false); + tl[k]->setBib(buff, number, true); } number += interval; - tl[k]->apply(true, 0, false); + tl[k]->applyBibs(); + tl[k]->evaluate(ChangeType::Update); } } + continue; } else { @@ -4685,13 +4484,13 @@ void oEvent::addAutoBib() { if (pattern[0]) { wchar_t buff[32]; swprintf_s(buff, pattern, number); - rl[k]->setBib(buff, number, !locked, false); + rl[k]->setBib(buff, number, !locked); number += interval; } else { rl[k]->getDI().setString("Bib", L""); //Update only bib } - rl[k]->synchronize(); + rl[k]->synchronize(true); } } } @@ -4699,7 +4498,7 @@ void oEvent::addAutoBib() { void oEvent::checkOrderIdMultipleCourses(int ClassId) { sortRunners(ClassStartTime); - int order=1; + int order = 1; oRunnerList::iterator it; //Find first free order @@ -4725,7 +4524,8 @@ void oEvent::checkOrderIdMultipleCourses(int ClassId) { it->updateStartNo(++order); } else { - it->setStartNo(it->getTeam()->getStartNo(), false); + it->setStartNo(it->getTeam()->getStartNo(), ChangeType::Update); + it->synchronize(true); } } else { @@ -4738,12 +4538,11 @@ void oEvent::checkOrderIdMultipleCourses(int ClassId) { void oEvent::fillStatus(gdioutput &gdi, const string& id) { vector< pair > d; - oe->fillStatus(d); + fillStatus(d); gdi.addItem(id, d); } -const vector< pair > &oEvent::fillStatus(vector< pair > &out) -{ +const vector< pair > &oEvent::fillStatus(vector< pair > &out) { out.clear(); out.push_back(make_pair(lang.tl(L"-"), StatusUnknown)); out.push_back(make_pair(lang.tl(L"Godkänd"), StatusOK)); @@ -4753,6 +4552,8 @@ const vector< pair > &oEvent::fillStatus(vector< pair 0) { oe->newCompetition(L"!TESTTÄVLING"); - oe->setZeroTime(L"05:00:00"); + oe->setZeroTime(L"05:00:00", true); oe->getMeOSFeatures().useAll(*oe); } vector gname; @@ -5364,9 +5174,9 @@ void oEvent::generateTestCompetition(int nClasses, int nRunners, pRunner r=addRunner(gname[rand()%gname.size()]+L" "+fname[rand()%fname.size()], rand()%nClubs+1, cls->getId(), 0, 0, true); - r->setStartNo(startno++, false); + r->setStartNo(startno++, ChangeType::Update); r->setCardNo(500001+Runners.size()*97+rand()%97, false); - r->apply(false, 0, false); + r->apply(ChangeType::Update, nullptr); } nRunners-=nRInClass; if (k%5!=5) { @@ -5381,7 +5191,7 @@ void oEvent::generateTestCompetition(int nClasses, int nRunners, int dr=cls->getNumDistinctRunners(); for (int i=0;igetId()); - t->setStartNo(startno++, false); + t->setStartNo(startno++, ChangeType::Update); for (int j=0;jId); r.sName = it->sName; - oRunner::getRealName(r.sName, r.tRealName); + r.getRealName(r.sName, r.tRealName); r.StartNo = it->StartNo; r.cardNumber = it->cardNumber; r.Club = ce.getClub(it->getClubId()); @@ -6119,7 +5937,7 @@ wstring oEvent::cloneCompetition(bool cloneRunners, bool cloneTimes, pt->Runners[k] = ce.getRunner(id, 0); } - t.apply(false, 0, false); + t.apply(ChangeType::Update, nullptr); } for (oRunnerList::iterator it = ce.Runners.begin(); it != ce.Runners.end(); ++it) { @@ -6153,7 +5971,7 @@ wstring oEvent::cloneCompetition(bool cloneRunners, bool cloneTimes, // Transfer lists and list configurations. if (listContainer) { - loadGeneralResults(false); + loadGeneralResults(false, false); swap(ce.generalResults, generalResults); try { ce.listContainer = new MetaListContainer(&ce, *listContainer); @@ -6170,7 +5988,7 @@ wstring oEvent::cloneCompetition(bool cloneRunners, bool cloneTimes, } void oEvent::transferListsAndSave(const oEvent &src) { - src.loadGeneralResults(false); + src.loadGeneralResults(false, false); swap(src.generalResults, generalResults); try { src.getListContainer().synchronizeTo(getListContainer()); @@ -6252,13 +6070,13 @@ bool checkTargetClass(pRunner target, pRunner source, int oldClass = target->getClassId(false); pRunner tgt = targetVacant[posToUse]; target->cloneStartTime(tgt); - target->setBib(tgt->getBib(), 0, false, false); - target->setStartNo(tgt->getStartNo(), false); + target->setBib(tgt->getBib(), 0, false); + target->setStartNo(tgt->getStartNo(), oBase::ChangeType::Update); target->setClassId(tgt->getClassId(false), false); - tgt->setStartTime(oldStart, true, false); - tgt->setBib(oldBib, 0, false, false); - tgt->setStartNo(oldSN, false); + tgt->setStartTime(oldStart, true, oBase::ChangeType::Update); + tgt->setBib(oldBib, 0, false); + tgt->setStartNo(oldSN, oBase::ChangeType::Update); tgt->setClassId(oldClass, false); return true; // Changed to correct class } @@ -6504,7 +6322,7 @@ void oEvent::transferResult(oEvent &ce, 0, src->getBirthYear(), true); dst->cloneData(src); dst->setInputData(*src); - dst->setStatus(StatusNotCompetiting, true, false); + dst->setStatus(StatusNotCompetiting, true, ChangeType::Update); notTransfered.push_back(dst); } else @@ -6547,7 +6365,7 @@ void oEvent::transferResult(oEvent &ce, } } - calculateTeamResults(true); + calculateTeamResults(set(), ResultType::TotalResult); // Lookup by id for (size_t k = 0; k < targetTeams.size(); k++) { pTeam it = targetTeams[k]; @@ -6782,7 +6600,7 @@ void oEvent::setStageNumber(int num) { oDataContainer &oEvent::getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const { data = (pvoid)oData; olddata = (pvoid)oDataOld; - strData = (pvectorstr)&dynamicData; + strData = const_cast(&dynamicData); return *oEventData; } @@ -6993,7 +6811,7 @@ void oEvent::updateStartTimes(int delta) { if (st > 0) { checkValid(*oe, st, delta, r.getName()); if (pass == 1) { - r.setStartTime(st, true, false, false); + r.setStartTime(st, true, ChangeType::Update, false); r.synchronize(true); } } @@ -7039,7 +6857,7 @@ void oEvent::updateStartTimes(int delta) { if (st > 0) { checkValid(*oe, st, delta, t.getName()); if (pass == 1) { - t.setStartTime(st, true, false, false); + t.setStartTime(st, true, ChangeType::Update, false); t.synchronize(true); } } @@ -7068,3 +6886,13 @@ void oEvent::updateStartTimes(int delta) { } } } + +bool oEvent::hasFlag(TransferFlags flag) const { + return (getDCI().getInt("TransferFlags") & flag) != 0; +} + +void oEvent::setFlag(TransferFlags flag, bool onoff) { + int cf = getDCI().getInt("TransferFlags"); + cf = onoff ? (cf | flag) : (cf & (~flag)); + getDI().setInt("TransferFlags", cf); +} diff --git a/code/oEvent.h b/code/oEvent.h index fe18fbf..8c90910 100644 --- a/code/oEvent.h +++ b/code/oEvent.h @@ -6,7 +6,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -78,25 +78,19 @@ struct oCounter { void operator++() {level1++, level2++, level3++;} }; - -struct GeneralResultCtr { - wstring name; - string tag; - wstring fileSource; - - bool isDynamic() const; - - mutable GeneralResult *ptr; - - GeneralResultCtr(const char *tag, const wstring &name, GeneralResult *ptr); - GeneralResultCtr(wstring &file, DynamicResult *ptr); - GeneralResultCtr() : ptr(0) {} - - ~GeneralResultCtr(); - GeneralResultCtr(const GeneralResultCtr &ctr); - void operator=(const GeneralResultCtr &ctr); +struct SqlUpdated { + string updated; + int counter = 0; + bool changed = false; + void reset() { + updated.clear(); + changed = false; + counter = 0; + } }; +struct GeneralResultCtr; + class oTimeLine { public: enum TimeLineType {TLTStart, TLTFinish, TLTRadio, TLTExpected}; @@ -217,9 +211,6 @@ enum PropertyType { class oEvent : public oBase { -private: - oDataDefiner *firstStartDefiner; - oDataDefiner *intervalDefiner; protected: // Revision number for data modified on this client. @@ -229,8 +220,7 @@ protected: bool globalModification; gdioutput &gdibase; - HMODULE hMod;//meosdb.dll - + void generateFixedList(gdioutput &gdi, const oListInfo &li); void startReconnectDaemon(); @@ -297,6 +287,16 @@ protected: oDataContainer *oRunnerData; oDataContainer *oTeamData; + SqlUpdated sqlRunners; + SqlUpdated sqlClasses; + SqlUpdated sqlCourses; + SqlUpdated sqlControls; + SqlUpdated sqlClubs; + SqlUpdated sqlCards; + SqlUpdated sqlPunches; + SqlUpdated sqlTeams; + + /* string sqlUpdateRunners; string sqlUpdateClasses; string sqlUpdateCourses; @@ -314,7 +314,7 @@ protected: int sqlCounterCards; int sqlCounterPunches; int sqlCounterTeams; - + bool sqlChangedRunners; bool sqlChangedClasses; bool sqlChangedCourses; @@ -323,7 +323,7 @@ protected: bool sqlChangedCards; bool sqlChangedPunches; bool sqlChangedTeams; - + */ bool needReEvaluate(); @@ -374,9 +374,7 @@ protected: bool HasDBConnection; bool HasPendingDBConnection; bool msSynchronize(oBase *ob); - void resetChangeStatus(bool onlyChangable=true); - void storeChangeStatus(bool onlyChangable=true); - + wstring clientName; vector connectedClients; DWORD clientCheckSum() const; //Calculate a check sum for current clients @@ -394,7 +392,7 @@ protected: int getDISize() const {return dataSize;} BYTE oData[dataSize]; BYTE oDataOld[dataSize]; - vector< vector > dynamicData; + vector> dynamicData; /** Get internal data buffers for DI */ oDataContainer &getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const; @@ -446,7 +444,7 @@ protected: int nextTimeLineEvent; // Time when next known event will occur. // Tables - map tables; + map> tables; // Internal list method void generateListInternal(gdioutput &gdi, const oListInfo &li, bool formatHead); @@ -474,7 +472,16 @@ protected: // Temporarily disable recaluclate leader times bool disableRecalculate; public: - + + enum TransferFlags { + FlagManualName = 1, + FlagManualDateTime = 2, + FlagManualFees = 4, + }; + + bool hasFlag(TransferFlags flag) const; + void setFlag(TransferFlags flag, bool state); + int getVacantClub(bool returnNoClubClub); // Create vacant club if it does not exist int getVacantClubIfExist(bool returnNoClubClub) const; @@ -510,6 +517,15 @@ private: public: + enum class ResultType { + ClassResult, + ClassResultDefault, // Class Result, but ignore any class specified result module + TotalResult, + TotalResultDefault, // Total Result, but ignore any class specified result module + CourseResult, ClassCourseResult, + PreliminarySplitResults + }; + // Returns true if there is a class with restart time bool hasAnyRestartTime() const; @@ -549,9 +565,13 @@ public: //Get list of runners in a class void getRunners(int classId, int courseId, vector &r, bool sortRunners = true); - - void getTeams(int classId, vector &t, bool sortTeams = true); + void getRunners(const set &classId, vector &r, bool synchRunners); + void getRunners(const set &classId, vector &r) const; + void getTeams(int classId, vector &t, bool sortTeams = true); + void getTeams(const set &classId, vector &t, bool synchTeams); + void getTeams(const set &classId, vector &t) const; + bool hasRank() const; bool hasBib(bool runnerBib, bool teamBib) const; bool hasTeam() const; @@ -804,6 +824,13 @@ public: /** Compute results for split times while runners are on course.*/ void computePreliminarySplitResults(const set &classes) const; + + void calculateRunnerResults(ResultType resultType, + const set &rgClasses, + vector &runners, + bool useComputedResult, + bool includePreliminary) const; + /** Synchronizes to server and checks if there are hired card data*/ bool hasHiredCardData(); bool isHiredCard(int cardNo) const; @@ -822,6 +849,8 @@ protected: mutable shared_ptr> cardToRunnerHash; vector getCardToRunner(int cardNo) const; + mutable shared_ptr>> classIdToRunnerHash; + mutable set hiredCardHash; mutable int tHiredCardHashDataRevision = -1; @@ -833,6 +862,9 @@ protected: mutable map > cachedFirstStart; //First start per classid. map, oFreePunch> advanceInformationPunches; + bool calculateTeamResults(vector &teams, int leg, ResultType resultType); + void calculateModuleTeamResults(const set &cls, vector &teams); + public: void updateStartTimes(int delta); @@ -990,19 +1022,19 @@ public: const wstring &getName() const; wstring getTitleName() const; - void setName(const wstring &m); + void setName(const wstring &m, bool manualSet); const wstring &getAnnotation() const {return Annotation;} void setAnnotation(const wstring &m); const wstring &getDate() const {return Date;} - void setDate(const wstring &m); + void setDate(const wstring &m, bool manualSet); int getZeroTimeNum() const {return ZeroTime;} wstring getZeroTime() const; - void setZeroTime(wstring m); + void setZeroTime(wstring m, bool manualSet); /** Get the automatic bib gap between classes. */ int getBibClassGap() const; @@ -1012,26 +1044,26 @@ public: bool openRunnerDatabase(const wchar_t *file); bool saveRunnerDatabase(const wchar_t *file, bool onlyLocal); - - enum class ResultType {ClassResult, TotalResult, CourseResult, - ClassCourseResult, PreliminarySplitResults}; + void calculateResults(const set &classes, ResultType result, bool includePreliminary = false) const; - void calculateRogainingResults(const set &classSelection) const; - + void calculateResults(list &rl); - void calculateTeamResults(bool totalMultiday); - bool calculateTeamResults(int leg, bool totalMultiday); + void calculateTeamResults(const set &cls, ResultType resultType); + void calculateTeamResults(const vector &teams, ResultType resultType); + // Set results for specified classes to tempResult void calculateTeamResultAtControl(const set &classId, int leg, int controlId, bool totalResults); bool sortRunners(SortOrder so); + bool sortRunners(SortOrder so, vector &runners) const; bool sortRunners(SortOrder so, vector &runners) const; /** If linear leg is true, leg is interpreted as actual leg numer, otherwise w.r.t to parallel legs. */ bool sortTeams(SortOrder so, int leg, bool linearLeg); bool sortTeams(SortOrder so, int leg, bool linearLeg, vector &teams) const; + bool sortTeams(SortOrder so, int leg, bool linearLeg, vector &teams) const; pCard allocateCard(pRunner owner); @@ -1055,11 +1087,11 @@ public: pTeam getTeam(int Id) const; pTeam getTeamByName(const wstring &pname) const; const vector< pair > &fillTeams(vector< pair > &out, int classId=0); - const vector< pair > &fillStatus(vector< pair > &out); + static const vector< pair > &fillStatus(vector< pair > &out); const vector< pair > &fillControlStatus(vector< pair > &out) const; void fillTeams(gdioutput &gdi, const string &id, int ClassId=0); - void fillStatus(gdioutput &gdi, const string &id); + static void fillStatus(gdioutput &gdi, const string &id); void fillControlStatus(gdioutput &gdi, const string &id) const; @@ -1119,15 +1151,9 @@ public: const unordered_set &personFilter); void fillRunners(gdioutput &gdi, const string &id, bool longName = false, int filter = 0); - Table *getRunnersTB();//Table mode - Table *getClubsTB(); - Table *getPunchesTB(); - Table *getClassTB(); - Table *getControlTB(); - Table *getCardsTB(); - Table *getTeamsTB(); - Table *getCoursesTB(); - + const shared_ptr
    &getTable(const string &key) const; + void setTable(const string &key, const shared_ptr
    &table); + bool hasTable(const string &key) const { return tables.count(key) > 0; } void generateTableData(const string &tname, Table &table, TableUpdateInfo &ui); @@ -1275,9 +1301,11 @@ protected: public: - GeneralResult &getGeneralResult(const string &tag, wstring &sourceFileOut) const; - void getGeneralResults(bool onlyEditable, vector< pair > > &tagNameList, bool includeDateInName) const; - void loadGeneralResults(bool forceReload) const; + const shared_ptr &getGeneralResult(const string &tag, wstring &sourceFileOut) const; + void getGeneralResults(bool onlyEditable, vector>> &tagNameList, bool includeDateInName) const; + void loadGeneralResults(bool forceReload, bool loadFromDisc) const; + // Set or clear temporary list context + void setGeneralResultContext(const oListParam *ctx); void getPredefinedClassTypes(map &types) const; diff --git a/code/oEventDraw.cpp b/code/oEventDraw.cpp index 94fdb72..1a9b914 100644 --- a/code/oEventDraw.cpp +++ b/code/oEventDraw.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -1137,7 +1137,7 @@ void oEvent::drawList(const vector &spec, int minStartNo = Runners.size(); vector> newStartNo; for(unsigned k=0;ksetStartTime(stimes[k], true, false, false); + runners[k]->setStartTime(stimes[k], true, ChangeType::Update, false); runners[k]->synchronize(); minStartNo = min(minStartNo, runners[k]->getStartNo()); newStartNo.emplace_back(stimes[k], k); @@ -1313,7 +1313,7 @@ void oEvent::drawListClumped(int ClassID, int FirstStart, int Interval, int Vaca for (it = Runners.begin(); it != Runners.end(); ++it) { if (it->Class && it->Class->Id == ClassID) { - it->setStartTime(stimes[k++], true, false, false); + it->setStartTime(stimes[k++], true, oBase::ChangeType::Update, false); it->StartNo = k; it->synchronize(); } @@ -1627,15 +1627,15 @@ void oEvent::drawPersuitList(int classId, int firstTime, int restartTime, if ((times[k].first - delta) < maxTime && breakIndex == -1) { if (!reverse) - r->setStartTime(firstTime + times[k].first - delta, true, false, false); + r->setStartTime(firstTime + times[k].first - delta, true, ChangeType::Update, false); else - r->setStartTime(firstTime - times[k].first + reverseDelta, true, false, false); + r->setStartTime(firstTime - times[k].first + reverseDelta, true, ChangeType::Update, false); } else if (!reverse) { if (breakIndex == -1) breakIndex = k; - r->setStartTime(restartTime + ((k - breakIndex)/pairSize) * interval, true, false, false); + r->setStartTime(restartTime + ((k - breakIndex)/pairSize) * interval, true, ChangeType::Update, false); } else { if (breakIndex == -1) { @@ -1643,7 +1643,7 @@ void oEvent::drawPersuitList(int classId, int firstTime, int restartTime, odd = times.size() % 2; } - r->setStartTime(restartTime + ((breakIndex - k + odd)/pairSize) * interval, true, false, false); + r->setStartTime(restartTime + ((breakIndex - k + odd)/pairSize) * interval, true, ChangeType::Update, false); } r->synchronize(true); } diff --git a/code/oEventDraw.h b/code/oEventDraw.h index 54c4d00..3b84136 100644 --- a/code/oEventDraw.h +++ b/code/oEventDraw.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/oEventResult.cpp b/code/oEventResult.cpp index a71c684..6a9dd6a 100644 --- a/code/oEventResult.cpp +++ b/code/oEventResult.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -40,6 +40,60 @@ #include "TabList.h" #include "listeditor.h" +template struct ResultCalcData { + int groupId; + int score; + bool operator<(const ResultCalcData &c) const { + if (groupId != c.groupId) + return groupId < c.groupId; + else + return score < c.score; + } + T *dst; + + ResultCalcData() {} + ResultCalcData(int g, int s, T* d) : groupId(g), score(s), dst(d) {} +}; + +template void calculatePlace(vector> &data, Apply apply) { + int groupId = -1; + int cPlace = 0, vPlace = 0, cScore = 0; + bool invalidClass = false; + bool useResults = true; + sort(data.begin(), data.end()); + for (auto &it : data) { + // Start new "class" + if (groupId != it.groupId) { + groupId = it.groupId; + cPlace = 0; + vPlace = 0; + cScore = 0; + pClass cls = it.dst->getClassRef(true); + useResults = cls ? cls->getNoTiming() == false : true; + invalidClass = cls ? cls->getClassStatus() != oClass::Normal : false; + } + + if (invalidClass) { + apply(it, 0); + } + else { + int tPlace = 0; + if (it.score > 0) { + cPlace++; + if (it.score > cScore) + vPlace = cPlace; + + cScore = it.score; + if (useResults) + tPlace = vPlace; + } + else + tPlace = 0; + + apply(it, tPlace); + } + } +} void oEvent::calculateSplitResults(int controlIdFrom, int controlIdTo) { oRunnerList::iterator it; @@ -100,275 +154,547 @@ void oEvent::calculateSplitResults(int controlIdFrom, int controlIdTo) { it->tPlace.update(*this, vPlace); // XXX User other result container } else - it->tPlace.update(*this, 99000+it->tStatus); + it->tPlace.update(*this, 0); } } void oEvent::calculateResults(const set &classes, ResultType resultType, bool includePreliminary) const { + static bool resultCalculationLock = false; + + if (resultCalculationLock) { + assert(resultType == ResultType::ClassResultDefault || resultType == ResultType::TotalResultDefault); + return; + } + if (resultType == ResultType::PreliminarySplitResults) { computePreliminarySplitResults(classes); return; } - const bool totalResults = resultType == ResultType::TotalResult; + + const bool defaultResult = resultType == ResultType::ClassResultDefault || resultType == ResultType::TotalResultDefault; + const bool standardResults = resultType == ResultType::ClassResult || resultType == ResultType::TotalResult; + + const bool individualResults = resultType == ResultType::ClassResult; + const bool totalResults = resultType == ResultType::TotalResult || resultType == ResultType::TotalResultDefault; const bool courseResults = resultType == ResultType::CourseResult; const bool classCourseResults = resultType == ResultType::ClassCourseResult; - - bool all = classes.empty(); - vector runners; - runners.reserve(Runners.size()); - for (auto &r : Runners) { - if (r.isRemoved()) - continue; + const bool specialResult = classCourseResults || courseResults; - if (!all && !classes.count(r.getClassId(true))) - continue; - runners.push_back(&r); + bool all = classes.empty() || specialResult; + vector>> runnersByResultModule; + set clsWithResultModule; + map classToResultModule; + set rgClasses; + + map resultModuleToIndex; + for (auto &cls : Classes) { + if (!cls.isRemoved() && (all || classes.count(cls.getId()))) { + + if (cls.isRogaining() && cls.getResultModuleTag().empty()) + rgClasses.insert(cls.getId()); + + string t; + if (!defaultResult) + t = cls.getResultModuleTag(); + + if (!t.empty() && !specialResult) { + if (!resultModuleToIndex.count(t)) { + resultModuleToIndex[t] = runnersByResultModule.size(); + pair> empty(t, {}); + runnersByResultModule.emplace_back(empty); + } + classToResultModule[cls.getId()] = resultModuleToIndex[t]; + } + else { + classToResultModule[cls.getId()] = -1; + + if (!t.empty()) // For special resultr + clsWithResultModule.insert(cls.getId()); + } + } } - if (classCourseResults) - sortRunners(ClassCourseResult, runners); - else if (courseResults) - sortRunners(CourseResult, runners); - else if (!totalResults) - sortRunners(ClassResult, runners); - else - sortRunners(ClassTotalResult, runners); + vector runners; + { + vector runnersCls; + if (all) + getRunners({}, runnersCls); + else + getRunners(classes, runnersCls); - oRunnerList::iterator it; + runners.reserve(runnersCls.size()); + bool resOK = true; - int cClassId=-1; - int cPlace=0; - int vPlace=0; - int cTime=0; - int cDuplicateLeg=0; - int cLegEquClass = 0; - bool invalidClass = false; - bool useResults = false; - for (auto it : runners) { + for (auto it : runnersCls) { + oRunner &r = *it; + int cid = r.getClassId(true); + auto c = classToResultModule.find(cid); + if (c != classToResultModule.end() && c->second != -1) { + runnersByResultModule[c->second].second.push_back(&r); + } + runners.push_back(&r); + + if (resOK) { + if (courseResults && r.tCoursePlace.isOld(*this)) + resOK = false; + else if (classCourseResults && r.tCourseClassPlace.isOld(*this)) + resOK = false; + else if (totalResults && r.tTotalPlace.isOld(*this)) + resOK = false; + else if (r.tPlace.isOld(*this)) + resOK = false; + } + } + + if (resOK) + return; + } + // Reset computed status/time + + bool useComputedResult = false; + if (specialResult && !clsWithResultModule.empty()) { + // Calculate standard results and setup computed time/status. This is for coursewise result etc. + calculateResults(clsWithResultModule, oEvent::ResultType::ClassResult, includePreliminary); + runnersByResultModule.clear(); + useComputedResult = true; + } + else { + // Reset leader times + for (auto &cls : Classes) { + if (!cls.isRemoved() && (all || classes.count(cls.getId()))) { + for (unsigned leg = 0; leg < cls.getNumStages(); leg++) + cls.getLeaderInfo(leg).resetComputed(oClass::LeaderInfo::Type::Leg); + } + } + + for (auto r : runners) { + r->tComputedPoints = -1; + r->tComputedTime = r->getRunningTime(false); + r->tComputedStatus = r->getStatus(); + } + } + + calculateRunnerResults(resultType, rgClasses, runners, useComputedResult, includePreliminary); + + if (!runnersByResultModule.empty()) { + oEvent::ResultType otherType = totalResults ? oEvent::ResultType::ClassResult : oEvent::ResultType::TotalResult; + calculateRunnerResults(otherType, rgClasses, runners, false, includePreliminary); - // Start new "class" + resultCalculationLock = true; + try { + for (auto &resCalc : runnersByResultModule) { + wstring dmy; + auto &ge = oe->getGeneralResult(resCalc.first, dmy); + ge->calculateIndividualResults(resCalc.second, true, oListInfo::ResultType::Classwise, false, 0); + + for (pRunner r : resCalc.second) { + r->tComputedTime = r->getTempResult().getRunningTime(); + r->tComputedPoints = r->getTempResult().getPoints(); + r->tComputedStatus = r->getTempResult().getStatus(); + r->tPlace.update(*oe, r->getTempResult().getPlace()); + if (r->tComputedStatus == StatusOK && r->tComputedTime>0) { + pClass cls = r->getClassRef(true); + cls->getLeaderInfo(cls->mapLeg(r->getLegNumber())).updateComputed(r->tComputedTime, oClass::LeaderInfo::Type::Leg); + } + } + } + } + catch (...) { + resultCalculationLock = false; + throw; + } + resultCalculationLock = false; + } +} + +void oEvent::calculateRunnerResults(ResultType resultType, + const set &rgClasses, + vector &runners, + bool useComputedResult, + bool includePreliminary) const { + + const bool individualResults = resultType == ResultType::ClassResult; + const bool totalResults = resultType == ResultType::TotalResult || resultType == ResultType::TotalResultDefault; + const bool courseResults = resultType == ResultType::CourseResult; + const bool classCourseResults = resultType == ResultType::ClassCourseResult; + + typedef ResultCalcData DT; + vector
    resData; + resData.reserve(runners.size()); + int groupId, score; + for (auto it : runners) { + int clsId = it->getClassId(true); if (classCourseResults) { const pCourse crs = it->getCourse(false); - int crsId = it->getClassId(true) * 997 + (crs ? crs->getId() : 0); - if (crsId != cClassId) { - cClassId = crsId; - cPlace=0; - vPlace=0; - cTime=0; - useResults = it->Class ? !it->Class->getNoTiming() : false; - invalidClass = it->Class ? it->Class->getClassStatus() != oClass::Normal : false; - } + groupId = it->getClassId(true) * 997 + (crs ? crs->getId() : 0); } else if (courseResults) { const pCourse crs = it->getCourse(false); - int crsId = crs ? crs->getId() : 0; - if (crsId != cClassId) { - cClassId = crsId; - useResults = crs != 0; - cPlace=0; - vPlace=0; - cTime=0; - } - } - else if (it->getClassId(true) != cClassId || it->tDuplicateLeg!=cDuplicateLeg || it->tLegEquClass != cLegEquClass) { - cClassId=it->getClassId(true); - useResults = it->Class ? !it->Class->getNoTiming() : false; - cPlace=0; - vPlace=0; - cTime=0; - cDuplicateLeg = it->tDuplicateLeg; - cLegEquClass = it->tLegEquClass; - - invalidClass = it->Class ? it->Class->getClassStatus() != oClass::Normal : false; - } - - // Calculate results - if (invalidClass) { - it->tTotalPlace = 0; - it->tPlace.update(*this, 0); - } - else if (!totalResults) { - int tPlace = 0; - - if (it->tStatus==StatusOK || (includePreliminary && it->tStatus == StatusUnknown && it->FinishTime > 0)){ - cPlace++; - - int rt = it->getRunningTime() + it->getNumShortening() * 3600 * 24* 8; - - if (rt > cTime) - vPlace=cPlace; - - cTime = rt; - - if (useResults && cTime > 0) - tPlace = vPlace; - } - else - tPlace = 99000 + it->tStatus; - - if (!classCourseResults) - it->tPlace.update(*this, tPlace); - else - it->tCoursePlace = tPlace; + groupId = crs ? crs->getId() : 0; } else { - int tt = it->getTotalRunningTime(it->FinishTime, true); + groupId = clsId * 100 + (it->tDuplicateLeg + 10 * it->tLegEquClass); + } + if (rgClasses.count(clsId)) { + RunnerStatus st; + if (totalResults) + st = useComputedResult ? it->getStatusComputed() : it->getStatus(); + else + st = it->getTotalStatus(); + + if (st == StatusOK) + score = numeric_limits::max() - (3600 * 24 * 7 * max(1, 1 + it->getRogainingPoints(useComputedResult, totalResults)) - it->getRunningTime(false)); + else + score = -1; + } + else if (!totalResults) { + RunnerStatus st = useComputedResult ? it->getStatusComputed() : it->getStatus(); + if (st == StatusOK || (includePreliminary && st == StatusUnknown && it->FinishTime > 0)) + score = it->getRunningTime(useComputedResult) + it->getNumShortening() * 3600 * 24 * 8; + else + score = -1; + } + else { + int tt = it->getTotalRunningTime(it->FinishTime, useComputedResult, true); RunnerStatus totStat = it->getTotalStatus(); - if (totStat == StatusOK || (includePreliminary && totStat == StatusUnknown) && tt>0) { - cPlace++; - - if (tt > cTime) - vPlace = cPlace; - - cTime = tt; - - if (useResults) - it->tTotalPlace = vPlace; - else - it->tTotalPlace = 0; - } + if (totStat == StatusOK || (includePreliminary && totStat == StatusUnknown && it->inputStatus == StatusOK) && tt > 0) + score = tt; else - it->tTotalPlace = 99000 + it->tStatus; + score = -1; } + + resData.emplace_back(groupId, score, it); } + + if (courseResults) + calculatePlace(resData, [this](DT &res, int value) {res.dst->tCoursePlace.update(*this, value); }); + else if (classCourseResults) + calculatePlace(resData, [this](DT &res, int value) {res.dst->tCourseClassPlace.update(*this, value); }); + else if (totalResults) + calculatePlace(resData, [this](DT &res, int value) {res.dst->tTotalPlace.update(*this, value); }); + else + calculatePlace(resData, [this](DT &res, int value) {res.dst->tPlace.update(*this, value); }); } -void oEvent::calculateRogainingResults(const set &classSelection) const { - const bool all = classSelection.empty(); - vector runners; - runners.reserve(Runners.size()); - for (auto &r : Runners) { - if (r.isRemoved()) - continue; - - if (!all && !classSelection.count(r.getClassId(true))) - continue; - - if (r.Class && r.Class->isRogaining()) { - runners.push_back(&r); - } - } - sortRunners(ClassPoints, runners); - - int cClassId=-1; - int cPlace = 0; - int vPlace = 0; - int cTime = numeric_limits::min(); - int cDuplicateLeg=0; - bool useResults = false; - bool isRogaining = false; - bool invalidClass = false; - - for (auto it : runners) { - if (it->getClassId(true)!=cClassId || it->tDuplicateLeg!=cDuplicateLeg) { - cClassId = it->getClassId(true); - useResults = it->Class ? !it->Class->getNoTiming() : false; - cPlace = 0; - vPlace = 0; - cTime = numeric_limits::min(); - cDuplicateLeg = it->tDuplicateLeg; - invalidClass = it->Class ? it->Class->getClassStatus() != oClass::Normal : false; - } - - if (invalidClass) { - it->tTotalPlace = 0; - it->tPlace.update(*this, 0); - } - else if (it->tStatus==StatusOK) { - cPlace++; - - int cmpRes = 3600 * 24 * 7 * it->tRogainingPoints - it->getRunningTime(); - - if (cmpRes != cTime) - vPlace = cPlace; - - cTime = cmpRes; - - if (useResults) - it->tPlace.update(*this, vPlace); - else - it->tPlace.update(*this, 0); - } - else - it->tPlace.update(*this, 99000 + it->tStatus); - } -} - -bool oEvent::calculateTeamResults(int leg, bool totalMultiday) -{ +bool oEvent::calculateTeamResults(vector &teams, int leg, ResultType resType) { oTeamList::iterator it; bool hasRunner; - if (totalMultiday) - hasRunner = sortTeams(ClassTotalResult, leg, true); + if (resType == ResultType::TotalResult) + hasRunner = sortTeams(ClassTotalResult, leg, true, teams); else - hasRunner = sortTeams(ClassResult, leg, true); + hasRunner = sortTeams(ClassDefaultResult, leg, true, teams); if (!hasRunner) return false; - int cClassId=0; - int cPlace=0; - int vPlace=0; - int cTime=0; + int cClassId = 0; + int cPlace = 0; + int vPlace = 0; + int cTime = 0; bool invalidClass = false; + oTeam::ComputedLegResult res; - for (it=Teams.begin(); it != Teams.end(); ++it){ + for (auto it : teams) { if (it->isRemoved()) continue; - if (it->Class && it->Class->Id!=cClassId){ - cClassId=it->Class->Id; - cPlace=0; - vPlace=0; - cTime=0; + if (it->Class && it->Class->Id != cClassId) { + cClassId = it->Class->Id; + cPlace = 0; + vPlace = 0; + cTime = 0; invalidClass = it->Class->getClassStatus() != oClass::Normal; } int sleg; - if (leg==-1) - sleg=it->Runners.size()-1; + if (leg == -1) + sleg = it->Runners.size() - 1; else - sleg=leg; + sleg = leg; + + if (size_t(leg) >= it->Runners.size()) + continue; + int p; if (invalidClass) { p = 0; } - else if (it->_cachedStatus == StatusOK){ + else if (it->_cachedStatus == StatusOK) { cPlace++; - if (it->_sortTime>cTime) - vPlace=cPlace; + if (it->_sortTime > cTime) + vPlace = cPlace; cTime = it->_sortTime; p = vPlace; } else { - p = 99000+it->_sortStatus; //XXX Set to zero!? + p = 0; } - if (totalMultiday) - it->_places[sleg].totalP = p; - else - it->_places[sleg].p = p; + if (resType == ResultType::TotalResult) + it->getTeamPlace(sleg).totalP.update(*this, p); + else { + it->getTeamPlace(sleg).p.update(*this, p); + res.version = dataRevision; + res.status = it->_cachedStatus; + res.time = it->_sortTime; + it->setComputedResult(sleg, res); } + } return true; } -void oEvent::calculateTeamResults(bool multidayTotal) -{ - for(int i=0;i &classSelection, ResultType resType) { + set classSelectionC; + set classSelectionM; + bool allC = resType == ResultType::ClassResultDefault || resType == ResultType::TotalResult; + bool totalResult = resType == ResultType::TotalResult; + if (classSelection.empty()) { + for (auto &c : Classes) { + if (!c.isRemoved()) { + if (allC || c.getResultModuleTag().empty()) + classSelectionC.insert(c.getId()); + else + classSelectionM.insert(c.getId()); + } + } + } + else { + for (int id : classSelection) { + pClass c = getClass(id); + if (c) { + if (c->getResultModuleTag().empty()) + classSelectionC.insert(id); + else + classSelectionM.insert(id); + } + } + } + + vector teams; + vector teamsMod; + bool resultOK = true; + teams.reserve(Teams.size()); + for (auto &t : Teams) { + if (t.isRemoved()) + continue; + + if (classSelectionC.count(t.getClassId(true))) { + if (resultOK && !t.isResultUpdated(totalResult)) + resultOK = false; + teams.push_back(&t); + } + else if (classSelectionM.count(t.getClassId(true))) { + if (resultOK && !t.isResultUpdated(totalResult)) + resultOK = false; + teamsMod.push_back(&t); + } + } + + if (resultOK) + return; + + for (int i = 0; i < maxRunnersTeam; i++) { + if (!calculateTeamResults(teams, i, resType)) + break; + } + + if (!teamsMod.empty()) + calculateModuleTeamResults(classSelectionM, teamsMod); +} + +void oEvent::calculateTeamResults(const vector &teams, ResultType resultType) { + bool allC = resultType == ResultType::ClassResultDefault || resultType == ResultType::TotalResultDefault; + bool totalResult = resultType == ResultType::TotalResult || resultType == ResultType::TotalResultDefault; + + set cls, clsMod; + for (pTeam t : teams) + cls.insert(t->getClassId(true)); + + if (!allC) { + for (int id : cls) { + pClass c = getClass(id); + if (c) { + if (!c->getResultModuleTag().empty()) + clsMod.insert(id); + } + } + } + vector teamsStd; + vector teamsMod; + bool resultOK = true; + teamsStd.reserve(teams.size()); + if (!clsMod.empty()) + teamsMod.reserve(teams.size()); + for (auto &t : teams) { + if (resultOK && !t->isResultUpdated(totalResult)) + resultOK = false; + + if (clsMod.count(t->getClassId(true))) + teamsMod.push_back(t); + else + teamsStd.push_back(t); + } + + if (resultOK) + return; + + for (int i = 0; i < maxRunnersTeam; i++) { + if (!calculateTeamResults(teamsStd, i, resultType)) + break; + } + + if (!teamsMod.empty()) + calculateModuleTeamResults(clsMod, teamsMod); +} + +void oEvent::calculateModuleTeamResults(const set &cls, vector &teams) { + map cls2Mod; + set rgClasses; + for (int id : cls) { + pClass c = getClass(id); + if (c->isRogaining()) + rgClasses.insert(id); + for (unsigned leg = 0; leg < c->getNumStages(); leg++) { + c->getLeaderInfo(leg).resetComputed(oClass::LeaderInfo::Type::Total); + c->getLeaderInfo(leg).resetComputed(oClass::LeaderInfo::Type::TotalInput); + } + + cls2Mod[c->Id] = c->getResultModuleTag(); + } + map> teamByResultModule; + for (auto t : teams) { + teamByResultModule[cls2Mod[t->getClassId(true)]].push_back(t); + } + typedef ResultCalcData DT; + typedef ResultCalcData DR; + + vector legResultsData; + legResultsData.reserve(Runners.size()); + + for (auto &resCalc : teamByResultModule) { + wstring dmy; + + auto &ge = oe->getGeneralResult(resCalc.first, dmy); + ge->calculateTeamResults(resCalc.second, true, oListInfo::ResultType::Classwise, false, 0); + vector
    resData; + resData.reserve(resCalc.second.size()); + + for (pTeam t : resCalc.second) { + pClass teamClass = t->getClassRef(true); + + t->tComputedTime = t->getTempResult().getRunningTime(); + t->tComputedPoints = t->getTempResult().getPoints(); + t->tComputedStatus = t->getTempResult().getStatus(); + bool ok = true; + int timeAcc = 0, timePar = 0; + + int totScore = -1; + int clsId = t->getClassId(true); + if (t->tComputedStatus == StatusOK && t->inputStatus == StatusOK) { + if (rgClasses.count(clsId)) + totScore = numeric_limits::max() - (7 * 24 * 3600 * max(1, (1 + t->getRogainingPoints(true, true))) - (t->tComputedTime + t->inputTime)); + else + totScore = t->tComputedTime + t->inputTime; + } + + resData.emplace_back(clsId, totScore, t); + + for (int i = 0; i < t->getNumRunners(); i++) { + t->getTeamPlace(i).p.update(*this, t->getTempResult().getPlace()); + t->getTeamPlace(i).totalP.update(*this, t->getTempResult().getPlace()); + oTeam::ComputedLegResult res; + res.version = dataRevision; + int legTime = 0; + bool lastLeg = i + 1 == t->getNumRunners(); + + if (t->Runners[i]) { + res.status = t->Runners[i]->getTempResult().getStatus(); + res.time = t->Runners[i]->getTempResult().getRunningTime(); + legTime = res.time; + + auto lt = teamClass->getLegType(i); + if (res.status == StatusOK) { + if (lt == LTParallel || lt == LTParallelOptional) + timePar = max(timePar, res.time); + else if (lt != LTIgnore && lt != LTExtra) + timePar = res.time; + + teamClass->getLeaderInfo(i).updateComputed(res.time, oClass::LeaderInfo::Type::Leg); + } + else { + ok = false; + } + + if (ok || (lastLeg && t->tComputedStatus == StatusOK)) { + if (lastLeg) + legTime = t->tComputedTime; + else + legTime = timeAcc + timePar; + teamClass->getLeaderInfo(i).updateComputed(legTime, oClass::LeaderInfo::Type::Total); + teamClass->getLeaderInfo(i).updateComputed(t->getInputTime() + legTime, oClass::LeaderInfo::Type::TotalInput); + } + + auto ltNext = teamClass->getLegType(i + 1); + if (ltNext == LTNormal || ltNext == LTSum) + timeAcc += timePar; + + timePar = 0; + + t->Runners[i]->tComputedTime = res.time; + t->Runners[i]->tComputedStatus = res.status; + t->Runners[i]->tComputedPoints = t->Runners[i]->getTempResult().getPoints(); + + int legScore = -1; + if (res.status == StatusOK) { + if (rgClasses.count(clsId)) + legScore = numeric_limits::max() - (7 * 24 * 3600 * max(1, (1 + t->Runners[i]->tComputedPoints)) - res.time); + else + legScore = res.time; + } + + pClass rCls = t->Runners[i]->getClassRef(true); + int leg = rCls->mapLeg(i); + legResultsData.emplace_back(rCls->getId() * 256 + leg, legScore, t->Runners[i]); + } + else { + if (!teamClass->isOptional(i)) + ok = false; + } + if (t->tComputedStatus == StatusOK || + t->tComputedStatus == StatusOutOfCompetition || + t->tComputedStatus == StatusNoTiming) + res.time = legTime; + + if (lastLeg) { + res.time = t->tComputedTime; + res.status = t->tComputedStatus; + } + + t->setComputedResult(i, res); + } + } + + calculatePlace(legResultsData, [this](DR &res, int value) { + res.dst->tPlace.update(*this, value); + res.dst->tTotalPlace.update(*this, value); }); + + // Calculate and store total result + calculatePlace(resData, [this](DT &res, int value) { + for (int i = 0; i < res.dst->getNumRunners(); i++) { + res.dst->getTeamPlace(i).totalP.update(*this, value); + }}); } } -GeneralResult &oEvent::getGeneralResult(const string &tag, wstring &sourceFileOut) const { +const shared_ptr &oEvent::getGeneralResult(const string &tag, wstring &sourceFileOut) const { for (int i = 0; i < 2; i++) { if (i>0) - loadGeneralResults(false); + loadGeneralResults(false, true); for (size_t k = 0; k < generalResults.size(); k++) { if (tag == generalResults[k].tag) { if (generalResults[k].ptr == 0) @@ -377,14 +703,14 @@ GeneralResult &oEvent::getGeneralResult(const string &tag, wstring &sourceFileOu if (sourceFileOut == L"*") sourceFileOut = L""; - return *generalResults[k].ptr; + return generalResults[k].ptr; } } } - throw meosException("Result module not found: " + tag); + throw meosException("There is no result module with X as identifier.#" + tag); } -void oEvent::loadGeneralResults(bool forceReload) const { +void oEvent::loadGeneralResults(bool forceReload, bool loadFromDisc) const { // OutputDebugString("Load General Results\n"); wchar_t bf[260]; getUserFile(bf, L""); @@ -398,79 +724,34 @@ void oEvent::loadGeneralResults(bool forceReload) const { vector newGeneralResults; set loaded; - set tags; + map tags; set loadedRes; - for (size_t k = 0; k < generalResults.size(); k++) { - if (forceReload) { + if (forceReload) { + // Keep only built-in (non-dynamic) result modules + for (size_t k = 0; k < generalResults.size(); k++) { if (!generalResults[k].isDynamic()) newGeneralResults.push_back(generalResults[k]); } - else if (generalResults[k].isDynamic()) { - loaded.insert(generalResults[k].fileSource); - tags.insert(generalResults[k].tag); - loadedRes.insert(dynamic_cast(*generalResults[k].ptr).getHashCode()); - } - } - if (forceReload) generalResults.clear(); - else + } + else { + // Mark all already loaded results as loaded + for (size_t k = 0; k < generalResults.size(); k++) { + if (generalResults[k].isDynamic()) { + loaded.insert(generalResults[k].fileSource); + tags.emplace(generalResults[k].tag, k); + loadedRes.insert(dynamic_cast(*generalResults[k].ptr).getHashCode()); + } + } swap(generalResults, newGeneralResults); - - size_t builtIn = res2.size(); - for (size_t k = 0; k < res.size(); k++) - res2.push_back(res[k]); - - for (size_t k = 0; k < res2.size(); k++) { - try { - if (loaded.count(res2[k])) - continue; - - dr.load(res2[k]); - while (tags.count(dr.getTag())) { - dr.setTag(dr.getTag() + "x"); - } - - tags.insert(dr.getTag()); - DynamicResult *drp = new DynamicResult(dr); - if (k < builtIn) - drp->setBuiltIn(); - - loadedRes.insert(drp->getHashCode()); - newGeneralResults.push_back(GeneralResultCtr(res2[k], drp)); - } - catch (meosException &ex) { - if (err.first.empty()) { - err.first = res2[k]; - err.second = ex.wwhat(); - } - } - catch (std::exception &ex) { - if (err.first.empty()) { - err.first = res2[k]; - err.second = gdibase.widen(ex.what()); - } - } } + vector rmAll; - for (int k = 0; k < getListContainer().getNumLists(); k++) { - vector rm; - //if (!getListContainer().isExternal(k)) - // continue; - getListContainer().getList(k).getDynamicResults(rm); - - if (!getListContainer().isExternal(k)) { - for (size_t j = 0; j < rm.size(); j++) { - if (rm[j].res) - rm[j].res->setReadOnly(); - } - } - - rmAll.insert(rmAll.end(), rm.begin(), rm.end()); - } + getListContainer().getGeneralResults(rmAll); // Get the open list from list editor TabList &tl = dynamic_cast(*gdibase.getTabs().get(TListTab)); - ListEditor *le = tl.getListeditor(); + ListEditor *le = tl.getListEditorPtr(); if (le) { MetaList *editorList = le->getCurrentList(); if (editorList) { @@ -479,7 +760,7 @@ void oEvent::loadGeneralResults(bool forceReload) const { rmAll.insert(rmAll.end(), rm.begin(), rm.end()); } } - + for (size_t ii = 1; ii <= rmAll.size(); ii++) { size_t i = rmAll.size() - ii; if (!rmAll[i].res) @@ -487,51 +768,153 @@ void oEvent::loadGeneralResults(bool forceReload) const { long long hash = rmAll[i].res->getHashCode(); string newTag = rmAll[i].res->getTag(); - string db = "Load result " + newTag + ", h=" + itos(hash) + "\n"; -// OutputDebugString(db.c_str()); - - if (loadedRes.count(hash) && tags.count(newTag)) - continue; // Already loaded - + //string db = "Load result " + newTag + ", h=" + itos(hash) + "\n"; + //OutputDebugStringA(db.c_str()); + int setIx = -1; if (tags.count(newTag)) { - int n = 1; - newTag = DynamicResult::undecorateTag(newTag); - while(tags.count(newTag + "-v" + itos(n))) { - n++; + int ix = tags[newTag]; + + if (loadedRes.count(hash) || newGeneralResults[ix].isImplicit()) + setIx = ix; // Already loaded + + if (setIx == -1) { + + if (!rmAll[i].res->retaggable()) { + //assert(newTag == rmAll.back().res->getTag()); + assert(newGeneralResults[ix].tag == newTag); + int n = 1; + string baseTag = newTag = DynamicResult::undecorateTag(newTag); + while (tags.count(newTag)) { + newTag = baseTag + "-v" + itos(++n); + } + if (newGeneralResults[ix].isDynamic()) { + tags[newTag] = ix; + newGeneralResults[ix].tag = newTag; + } + } + else { + int n = 1; + string baseTag = newTag = DynamicResult::undecorateTag(newTag); + while (tags.count(newTag)) { + newTag = baseTag + "-v" + itos(++n); + } + + string db = "Retag " + newTag + "\n"; + OutputDebugStringA(db.c_str()); + + if (rmAll[i].ctr) + rmAll[i].ctr->retagResultModule(newTag, true); + } } - newTag += "-v" + itos(n); - - string db = "Retag " + newTag + "\n"; -// OutputDebugString(db.c_str()); - - rmAll[i].ctr->retagResultModule(newTag, true); } + auto &drp = rmAll[i].res; + drp->setAnnotation(rmAll[i].getAnnotation()); - tags.insert(rmAll[i].res->getTag()); - DynamicResult *drp = new DynamicResult(*rmAll[i].res); - if (rmAll[i].res->isReadOnly()) - drp->setReadOnly(); - drp->setAnnotation(rmAll[i].ctr->getListName()); - wstring file = L"*"; - newGeneralResults.push_back(GeneralResultCtr(file, drp)); + if (setIx == -1) { + tags.emplace(drp->getTag(), newGeneralResults.size()); + newGeneralResults.emplace_back(wstring(L"*"), drp); + } + else { + newGeneralResults[setIx] = GeneralResultCtr(wstring(L"*"), drp); + } + } + + if (loadFromDisc) { + size_t builtIn = res2.size(); + for (size_t k = 0; k < res.size(); k++) + res2.push_back(res[k]); + + for (size_t k = 0; k < res2.size(); k++) { + try { + if (loaded.count(res2[k])) + continue; + + dr.load(res2[k]); + string tag = DynamicResult::undecorateTag(dr.getTag()); + int iter = 1; + while (tags.count(tag)) + tag = dr.getTag() + + "_v" + itos(++iter); + if (iter > 1) + dr.setTag(tag); + + auto drp = make_shared(dr); + if (loadedRes.count(drp->getHashCode())) + continue; + + tags.emplace(dr.getTag(), newGeneralResults.size()); + if (k < builtIn) + drp->setBuiltIn(); + + loadedRes.insert(drp->getHashCode()); + newGeneralResults.push_back(GeneralResultCtr(res2[k], drp)); + } + catch (meosException &ex) { + if (err.first.empty()) { + err.first = res2[k]; + err.second = ex.wwhat(); + } + } + catch (std::exception &ex) { + if (err.first.empty()) { + err.first = res2[k]; + err.second = gdibase.widen(ex.what()); + } + } + } } swap(newGeneralResults, generalResults); + size_t ndIx; + for (ndIx = 0; ndIx < generalResults.size(); ndIx++) { + if (generalResults[ndIx].isDynamic()) + break; + } + sort(generalResults.begin() + ndIx, generalResults.end()); if (!err.first.empty()) throw meosException(L"Error loading X (Y)#" + err.first + L"#" + err.second); } +void oEvent::setGeneralResultContext(const oListParam *ctx) { + for (auto &gr : generalResults) { + if (ctx) + gr.ptr->setContext(ctx); + else + gr.ptr->clearContext(); + } +} + void oEvent::getGeneralResults(bool onlyEditable, vector< pair > > &tagNameList, bool includeDate) const { tagNameList.clear(); + map count; + map, int> countDate; + for (size_t k = 0; k < generalResults.size(); k++) { + string date = generalResults[k].ptr->getTimeStamp().substr(0,10); + ++count[generalResults[k].name]; + ++countDate[make_pair(generalResults[k].name, date)]; + } + for (size_t k = 0; k < generalResults.size(); k++) { if (!onlyEditable || generalResults[k].isDynamic()) { tagNameList.push_back(make_pair(100 + k, make_pair(generalResults[k].tag, lang.tl(generalResults[k].name)))); - if (includeDate && generalResults[k].isDynamic()) { - const DynamicResult &dr = dynamic_cast(*generalResults[k].ptr); - const wstring &date = gdibase.widen(dr.getTimeStamp()); - if (!date.empty()) - tagNameList.back().second.second += L" [" + date + L"]"; + if (count[generalResults[k].name] > 1) { + size_t res = generalResults[k].tag.find_last_of('v'); + if (res != string::npos) { + string version = generalResults[k].tag.substr(res); + tagNameList.back().second.second += L", " + gdioutput::widen(version); + } + if (includeDate) { + const string &datetime = generalResults[k].ptr->getTimeStamp(); + if (!datetime.empty()) { + string date = datetime.substr(0, 10); + if (countDate[make_pair(generalResults[k].name, date)] > 1) { + tagNameList.back().second.second += L", " + gdioutput::widen(datetime); + } + else { + tagNameList.back().second.second += L", " + gdioutput::widen(date); + } + } + } } } } @@ -730,12 +1113,12 @@ void oEvent::computePreliminarySplitResults(const set &classes) const { negLeg = -1000; //Finish, smallest number for (int j = 0; j < nRun; j++) { auto r = rr[j]; - if (r->prelStatusOK()) { + if (r->prelStatusOK(true, false)) { int time; if (!r->tInTeam || !totRes) - time = r->getRunningTime(); + time = r->getRunningTime(true); else { - time = r->tInTeam->getLegRunningTime(r->tLeg, false); + time = r->tInTeam->getLegRunningTime(r->tLeg, true, false); } int ix = -1; int nr = r->tOnCourseResults.res.size(); diff --git a/code/oEventSQL.cpp b/code/oEventSQL.cpp index 2f01bd2..83fe22e 100644 --- a/code/oEventSQL.cpp +++ b/code/oEventSQL.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -79,7 +79,6 @@ void oEvent::startReconnectDaemon() bool oEvent::msSynchronize(oBase *ob) { - //if (!msSynchronizeRead) return true; if (!HasDBConnection && !HasPendingDBConnection) return true; @@ -95,7 +94,7 @@ bool oEvent::msSynchronize(oBase *ob) } if (typeid(*ob)==typeid(oTeam)) { - static_cast(ob)->apply(false, 0, false); + static_cast(ob)->apply(ChangeType::Quiet, nullptr); } if (ret==1) { @@ -105,98 +104,33 @@ bool oEvent::msSynchronize(oBase *ob) return ret!=0; } -void oEvent::resetChangeStatus(bool onlyChangable) -{ - if (HasDBConnection) { - if (!onlyChangable) { - // The object here has no dependeces that makes it necessary to set/rest - - for (list::iterator it=oe->punches.begin(); - it!=oe->punches.end(); ++it) - it->resetChangeStatus(); - - //synchronize changed objects - for (list::iterator it=oe->Cards.begin(); - it!=oe->Cards.end(); ++it) - it->resetChangeStatus(); - - for (list::iterator it=oe->Clubs.begin(); - it!=oe->Clubs.end(); ++it) - it->resetChangeStatus(); - - for (list::iterator it=oe->Controls.begin(); - it!=oe->Controls.end(); ++it) - it->resetChangeStatus(); - - for (list::iterator it=oe->Courses.begin(); - it!=oe->Courses.end(); ++it) - it->resetChangeStatus(); - - for (list::iterator it=oe->Classes.begin(); - it!=oe->Classes.end(); ++it) - it->resetChangeStatus(); - } - - for (list::iterator it=oe->Runners.begin(); - it!=oe->Runners.end(); ++it) - it->resetChangeStatus(); - - for (list::iterator it=oe->Teams.begin(); - it!=oe->Teams.end(); ++it) - it->resetChangeStatus(); - } -} - -void oEvent::storeChangeStatus(bool onlyChangable) -{ - if (HasDBConnection) { - if (!onlyChangable) { - // The object here has no dependeces that makes it necessary to set/rest - for (list::iterator it=oe->punches.begin(); - it!=oe->punches.end(); ++it) - it->storeChangeStatus(); - - for (list::iterator it=oe->Cards.begin(); - it!=oe->Cards.end(); ++it) - it->storeChangeStatus(); - - for (list::iterator it=oe->Clubs.begin(); - it!=oe->Clubs.end(); ++it) - it->storeChangeStatus(); - - for (list::iterator it=oe->Controls.begin(); - it!=oe->Controls.end(); ++it) - it->storeChangeStatus(); - - for (list::iterator it=oe->Courses.begin(); - it!=oe->Courses.end(); ++it) - it->storeChangeStatus(); - - for (list::iterator it=oe->Classes.begin(); - it!=oe->Classes.end(); ++it) - it->storeChangeStatus(); - } - - for (list::iterator it=oe->Runners.begin(); - it!=oe->Runners.end(); ++it) - it->storeChangeStatus(); - - for (list::iterator it=oe->Teams.begin(); - it!=oe->Teams.end(); ++it) - it->storeChangeStatus(); - } -} - bool MEOSDB_API msSynchronizeList(oEvent *, oListId lid); bool oEvent::synchronizeList(initializer_list types) { if (!HasDBConnection) return true; + resetSynchTimes(); msSynchronize(this); resetSQLChanged(true, false); + vector toSync; + bool hasCard = false; for (oListId t : types) { + if (t == oListId::oLRunnerId && !hasCard) { + hasCard = true; + toSync.push_back(oListId::oLCardId); // Make always card sync before runners + } + else if (t == oListId::oLCardId) { + if (hasCard) + continue; + else + hasCard = true; + } + toSync.push_back(t); + } + + for (oListId t : toSync) { if (!msSynchronizeList(this, t)) { verifyConnection(); return false; @@ -208,7 +142,6 @@ bool oEvent::synchronizeList(initializer_list types) { reinitializeClasses(); reEvaluateChanged(); - resetChangeStatus(); return true; } @@ -216,6 +149,12 @@ bool oEvent::synchronizeList(oListId id, bool preSyncEvent, bool postSyncEvent) if (!HasDBConnection) return true; + if (preSyncEvent && postSyncEvent && id == oListId::oLRunnerId) { + resetSynchTimes(); + synchronizeList(oListId::oLCardId, true, false); + preSyncEvent = false; + } + if (preSyncEvent) { msSynchronize(this); resetSQLChanged(true, false); @@ -232,7 +171,6 @@ bool oEvent::synchronizeList(oListId id, bool preSyncEvent, bool postSyncEvent) if (postSyncEvent) { reinitializeClasses(); reEvaluateChanged(); - resetChangeStatus(); return true; } @@ -240,43 +178,36 @@ bool oEvent::synchronizeList(oListId id, bool preSyncEvent, bool postSyncEvent) } bool oEvent::needReEvaluate() { - return sqlChangedRunners | - sqlChangedClasses | - sqlChangedCourses | - sqlChangedControls | - sqlChangedCards | - sqlChangedTeams; + return sqlRunners.changed | + sqlClasses.changed | + sqlCourses.changed | + sqlControls.changed | + sqlCards.changed | + sqlTeams.changed; } void oEvent::resetSQLChanged(bool resetAllTeamsRunners, bool cleanClasses) { if (empty()) return; - sqlChangedRunners = false; - sqlChangedClasses = false; - sqlChangedCourses = false; - sqlChangedControls = false; - sqlChangedClubs = false; - sqlChangedCards = false; - sqlChangedPunches = false; - sqlChangedTeams = false; + sqlRunners.changed = false; + sqlClasses.changed = false; + sqlCourses.changed = false; + sqlControls.changed = false; + sqlClubs.changed = false; + sqlCards.changed = false; + sqlPunches.changed = false; + sqlTeams.changed = false; if (resetAllTeamsRunners) { - for (list::iterator it=oe->Runners.begin(); - it!=oe->Runners.end(); ++it) { - it->storeChangeStatus(); - it->sqlChanged = false; - } - for (list::iterator it=oe->Teams.begin(); - it!=oe->Teams.end(); ++it) { - it->storeChangeStatus(); - it->sqlChanged = false; - } + for (auto &r : Runners) + r.sqlChanged = false; + for (auto &t : Teams) + t.sqlChanged = false; } if (cleanClasses) { // This data is used to redraw lists/speaker etc. for (list::iterator it=oe->Classes.begin(); it!=oe->Classes.end(); ++it) { - it->storeChangeStatus(); it->sqlChangedControlLeg.clear(); it->sqlChangedLegControl.clear(); } @@ -304,6 +235,7 @@ bool oEvent::autoSynchronizeLists(bool SyncPunches) if (mask == 0) return false; + resetSynchTimes(); // Reset change data and store update status on objects // (which might be incorrectly changed during sql update) resetSQLChanged(true, false); @@ -320,74 +252,74 @@ bool oEvent::autoSynchronizeLists(bool SyncPunches) //Controls if (isSet(mask, oListId::oLControlId)) { - int oc = sqlCounterControls; - ot = sqlUpdateControls; + int oc = sqlControls.counter; + ot = sqlControls.updated; synchronizeList(oListId::oLControlId, false, false); - changed |= oc!=sqlCounterControls; - changed |= ot!=sqlUpdateControls; + changed |= oc != sqlControls.counter; + changed |= ot != sqlControls.updated; } //Courses if (isSet(mask, oListId::oLCourseId)) { - int oc = sqlCounterCourses; - ot = sqlUpdateCourses; + int oc = sqlCourses.counter; + ot = sqlCourses.updated; synchronizeList(oListId::oLCourseId, false, false); - changed |= oc!=sqlCounterCourses; - changed |= ot!=sqlUpdateCourses; + changed |= oc != sqlCourses.counter; + changed |= ot != sqlCourses.updated; } //Classes if (isSet(mask, oListId::oLClassId)) { - int oc = sqlCounterClasses; - ot = sqlUpdateClasses; + int oc = sqlClasses.counter; + ot = sqlClasses.updated; synchronizeList(oListId::oLClassId, false, false); - changed |= oc!=sqlCounterClasses; - changed |= ot!=sqlUpdateClasses; + changed |= oc != sqlClasses.counter; + changed |= ot != sqlClasses.updated; } //Clubs if (isSet(mask, oListId::oLClubId)) { - int oc = sqlCounterClubs; - ot = sqlUpdateClubs; + int oc = sqlClubs.counter; + ot = sqlClubs.updated; synchronizeList(oListId::oLClubId, false, false); - changed |= oc!=sqlCounterClubs; - changed |= ot!=sqlUpdateClubs; + changed |= oc != sqlClubs.counter; + changed |= ot != sqlClubs.updated; } //Cards if (isSet(mask, oListId::oLCardId)) { - int oc = sqlCounterCards; - ot = sqlUpdateCards; + int oc = sqlCards.counter; + ot = sqlCards.updated; synchronizeList(oListId::oLCardId, false, false); - changed |= oc!=sqlCounterCards; - changed |= ot!=sqlUpdateCards; + changed |= oc != sqlCards.counter; + changed |= ot != sqlCards.updated; } //Runners if (isSet(mask, oListId::oLRunnerId)) { - int oc = sqlCounterRunners; - ot = sqlUpdateRunners; + int oc = sqlRunners.counter; + ot = sqlRunners.updated; synchronizeList(oListId::oLRunnerId, false, false); - changed |= oc!=sqlCounterRunners; - changed |= ot!=sqlUpdateRunners; + changed |= oc != sqlRunners.counter; + changed |= ot != sqlRunners.updated; } //Teams if (isSet(mask, oListId::oLTeamId)) { - int oc = sqlCounterTeams; - ot = sqlUpdateTeams; + int oc = sqlTeams.counter; + ot = sqlTeams.updated; synchronizeList(oListId::oLTeamId, false, false); - changed |= oc!=sqlCounterTeams; - changed |= ot!=sqlUpdateTeams; + changed |= oc != sqlTeams.counter; + changed |= ot != sqlTeams.updated; } if (SyncPunches && isSet(mask, oListId::oLPunchId)) { //Punches - int oc = sqlCounterPunches; - ot = sqlUpdatePunches; + int oc = sqlPunches.counter; + ot = sqlPunches.updated; synchronizeList(oListId::oLPunchId, false, false); - changed |= oc!=sqlCounterPunches; - changed |= ot!=sqlUpdatePunches; + changed |= oc != sqlPunches.counter; + changed |= ot != sqlPunches.updated; } if (changed) { @@ -397,7 +329,6 @@ bool oEvent::autoSynchronizeLists(bool SyncPunches) reCalculateLeaderTimes(0); //Restore changed staus on object that might have been changed //during sql update, due to partial updates - resetChangeStatus(); return true; } @@ -430,7 +361,7 @@ bool oEvent::connectToMySQL(const string &server, const string &user, const stri if (!msConnectToServer(this)) { char bf[256]; msGetErrorState(bf); - MessageBox(NULL, lang.tl(bf).c_str(), L"Error", MB_OK); + gdibase.alert(bf); return false; } @@ -666,7 +597,10 @@ bool oEvent::readSynchronize(const CompetitionInfo &ci) if (teamCorrected) { for (oTeamList::iterator it = Teams.begin(); it != Teams.end(); ++it) { - it->apply(true, 0, false); + if (!it->isRemoved()) { + it->apply(oBase::ChangeType::Update, nullptr); + it->synchronize(true); + } } } diff --git a/code/oEventSpeaker.cpp b/code/oEventSpeaker.cpp index 25ac166..f69854b 100644 --- a/code/oEventSpeaker.cpp +++ b/code/oEventSpeaker.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -681,28 +681,40 @@ void oEvent::speakerList(gdioutput &gdi, int ClassId, int leg, int ControlId, } vector dx_new(maxRow); + int s60 = gdi.scaleLength(60); + int s28 = gdi.scaleLength(35); + int s7 = gdi.scaleLength(7); + TextInfo ti; + HDC hDC = GetDC(gdi.getHWNDTarget()); for (size_t k = 0; k < toRender.size(); k++) { const vector &row = toRender[k].second; for (size_t j = 0; j < row.size(); j++) { if (!row[j].str.empty()) { - double len = row[j].str.length(); + ti.xp = 0; + ti.yp = 0; + ti.format = 0; + ti.text = row[j].str; + gdi.calcStringSize(ti, hDC); + dx_new[j] = max(s28, max(dx_new[j], ti.textRect.right+s7)); + /*double len = row[j].str.length(); double factor = 5.6; if (len < 4) factor = 7; else if (len <10) factor = 5.8; - dx_new[j] = max(28, max(dx_new[j], int(len * factor)+15)); + dx_new[j] = max(28, max(dx_new[j], int(len * factor)+15));*/ } else if (row[j].hasTimer) { - dx_new[j] = max(dx_new[j], 60); + dx_new[j] = max(dx_new[j], s60); } } } + ReleaseDC(gdi.getHWNDTarget(), hDC); bool rendered; int limit = gdi.scaleLength(280); vector dx(maxRow+1); for (size_t k = 1; k < dx.size(); k++) { - dx[k] = dx[k-1] + min(limit, gdi.scaleLength(dx_new[k-1])); + dx[k] = dx[k-1] + min(limit, dx_new[k-1] + gdi.scaleLength(4)); } rendered = false; @@ -1240,7 +1252,7 @@ int oEvent::setupTimeLineEvents(vector &started, const vector< pair 0) { - bestTotalTime[j].addTime(r.getTotalRunningTime(rt + r.tStartTime, true)); + bestTotalTime[j].addTime(r.getTotalRunningTime(rt + r.tStartTime, true, true)); bestRaceTime[j].addTime(rt); // Calculate leg time since last radio (or start) int lt = 0; @@ -1254,7 +1266,7 @@ int oEvent::setupTimeLineEvents(vector &started, const vector< pair0 && r.tStatus == StatusOK) { // Get best total time - bestTotalTime[j+1].addTime(r.getTotalRunningTime(r.FinishTime, true)); + bestTotalTime[j+1].addTime(r.getTotalRunningTime(r.FinishTime, true, true)); // Calculate best time from last radio to finish int ft = r.FinishTime - (rt + r.tStartTime); @@ -1368,7 +1380,7 @@ int oEvent::setupTimeLineEvents(vector &started, const vector< pair 0) { while (expIndex < expectedRadio.size() && expectedRadio[expIndex].time < radio[k].time) { TimeRunner &tr = expectedRadio[expIndex]; - int prelT = tr.runner->getTotalRunningTime(tr.time + pwTime, true); + int prelT = tr.runner->getTotalRunningTime(tr.time + pwTime, true, true); timeLinePrognose(results, tr, prelT, j, rname, rc[j].first); expIndex++; @@ -1379,7 +1391,7 @@ int oEvent::setupTimeLineEvents(vector &started, const vector< pair preRunners; int time = radio[k].time - r.tStartTime; - int totTime = r.getTotalRunningTime(radio[k].time, true); + int totTime = r.getTotalRunningTime(radio[k].time, true, true); insertResult(results, r, totTime, place, sharedPlace, preRunners); int leaderTime = results.begin()->first; int timeAfter = totTime - leaderTime; @@ -1453,7 +1465,7 @@ int oEvent::setupTimeLineEvents(vector &started, const vector< pairgetTotalRunningTime(tr.time + pwTime, true); + int prelT = tr.runner->getTotalRunningTime(tr.time + pwTime, true, true); timeLinePrognose(results, tr, prelT, 1, thelocation, 0); expIndex++; } @@ -1461,7 +1473,7 @@ int oEvent::setupTimeLineEvents(vector &started, const vector< pair preRunners; - int time = r.getTotalRunningTime(r.FinishTime, true); + int time = r.getTotalRunningTime(r.FinishTime, true, true); insertResult(results, r, time, place, sharedPlace, preRunners); @@ -1529,7 +1541,7 @@ int oEvent::setupTimeLineEvents(vector &started, const vector< pair 0) { place = results.size() + 1; - int rt = r.getTotalRunningTime(r.FinishTime, true); + int rt = r.getTotalRunningTime(r.FinishTime, true, true); if (rt > 0) { TempResultMap::iterator place_it = results.lower_bound(rt); place = place_it != results.end() ? place_it->second.place : results.size() + 1; @@ -1700,7 +1712,7 @@ void oEvent::getResultEvents(const set &classFilter, const set &punchF } if (!ok) { // A more careful analysis for(int k = 0; k < nr; k++) { - teamLegStatusOK[base + k] = it->getLegStatus(k, true); + teamLegStatusOK[base + k] = it->getLegStatus(k, true, true); } } } @@ -1709,8 +1721,10 @@ void oEvent::getResultEvents(const set &classFilter, const set &punchF const oRunner &r = *it; if (r.isRemoved() || !classFilter.count(r.getClassId(true))) continue; - if (r.prelStatusOK() || r.getStatus() != StatusUnknown) { - RunnerStatus stat = r.prelStatusOK() ? StatusOK : r.getStatus(); + if (r.getStatusComputed() != StatusOutOfCompetition && + r.getStatusComputed() != StatusNoTiming && + (r.prelStatusOK(true, false) || r.getStatusComputed() != StatusUnknown)) { + RunnerStatus stat = r.prelStatusOK(true, false) ? StatusOK : r.getStatusComputed(); results.push_back(ResultEvent(pRunner(&r), r.getFinishTime(), oPunch::PunchFinish, stat)); } pCard card = r.getCard(); diff --git a/code/oFreeImport.cpp b/code/oFreeImport.cpp index 8c91168..6949bcb 100644 --- a/code/oFreeImport.cpp +++ b/code/oFreeImport.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -1783,7 +1783,7 @@ void oFreeImport::addEntries(pEvent oe, const vector &entries) //sprintf_s(tname, "%s %d", entries[k].eClub.c_str(), ++teamno[id]); pTeam t=oe->addTeam(team, club ? club->getId() : 0, pc->getId()); if (t) { - t->setStartNo(t->getId(), false); + t->setStartNo(t->getId(), oBase::ChangeType::Update); for (int j=0;jaddRunner(entries[k].getName(j), entries[k].getClub(j), @@ -1793,7 +1793,8 @@ void oFreeImport::addEntries(pEvent oe, const vector &entries) t->setRunner(j, r, true); } - t->apply(true, 0, false); + t->apply(oBase::ChangeType::Update, nullptr); + t->synchronize(); } } } diff --git a/code/oFreeImport.h b/code/oFreeImport.h index 6ff6633..e768c98 100644 --- a/code/oFreeImport.h +++ b/code/oFreeImport.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/oFreePunch.cpp b/code/oFreePunch.cpp index 5a0123d..83cb78f 100644 --- a/code/oFreePunch.cpp +++ b/code/oFreePunch.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -146,10 +146,9 @@ bool oFreePunch::canRemove() const return true; } -Table *oEvent::getPunchesTB()//Table mode -{ - if (tables.count("punch") == 0) { - Table *table=new Table(this, 20, L"Stämplingar", "punches"); +const shared_ptr
    &oFreePunch::getTable(oEvent *oe) { + if (!oe->hasTable("punch")) { + auto table = make_shared
    (oe, 20, L"Stämplingar", "punches"); table->addColumn("Id", 70, true, true); table->addColumn("Ändrad", 150, false); table->addColumn("Bricka", 70, true); @@ -158,12 +157,10 @@ Table *oEvent::getPunchesTB()//Table mode table->addColumn("Löpare", 170, false); table->addColumn("Lag", 170, false); table->addColumn("Klass", 170, false); - tables["punch"] = table; - table->addOwnership(); + oe->setTable("punch", table); } - tables["punch"]->update(); - return tables["punch"]; + return oe->getTable("punch"); } void oEvent::generatePunchTableData(Table &table, oFreePunch *addPunch) @@ -206,8 +203,8 @@ void oFreePunch::addTableRow(Table &table) const { table.set(row++, it, TID_CLASSNAME, r ? r->getClass(true) : L"", false, cellEdit); } -bool oFreePunch::inputData(int id, const wstring &input, - int inputId, wstring &output, bool noUpdate) +pair oFreePunch::inputData(int id, const wstring &input, + int inputId, wstring &output, bool noUpdate) { synchronize(false); switch(id) { @@ -228,7 +225,7 @@ bool oFreePunch::inputData(int id, const wstring &input, synchronize(true); output = getType(); } - return false; + return make_pair(0, false); } void oFreePunch::fillInput(int id, vector< pair > &out, size_t &selected) @@ -486,7 +483,7 @@ pFreePunch oEvent::addFreePunch(int time, int type, int card, bool updateStartFi tr->synchronize(); if (type == startType) { if (tr->getClassRef(false) && !tr->getClassRef(true)->ignoreStartPunch()) - tr->setStartTime(time, true, false); + tr->setStartTime(time, true, ChangeType::Update); } else tr->setFinishTime(time); @@ -494,7 +491,7 @@ pFreePunch oEvent::addFreePunch(int time, int type, int card, bool updateStartFi // Direct result if (type == finishType && tr->getClassRef(false) && tr->getClassRef(true)->hasDirectResult()) { if (tr->getCourse(false) == 0 && tr->getCard() == 0) { - tr->setStatus(StatusOK, true, false, true); + tr->setStatus(StatusOK, true, oBase::ChangeType::Update, true); } else if (tr->getCourse(false) != 0 && tr->getCard() == 0) { pCard card = allocateCard(tr); diff --git a/code/oFreePunch.h b/code/oFreePunch.h index fcfaa46..0dd3ce2 100644 --- a/code/oFreePunch.h +++ b/code/oFreePunch.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -51,6 +51,8 @@ protected: public: + static const shared_ptr
    &getTable(oEvent *oe); + // Get control hash (itype) from course controld and race number static int getControlHash(int courseControlId, int race); @@ -70,8 +72,8 @@ public: // Get the runner currently tied to this punch pRunner getTiedRunner() const; void addTableRow(Table &table) const; - void fillInput(int id, vector< pair > &out, size_t &selected); - bool inputData(int id, const wstring &input, int inputId, wstring &output, bool noUpdate); + void fillInput(int id, vector< pair > &out, size_t &selected) override; + pair inputData(int id, const wstring &input, int inputId, wstring &output, bool noUpdate) override; void remove(); bool canRemove() const; diff --git a/code/oImportExport.cpp b/code/oImportExport.cpp index 107beec..bda9c67 100644 --- a/code/oImportExport.cpp +++ b/code/oImportExport.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -65,16 +65,17 @@ string conv_is(int i) return ""; } - int ConvertStatusToOE(int i) { switch(i) { case StatusOK: + case StatusNoTiming: return 0; case StatusDNS: // Ej start case StatusCANCEL: case StatusNotCompetiting: + case StatusOutOfCompetition: return 1; case StatusDNF: // Utg. return 2; @@ -207,10 +208,10 @@ bool oEvent::exportOECSV(const wchar_t *file, int languageTypeIndex, bool includ // Excel format HH:MM:SS - if (it->getRunningTime() > 0) - row[OEtime] = gdibase.recodeToNarrow(formatTimeHMS(it->getRunningTime())); + if (it->getRunningTime(true) > 0) + row[OEtime] = gdibase.recodeToNarrow(formatTimeHMS(it->getRunningTime(true))); - row[OEstatus] = conv_is(ConvertStatusToOE(it->getStatus())); + row[OEstatus] = conv_is(ConvertStatusToOE(it->getStatusComputed())); row[OEclubno] = conv_is(it->getClubId()); if (it->getClubRef()) { @@ -841,7 +842,7 @@ bool oEvent::addXMLTeamEntry(const xmlobject &xentry, int clubId) oTeam or(this); t = addTeam(or, true); } - t->setStartNo(Teams.size(), false); + t->setStartNo(Teams.size(), oBase::ChangeType::Update); } if (!t->hasFlag(oAbstractRunner::FlagUpdateName)) @@ -993,6 +994,7 @@ pRunner oEvent::addXMLEntry(const xmlobject &xentry, int clubId, bool setClass) if (setClass && !r->hasFlag(oAbstractRunner::FlagUpdateClass) ) r->Class = getXMLClass(xentry); + classIdToRunnerHash.reset(); r->Club = getClubCreate(clubId); @@ -1095,6 +1097,7 @@ pRunner oEvent::addXMLStart(const xmlobject &xstart, pClass cls) { pClass oldClass = r->Class; pClub oldClub = r->Club; r->Class = cls; + classIdToRunnerHash.reset(); xmlobject xclub = xstart.getObject("Club"); int clubId = xstart.getObjectInt("ClubId"); @@ -1362,12 +1365,14 @@ bool oEvent::addXMLEvent(const xmlobject &xevent) if (id>0) setExtIdentifier(id); - setName(name); + if (!hasFlag(TransferFlags::FlagManualName)) + setName(name, false); if (date) { wstring dateStr; date.getObjectString("Date", dateStr); - setDate(dateStr); + if (!hasFlag(TransferFlags::FlagManualDateTime)) + setDate(dateStr, false); } synchronize(); @@ -1636,7 +1641,8 @@ bool oEvent::addXMLClass(const xmlobject &xclass) else pc = addClass(name); - pc->setName(name); + if (!pc->hasFlag(oClass::TransferFlags::FlagManualName)) + pc->setName(name, false); oDataInterface DI=pc->getDI(); wstring tmp; @@ -2310,7 +2316,7 @@ void oEvent::exportIOFResults(xmlparser &xml, bool selfContained, const set xml.write("Clock", "clockFormat", hhmmss, formatTimeIOF(it->getLegFinishTime(-1), ZeroTime)); xml.endTag(); - xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(it->getLegRunningTime(-1, false), 0)); + xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(it->getLegRunningTime(-1, true, false), 0)); xml.write("ResultPosition", it->getLegPlaceS(-1, false)); xml.write("CompetitorStatus", "value", it->Runners[0]->getIOFStatusS()); @@ -2321,7 +2327,7 @@ void oEvent::exportIOFResults(xmlparser &xml, bool selfContained, const set if (pc) xml.write("CourseLength", "unit", L"m", pc->getLengthS()); pCourse pcourse=pc; - auto legStatus = it->getLegStatus(-1, false); + auto legStatus = it->getLegStatus(-1, true, false); if (pcourse && legStatus>0 && legStatus!=StatusDNS && legStatus!=StatusCANCEL) { int no = 1; bool hasRogaining = pcourse->hasRogaining(); @@ -2416,7 +2422,7 @@ void oEvent::exportIOFResults(xmlparser &xml, bool selfContained, const set xml.write("Clock", "clockFormat", hhmmss, formatTimeIOF(it->getFinishTimeAdjusted(), ZeroTime)); xml.endTag(); - xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(it->getRunningTime(),0)); + xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(it->getRunningTime(true),0)); xml.write("ResultPosition", it->getPlaceS()); xml.write("CompetitorStatus", "value", it->getIOFStatusS()); @@ -2538,7 +2544,7 @@ void oEvent::exportTeamSplits(xmlparser &xml, const set &classes, bool oldS xml.write("Clock", "clockFormat", hhmmss, formatTimeIOF(it->getFinishTimeAdjusted(), ZeroTime)); xml.endTag(); - xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(it->getRunningTime(), 0)); + xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(it->getRunningTime(true), 0)); xml.write("ResultPosition", it->getPlaceS()); xml.write("TeamStatus", "value", it->getIOFStatusS()); @@ -2563,7 +2569,7 @@ void oEvent::exportTeamSplits(xmlparser &xml, const set &classes, bool oldS xml.write("Clock", "clockFormat", hhmmss, formatTimeIOF(r->getFinishTimeAdjusted(), ZeroTime)); xml.endTag(); - xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(r->getRunningTime(), 0)); + xml.write("Time", "timeFormat", hhmmss, formatTimeIOF(r->getRunningTime(true), 0)); xml.write("ResultPosition", r->getPlaceS()); xml.write("CompetitorStatus", "value", r->getIOFStatusS()); @@ -2621,25 +2627,17 @@ void oEvent::exportIOFSplits(IOFVersion version, const wchar_t *file, xml.openOutput(file, false); oClass::initClassId(*this); - reEvaluateAll(set(), true); + reEvaluateAll(classes, true); if (version != IOF20) - calculateResults(set(), ResultType::ClassCourseResult); - calculateResults(set(), ResultType::TotalResult); - calculateResults(set(), ResultType::ClassResult); - calculateTeamResults(true); - calculateTeamResults(false); + calculateResults(classes, ResultType::ClassCourseResult); + calculateResults(classes, ResultType::TotalResult); + calculateResults(classes, ResultType::ClassResult); + calculateTeamResults(classes, ResultType::TotalResult); + calculateTeamResults(classes, ResultType::ClassResult); + sortRunners(SortOrder::ClassResult); sortTeams(SortOrder::ClassResult, -1, false); - set rgClasses; - for (int clz : classes) { - pClass pc = getClass(clz); - if (pc && pc->isRogaining()) - rgClasses.insert(clz); - } - if (!rgClasses.empty()) - calculateRogainingResults(rgClasses); - if (version == IOF20) exportIOFResults(xml, true, classes, leg, oldStylePatrolExport); else { diff --git a/code/oListInfo.cpp b/code/oListInfo.cpp index 8819608..5d6ea47 100644 --- a/code/oListInfo.cpp +++ b/code/oListInfo.cpp @@ -1,6 +1,6 @@ /********************i**************************************************** MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -55,6 +55,7 @@ oListInfo::oListInfo() { rogainingResults = false; calculateLiveResults = false; calcCourseClassResults = false; + calcCourseResults = false; listPostFilter.resize(_EFilterMax+1, 0); listPostSubFilter.resize(_ESubFilterMax+1, 0); fixedType = false; @@ -80,6 +81,20 @@ oListInfo::oListInfo() { oListInfo::~oListInfo(void) { } +void oListInfo::replaceType(EPostType find, EPostType replace, bool onlyFirst) { + for (auto blp : { &Head, &subHead, &listPost, &subListPost }) { + for (auto &pp : *blp) { + if (pp.type == replace && onlyFirst) + return; + if (pp.type == find) { + pp.type = replace; + if (onlyFirst) + return; + } + } + } +} + wstring oListParam::getContentsDescriptor(const oEvent &oe) const { wstring cls; vector classes; @@ -378,6 +393,7 @@ int oListInfo::getMaxCharWidth(const oEvent *oe, case lTeamPlaceDiff: case lRunnerTotalPlace: case lRunnerClassCoursePlace: + case lRunnerCoursePlace: case lTeamPlace: case lTeamTotalPlace: case lPunchControlPlace: @@ -996,6 +1012,12 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara const wstring *wsptr=0; wbf[0]=0; + auto noTimingRunner = [&]() { + return (pc ? pc->getNoTiming() : false) || (r ? r->getStatusComputed() == StatusNoTiming : false); + }; + auto noTimingTeam = [&]() { + return (pc ? pc->getNoTiming() : false) || (t ? t->getStatusComputed() == StatusNoTiming : false); + }; bool invalidClass = pc && pc->getClassStatus() != oClass::Normal; int legIndex = pp.legIndex; if(pc && pp.type != lResultModuleNumber && pp.type != lResultModuleNumberTeam @@ -1222,7 +1244,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lRunnerTime: if (r && !invalidClass) { if (pp.resultModuleIndex == -1) - wsptr = &r->getRunningTimeS(); + wsptr = &r->getRunningTimeS(true); else wsptr = &r->getTempResult(pp.resultModuleIndex).getRunningTimeS(0); @@ -1234,7 +1256,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara break; case lRunnerGrossTime: if (r && !invalidClass) { - int tm = r->getRunningTime(); + int tm = r->getRunningTime(true); if (tm > 0) tm -= r->getTimeAdjustment(); @@ -1247,29 +1269,37 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara if (invalidClass) wsptr = &lang.tl("Struken"); else if (pp.resultModuleIndex == -1) { - bool ok = r->prelStatusOK(); - if (ok && pc && !pc->getNoTiming()) { - wsptr = &r->getRunningTimeS(); + bool ok = r->prelStatusOK(true, true); + if (ok && !noTimingRunner()) { + wsptr = &r->getRunningTimeS(true); if (r->getNumShortening() > 0) { swprintf_s(wbf, L"*%s", wsptr->c_str()); wsptr = 0; } + else if (r->getStatusComputed() == StatusOutOfCompetition) { + swprintf_s(wbf, L"(%s)", wsptr->c_str()); + wsptr = 0; + } } else { if (ok) wsptr = &formatStatus(StatusOK, true); else - wsptr = &r->getStatusS(true); + wsptr = &r->getStatusS(true, true); } } else { const oAbstractRunner::TempResult &res = r->getTempResult(pp.resultModuleIndex); - if (res.getStatus() == StatusOK && pc && !pc->getNoTiming()) { + if (res.isStatusOK() && !noTimingRunner()) { wsptr = &res.getRunningTimeS(0); if (r->getNumShortening() > 0) { swprintf_s(wbf, L"*%s", wsptr->c_str()); wsptr = 0; } + else if (res.getStatus() == StatusOutOfCompetition) { + swprintf_s(wbf, L"(%s)", wsptr->c_str()); + wsptr = 0; + } } else wsptr = &res.getStatusS(StatusOK); @@ -1282,8 +1312,8 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara if (invalidClass) wsptr = &lang.tl("Struken"); else if (pp.resultModuleIndex == -1) { - if (r->prelStatusOK() && pc && !pc->getNoTiming()) { - wstring timeStatus = r->getRunningTimeS(); + if (r->prelStatusOK(true, true) && !noTimingRunner()) { + wstring timeStatus = r->getRunningTimeS(true); if (r->hasInputData() || (r->getLegNumber() > 0 && !r->isPatrolMember())) { RunnerStatus ts = r->getTotalStatus(); @@ -1306,16 +1336,20 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara } } else - wsptr = &r->getStatusS(true); + wsptr = &r->getStatusS(true, true); } else { const oAbstractRunner::TempResult &res = r->getTempResult(pp.resultModuleIndex); - if (res.getStatus() == StatusOK && pc && !pc->getNoTiming()) { + if (res.isStatusOK() && !noTimingRunner()) { wsptr = &res.getRunningTimeS(0); if (r->getNumShortening() > 0) { swprintf_s(wbf, L"*%s", wsptr->c_str()); wsptr = 0; } + else if (res.getStatus() == StatusOutOfCompetition) { + swprintf_s(wbf, L"(%s)", wsptr->c_str()); + wsptr = 0; + } } else wsptr = &res.getStatusS(StatusOK); @@ -1326,14 +1360,14 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara break; case lRunnerGeneralTimeAfter: - if (r && pc && !invalidClass && !pc->getNoTiming()) { + if (r && pc && !invalidClass && !noTimingRunner()) { if (pp.resultModuleIndex == -1) { if (r->hasInputData() || (r->getLegNumber() > 0 && !r->isPatrolMember())) { int tleg = r->tLeg >= 0 ? r->tLeg:0; if (r->getTotalStatus()==StatusOK) { if ( (t && t->getNumShortening(tleg) == 0) || (!t && r->getNumShortening() == 0)) { - int after = r->getTotalRunningTime() - pc->getTotalLegLeaderTime(tleg, true); + int after = r->getTotalRunningTime() - pc->getTotalLegLeaderTime(tleg, true, true); if (after > 0) swprintf_s(wbf, L"+%d:%02d", after/60, after%60); } @@ -1344,9 +1378,9 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara } else { int tleg=r->tLeg>=0 ? r->tLeg:0; - if (r->tStatus==StatusOK && pc && !pc->getNoTiming() ) { + if (r->tStatus==StatusOK && pc && !noTimingRunner()) { if (r->getNumShortening() == 0) { - int after = r->getRunningTime() - pc->getBestLegTime(tleg); + int after = r->getRunningTime(true) - pc->getBestLegTime(tleg, true); if (after > 0) swprintf_s(wbf, L"+%d:%02d", after/60, after%60); } @@ -1366,10 +1400,10 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lRunnerTimePerKM: - if (r && !invalidClass && r->prelStatusOK()) { + if (r && !invalidClass && r->prelStatusOK(true, true)) { const pCourse pc = r->getCourse(false); if (pc) { - int t = r->getRunningTime(); + int t = r->getRunningTime(false); int len = pc->getLength(); if (len > 0 && t > 0) { int sperkm = (1000 * t) / len; @@ -1392,7 +1426,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara else if (r) { if (pp.resultModuleIndex == -1) { if ((r->getTotalStatus()==StatusOK || (r->getTotalStatus()==StatusUnknown - && r->prelStatusOK() && r->getInputStatus() == StatusOK) )&& pc && !pc->getNoTiming()) { + && r->prelStatusOK(true, true) && r->getInputStatus() == StatusOK) ) && !noTimingRunner()) { wsptr = &r->getTotalRunningTimeS(); if (r->getNumShortening() > 0) { swprintf_s(wbf, L"*%s", wsptr->c_str()); @@ -1405,7 +1439,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara else { const oAbstractRunner::TempResult &res = r->getTempResult(pp.resultModuleIndex); RunnerStatus input = r->getTotalStatusInput(); - if (input == StatusOK && res.getStatus() == StatusOK && pc && !pc->getNoTiming()) { + if (input == StatusOK && res.getStatus() == StatusOK && !noTimingRunner()) { wsptr = &res.getRunningTimeS(r->getTotalTimeInput()); if (r->getNumShortening() > 0) { swprintf_s(wbf, L"*%s", wsptr->c_str()); @@ -1421,14 +1455,14 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara if (invalidClass) wsptr = &lang.tl("Struken"); else if (r) { - if (r->tempStatus==StatusOK && pc && !pc->getNoTiming()) + if (r->tempStatus==StatusOK && !noTimingRunner()) wcscpy_s(wbf, formatTime(r->tempRT).c_str()); else wcscpy_s(wbf, formatStatus(r->tempStatus, true).c_str() ); } break; case lRunnerPlace: - if (r && !invalidClass && pc && !pc->getNoTiming()) { + if (r && !invalidClass && !noTimingRunner()) { if (pp.resultModuleIndex == -1) wcscpy_s(wbf, r->getPrintPlaceS(pp.text.empty()).c_str() ); else @@ -1436,12 +1470,12 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara } break; case lRunnerTotalPlace: - if (r && !invalidClass && pc && !pc->getNoTiming()) + if (r && !invalidClass && !noTimingRunner()) wcscpy_s(wbf, r->getPrintTotalPlaceS(pp.text.empty()).c_str() ); break; case lRunnerGeneralPlace: - if (r && !invalidClass && pc && !pc->getNoTiming()) { + if (r && !invalidClass && pc && !noTimingRunner()) { if (pp.resultModuleIndex == -1) { if (r->hasInputData() || (r->getLegNumber() > 0 && !r->isPatrolMember())) { wstring iPlace; @@ -1468,14 +1502,22 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara break; case lRunnerClassCoursePlace: - if (r && !invalidClass && pc && !pc->getNoTiming()) { - int p = r->getCoursePlace(); + if (r && !invalidClass && !noTimingRunner()) { + int p = r->getCoursePlace(true); + if (p>0 && p<10000) + swprintf_s(wbf, L"%d.", p); + } + break; + + case lRunnerCoursePlace: + if (r && !invalidClass && !noTimingRunner()) { + int p = r->getCoursePlace(false); if (p>0 && p<10000) swprintf_s(wbf, L"%d.", p); } break; case lRunnerPlaceDiff: - if (r && !invalidClass && pc && !pc->getNoTiming()) { + if (r && !invalidClass && !noTimingRunner()) { int p = r->getTotalPlace(); if (r->getTotalStatus() == StatusOK && p > 0 && r->inputPlace>0) { int pd = p - r->inputPlace; @@ -1489,21 +1531,21 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lRunnerTimeAfterDiff: if (r && pc && !invalidClass) { int tleg = r->tLeg >= 0 ? r->tLeg:0; - if (r->getTotalStatus()==StatusOK && pc && !pc->getNoTiming()) { - int after = r->getTotalRunningTime() - pc->getTotalLegLeaderTime(tleg, true); + if (r->getTotalStatus() == StatusOK && pc && !noTimingRunner()) { + int after = r->getTotalRunningTime() - pc->getTotalLegLeaderTime(tleg, true, true); int afterOld = r->inputTime - pc->getBestInputTime(tleg); int ad = after - afterOld; if (ad > 0) - swprintf_s(wbf, L"+%d:%02d", ad/60, ad%60); + swprintf_s(wbf, L"+%d:%02d", ad / 60, ad % 60); if (ad < 0) - swprintf_s(wbf, L"-%d:%02d", (-ad)/60, (-ad)%60); + swprintf_s(wbf, L"-%d:%02d", (-ad) / 60, (-ad) % 60); } } break; case lRunnerRogainingPoint: if (r && !invalidClass) { if (pp.resultModuleIndex == -1) - swprintf_s(wbf, L"%d", r->getRogainingPoints(false)); + swprintf_s(wbf, L"%d", r->getRogainingPoints(true, false)); else swprintf_s(wbf, L"%d", r->getTempResult(pp.resultModuleIndex).getPoints()); } @@ -1512,7 +1554,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lRunnerRogainingPointTotal: if (r && !invalidClass) { if (pp.resultModuleIndex == -1) - swprintf_s(wbf, L"%d", r->getRogainingPoints(true)); + swprintf_s(wbf, L"%d", r->getRogainingPoints(true, true)); else swprintf_s(wbf, L"%d", r->getTempResult(pp.resultModuleIndex).getPoints() + r->getInputPoints()); } @@ -1520,7 +1562,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lRunnerRogainingPointReduction: if (r && !invalidClass) { - int red = r->getRogainingReduction(); + int red = r->getRogainingReduction(true); if (red > 0) swprintf_s(wbf, L"-%d", red); } @@ -1528,7 +1570,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lRunnerRogainingPointGross: if (r && !invalidClass) { - int p = r->getRogainingPointsGross(); + int p = r->getRogainingPointsGross(true); wsptr = &itow(p); } break; @@ -1551,20 +1593,20 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara break; case lRunnerRogainingPointOvertime: if (r && !invalidClass) { - int over = r->getRogainingOvertime(); + int over = r->getRogainingOvertime(true); if (over > 0) wsptr = &formatTime(over); } break; case lRunnerTimeAfter: - if (r && pc && !invalidClass && !pc->getNoTiming()) { + if (r && pc && !invalidClass && !noTimingRunner()) { int after = 0; if (pp.resultModuleIndex == -1) { int tleg=r->tLeg>=0 ? r->tLeg:0; - int brt = pc->getBestLegTime(tleg); - if (r->prelStatusOK() && brt > 0) { - after=r->getRunningTime() - brt; + int brt = pc->getBestLegTime(tleg, true); + if (r->prelStatusOK(true, true) && brt > 0) { + after=r->getRunningTime(true) - brt; } } else { @@ -1583,9 +1625,9 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lRunnerTotalTimeAfter: if (r && pc && !invalidClass) { int tleg = r->tLeg >= 0 ? r->tLeg:0; - if (r->getTotalStatus()==StatusOK && pc && !pc->getNoTiming()) { + if (r->getTotalStatus()==StatusOK && pc && !noTimingRunner()) { if ( (t && t->getNumShortening(tleg) == 0) || (!t && r->getNumShortening() == 0)) { - int after = r->getTotalRunningTime() - pc->getTotalLegLeaderTime(tleg, true); + int after = r->getTotalRunningTime() - pc->getTotalLegLeaderTime(tleg, true, true); if (after > 0) swprintf_s(wbf, L"+%d:%02d", after/60, after%60); } @@ -1598,8 +1640,8 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lRunnerClassCourseTimeAfter: if (r && pc && !invalidClass) { pCourse crs = r->getCourse(false); - if (crs && r->tStatus==StatusOK && !pc->getNoTiming()) { - int after = r->getRunningTime() - pc->getBestTimeCourse(crs->getId()); + if (crs && r->tStatus==StatusOK && !noTimingRunner()) { + int after = r->getRunningTime(true) - pc->getBestTimeCourse(crs->getId()); if (after > 0) swprintf_s(wbf, L"+%d:%02d", after/60, after%60); } @@ -1625,13 +1667,13 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara wcscpy_s(wbf, par.getLegName().c_str()); break; case lRunnerLostTime: - if (r && r->tStatus == StatusOK && pc && !pc->getNoTiming() && !invalidClass) { + if (r && r->prelStatusOK(true, true) && !noTimingRunner() && !invalidClass) { wcscpy_s(wbf, r->getMissedTimeS().c_str()); } break; case lRunnerTempTimeAfter: if (r && pc) { - if (r->tempStatus==StatusOK && pc && !pc->getNoTiming() + if (r->tempStatus==StatusOK && pc && !noTimingRunner() && r->tempRT>pc->tLegLeaderTime) { int after=r->tempRT-pc->tLegLeaderTime; swprintf_s(wbf, L"+%d:%02d", after/60, after%60); @@ -1783,7 +1825,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lTeamStatus: if (t && !invalidClass) { if (pp.resultModuleIndex == -1) - wsptr = &t->getLegStatusS(legIndex, false); + wsptr = &t->getLegStatusS(legIndex, true, false); else wsptr = &t->getTempResult(pp.resultModuleIndex).getStatusS(StatusOK); } @@ -1791,7 +1833,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lTeamTime: if (t && !invalidClass) { if (pp.resultModuleIndex == -1) - wcscpy_s(wbf, t->getLegRunningTimeS(legIndex, false).c_str() ); + wcscpy_s(wbf, t->getLegRunningTimeS(legIndex, true, false).c_str() ); else wsptr = &t->getTempResult(pp.resultModuleIndex).getRunningTimeS(0); } @@ -1807,11 +1849,15 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara wsptr = &lang.tl("Struken"); else if (t) { if (pp.resultModuleIndex == -1) { - RunnerStatus st = t->getLegStatus(legIndex, false); - if (st==StatusOK || (st==StatusUnknown && t->getLegRunningTime(legIndex, false)>0)) - wcscpy_s(wbf, t->getLegRunningTimeS(legIndex, false).c_str()); + RunnerStatus st = t->getLegStatus(legIndex, true, false); + if (st == StatusOK || ((st == StatusUnknown || st == StatusOutOfCompetition) && t->getLegRunningTime(legIndex, true, false) > 0)) { + if (st != StatusOutOfCompetition) + wcscpy_s(wbf, t->getLegRunningTimeS(legIndex, true, false).c_str()); + else + swprintf_s(wbf, L"(%s)", t->getLegRunningTimeS(legIndex, true, false).c_str()); + } else - wsptr = &t->getLegStatusS(legIndex, false); + wsptr = &t->getLegStatusS(legIndex, true, false); } else { RunnerStatus st = t->getTempResult(pp.resultModuleIndex).getStatus(); @@ -1830,7 +1876,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lTeamRogainingPoint: if (t && !invalidClass) { if (pp.resultModuleIndex == -1) - swprintf_s(wbf, L"%d", t->getRogainingPoints(false)); + swprintf_s(wbf, L"%d", t->getRogainingPoints(true, false)); else swprintf_s(wbf, L"%d", t->getTempResult(pp.resultModuleIndex).getPoints()); } @@ -1838,7 +1884,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lTeamRogainingPointTotal: if (t && !invalidClass) { if (pp.resultModuleIndex == -1) - swprintf_s(wbf, L"%d", t->getRogainingPoints(true)); + swprintf_s(wbf, L"%d", t->getRogainingPoints(true, true)); else swprintf_s(wbf, L"%d", t->getTempResult(pp.resultModuleIndex).getPoints() + t->getInputPoints()); } @@ -1846,7 +1892,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lTeamRogainingPointReduction: if (t && !invalidClass) { - int red = t->getRogainingReduction(); + int red = t->getRogainingReduction(true); if (red > 0) swprintf_s(wbf, L"-%d", red); } @@ -1854,7 +1900,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lTeamRogainingPointOvertime: if (t && !invalidClass) { - int over = t->getRogainingOvertime(); + int over = t->getRogainingOvertime(true); if (over > 0) wsptr = &formatTime(over); } @@ -1881,7 +1927,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lTeamTimeAfter: if (t && !invalidClass) { if (pp.resultModuleIndex == -1) { - if (t->getLegStatus(legIndex, false)==StatusOK) { + if (t->getLegStatus(legIndex, true, false)==StatusOK) { if (t->getNumShortening(legIndex) == 0) { int ta=t->getTimeAfter(legIndex); if (ta>0) @@ -1902,7 +1948,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara } break; case lTeamPlace: - if (t && !invalidClass && pc && !pc->getNoTiming()) { + if (t && !invalidClass && !noTimingTeam()) { if (pp.resultModuleIndex == -1) { wcscpy_s(wbf, t->getLegPrintPlaceS(legIndex, false, pp.text.empty()).c_str()); } @@ -1916,16 +1962,19 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara wsptr = &lang.tl("Struken"); else if (t) { int ix = r ? r->getLegNumber() : counter.level3; - if (t->getLegStatus(ix, false)==StatusOK) - wcscpy_s(wbf, t->getLegRunningTimeS(ix, false).c_str() ); + RunnerStatus st = t->getLegStatus(ix, true, false); + if (st == StatusOK) + wcscpy_s(wbf, t->getLegRunningTimeS(ix, true, false).c_str() ); + else if (st == StatusOutOfCompetition && t->getLegRunningTime(ix, true, false) > 0) + swprintf_s(wbf, L"(%s)", t->getLegRunningTimeS(ix, true, false).c_str()); else - wcscpy_s(wbf, t->getLegStatusS(ix, false).c_str() ); + wcscpy_s(wbf, t->getLegStatusS(ix, true, false).c_str() ); } break; case lTeamLegTimeAfter: if (t) { int ix = r ? r->getLegNumber() : counter.level3; - if (t->getLegStatus(ix, false)==StatusOK && !invalidClass) { + if (t->getLegStatus(ix, true, false)==StatusOK && !invalidClass) { if (t->getNumShortening(ix) == 0) { int ta=t->getTimeAfter(ix); if (ta>0) @@ -1957,17 +2006,17 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara } break; case lTeamTotalTime: - if (t && !invalidClass) wcscpy_s(wbf, t->getLegRunningTimeS(legIndex, true).c_str() ); + if (t && !invalidClass) wcscpy_s(wbf, t->getLegRunningTimeS(legIndex, true, true).c_str() ); break; case lTeamTotalTimeStatus: if (invalidClass) wsptr = &lang.tl("Struken"); else if (t) { if (pp.resultModuleIndex == -1) { - if (t->getLegStatus(legIndex, true)==StatusOK) - wcscpy_s(wbf, t->getLegRunningTimeS(legIndex, true).c_str() ); + if (t->getLegStatus(legIndex, true, true)==StatusOK) + wcscpy_s(wbf, t->getLegRunningTimeS(legIndex, true, true).c_str() ); else - wcscpy_s(wbf, t->getLegStatusS(legIndex, true).c_str() ); + wcscpy_s(wbf, t->getLegStatusS(legIndex, true, true).c_str() ); } else { RunnerStatus st = t->getTempResult(pp.resultModuleIndex).getStatus(); @@ -1997,15 +2046,15 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara } break; case lTeamTotalPlace: - if (t && !invalidClass && pc && !pc->getNoTiming()) wcscpy_s(wbf, t->getPrintTotalPlaceS(pp.text.empty()).c_str() ); + if (t && !invalidClass && !noTimingTeam()) wcscpy_s(wbf, t->getPrintTotalPlaceS(pp.text.empty()).c_str() ); break; break; case lTeamTotalTimeAfter: if (t && pc && !invalidClass) { int tleg = t->getNumRunners() - 1; - if (t->getTotalStatus()==StatusOK && pc && !pc->getNoTiming()) { - int after = t->getTotalRunningTime() - pc->getTotalLegLeaderTime(tleg, true); + if (t->getTotalStatus()==StatusOK && pc && !noTimingTeam()) { + int after = t->getTotalRunningTime() - pc->getTotalLegLeaderTime(tleg, true, true); if (after > 0) swprintf_s(wbf, L"+%d:%02d", after/60, after%60); } @@ -2014,8 +2063,8 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara case lTeamTotalTimeDiff: if (t && pc && !invalidClass) { int tleg = t->getNumRunners() - 1; - if (t->getTotalStatus()==StatusOK && pc && !pc->getNoTiming()) { - int after = t->getTotalRunningTime() - pc->getTotalLegLeaderTime(tleg, true); + if (t->getTotalStatus()==StatusOK && pc && !noTimingTeam()) { + int after = t->getTotalRunningTime() - pc->getTotalLegLeaderTime(tleg, true, true); int afterOld = t->inputTime - pc->getBestInputTime(tleg); int ad = after - afterOld; if (ad > 0) @@ -2415,6 +2464,8 @@ void oEvent::listGeneratePunches(const oListInfo &listInfo, gdioutput &gdi, if (cls && cls->getNoTiming()) return; + if (r && r->getStatusComputed() == StatusNoTiming) + return; int h = gdi.getLineHeight(); int w = 0; @@ -2558,12 +2609,8 @@ void oEvent::generateList(gdioutput &gdi, bool reEvaluate, const oListInfo &li, oe->calcUseStartSeconds(); oe->calculateNumRemainingMaps(false); oe->updateComputerTime(); - vector< pair > > tagNameList; - oe->getGeneralResults(false, tagNameList, false); - wstring src; - for (size_t k = 0; k < tagNameList.size(); k++) - oe->getGeneralResult(tagNameList[k].second.first, src).setContext(&li.lp); - + oe->setGeneralResultContext(&li.lp); + wstring listname; if (!li.Head.empty()) { oCounter counter; @@ -2608,9 +2655,8 @@ void oEvent::generateList(gdioutput &gdi, bool reEvaluate, const oListInfo &li, generateListInternal(gdi, *it, interHead); } - - for (size_t k = 0; k < tagNameList.size(); k++) - oe->getGeneralResult(tagNameList[k].second.first, src).clearContext(); + // Reset context + oe->setGeneralResultContext(nullptr); gdi.setListDescription(listname); if (updateScrollBars) @@ -2627,6 +2673,29 @@ bool oListInfo::filterRunner(const oRunner &r) const { if (!lp.matchLegNumber(r.getClassRef(false), r.legToRun())) return true; + if (lp.ageFilter != oListParam::AgeFilter::All) { + int age = r.getBirthAge(); + if (age > 0) { + oDataConstInterface odc = r.getEvent()->getDCI(); + int lowAgeLimit = odc.getInt("YouthAge"); + //int highAgeLimit = odc.getInt("SeniorAge"); + + + if (lp.ageFilter == oListParam::AgeFilter::ExludeYouth && + age <= lowAgeLimit) + return true; + + if (lp.ageFilter == oListParam::AgeFilter::OnlyYouth && + age > lowAgeLimit) + return true; + } + else { + // Consider "normal" + if (lp.ageFilter != oListParam::AgeFilter::ExludeYouth) + return true; + } + } + if (filter(EFilterExcludeDNS)) { if (r.tStatus == StatusDNS) return true; @@ -2680,7 +2749,7 @@ bool oListInfo::filterRunnerResult(GeneralResult *gResult, const oRunner &r) con if (filter(EFilterHasResult)) { if (gResult == 0) { - if (lp.useControlIdResultTo <= 0 && r.tStatus == StatusUnknown) + if (lp.useControlIdResultTo <= 0 && !r.hasResult()) return true; else if ((lp.useControlIdResultTo > 0 || lp.useControlIdResultFrom > 0) && r.tempStatus != StatusOK) return true; @@ -2690,13 +2759,13 @@ bool oListInfo::filterRunnerResult(GeneralResult *gResult, const oRunner &r) con else { auto &res = r.getTempResult(0); RunnerStatus st = res.getStatus(); - if (st == StatusUnknown) + if (st == StatusUnknown || isPossibleResultStatus(st) && r.getRunningTime(false) <= 0) return true; } } else if (filter(EFilterHasPrelResult)) { if (gResult == 0) { - if (lp.useControlIdResultTo <= 0 && r.tStatus == StatusUnknown && r.getRunningTime() <= 0) + if (lp.useControlIdResultTo <= 0 && (r.tStatus == StatusUnknown || isPossibleResultStatus(r.getStatusComputed())) && r.getRunningTime(false) <= 0) return true; else if ((lp.useControlIdResultTo > 0 || lp.useControlIdResultFrom > 0) && r.tempStatus != StatusOK) return true; @@ -2707,7 +2776,7 @@ bool oListInfo::filterRunnerResult(GeneralResult *gResult, const oRunner &r) con auto &res = r.getTempResult(0); int rt = res.getRunningTime(); RunnerStatus st = res.getStatus(); - if (st == StatusUnknown && rt <= 0) + if ((st == StatusUnknown || isPossibleResultStatus(st)) && rt <= 0) return true; } } @@ -2715,12 +2784,12 @@ bool oListInfo::filterRunnerResult(GeneralResult *gResult, const oRunner &r) con } GeneralResult *oListInfo::applyResultModule(oEvent &oe, vector &rlist) const { - GeneralResult *gResult = 0; + GeneralResult *gResult = nullptr; if (!resultModule.empty()) { wstring src; oListInfo::ResultType resType = getResultType(); - gResult = &oe.getGeneralResult(resultModule, src); - gResult->calculateIndividualResults(rlist, resType, sortOrder == Custom, getParam().getInputNumber()); + gResult = oe.getGeneralResult(resultModule, src).get(); + gResult->calculateIndividualResults(rlist, false, resType, sortOrder == Custom, getParam().getInputNumber()); if (sortOrder == SortByFinishTime || sortOrder == SortByFinishTimeReverse || sortOrder == SortByStartTime) gResult->sort(rlist, sortOrder); @@ -2737,6 +2806,62 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form if (!sampleClass && !Classes.empty()) sampleClass = &*Classes.begin(); + if (li.listType == li.EBaseTypeRunner) { + if (li.calculateLiveResults || li.sortOrder == SortOrder::ClassLiveResult) + calculateResults(li.lp.selection, ResultType::PreliminarySplitResults); + + if (li.calcCourseClassResults) + calculateResults(li.lp.selection, ResultType::ClassCourseResult); + + if (li.calcCourseResults) + calculateResults(li.lp.selection, ResultType::CourseResult); + + if (li.calcTotalResults) { + calculateTeamResults(li.lp.selection, ResultType::TotalResult); + calculateResults(li.lp.selection, ResultType::TotalResult); + } + + if (li.calcResults) { + if (li.lp.useControlIdResultTo > 0 || li.lp.useControlIdResultFrom > 0) + calculateSplitResults(li.lp.useControlIdResultFrom, li.lp.useControlIdResultTo); + else { + calculateTeamResults(li.lp.selection, ResultType::ClassResult); + calculateResults(li.lp.selection, ResultType::ClassResult); + } + } + } + else if (li.listType == li.EBaseTypeTeam) { + if (li.calcResults) + calculateTeamResults(li.lp.selection, ResultType::ClassResult); + if (li.calcTotalResults) + calculateTeamResults(li.lp.selection, ResultType::TotalResult); + if (li.calcCourseResults) + calculateTeamResults(li.lp.selection, ResultType::CourseResult); + + if (li.calcCourseClassResults) + calculateResults(li.lp.selection, ResultType::ClassCourseResult); + } + else if (li.listType == li.EBaseTypeClub) { + if (li.calcResults) { + calculateTeamResults(li.lp.selection, ResultType::TotalResult); + calculateTeamResults(li.lp.selection, ResultType::ClassResult); + } + if (li.calcCourseClassResults) + calculateResults(li.lp.selection, ResultType::ClassCourseResult); + if (li.calcCourseResults) + calculateResults(li.lp.selection, ResultType::CourseResult); + + //pair info = li.lp.getLegInfo(sampleClass); + //sortTeams(li.sortOrder, info.first, info.second); + if (li.calcResults) { + if (li.lp.useControlIdResultTo > 0 || li.lp.useControlIdResultFrom > 0) + calculateSplitResults(li.lp.useControlIdResultFrom, li.lp.useControlIdResultTo); + else { + calculateResults(li.lp.selection, ResultType::ClassResult); + } + } + } + PrintPostInfo printPostInfo(gdi, li.lp); //oCounter counter; //Render header @@ -2772,60 +2897,31 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form } // Apply for all teams (calculate start times etc.) + + vector tlist; + tlist.reserve(Teams.size()); for (oTeamList::iterator it = Teams.begin(); it != Teams.end(); ++it) { if (it->isRemoved() || it->tStatus == StatusNotCompetiting) continue; - if (!li.lp.selection.empty() && li.lp.selection.count(it->getClassId(false)) == 0) + if (!li.lp.selection.empty() && li.lp.selection.count(it->getClassId(true)) == 0) continue; - it->apply(false, 0, true); + it->apply(oBase::ChangeType::Quiet, nullptr); + tlist.push_back(&*it); } wstring oldKey; if (li.listType == li.EBaseTypeRunner) { - - if (li.calculateLiveResults || li.sortOrder == SortOrder::ClassLiveResult) - calculateResults(li.lp.selection, ResultType::PreliminarySplitResults); - - if (li.calcCourseClassResults) - calculateResults(li.lp.selection, ResultType::ClassCourseResult); - - if (li.calcTotalResults) { - if (li.calcResults) { - calculateResults(li.lp.selection, ResultType::ClassResult); - calculateTeamResults(false); - } - - calculateTeamResults(true); - sortRunners(li.sortOrder); - calculateResults(li.lp.selection, ResultType::TotalResult); + vector rlist, rlistInput; + getRunners(li.lp.selection, rlistInput, false); + rlist.reserve(rlistInput.size()); + for (auto r : rlistInput) { + if (!li.filterRunner(*r)) + rlist.push_back(r); } - else if (li.calcResults) { - if (li.rogainingResults) { - sortRunners(li.sortOrder); - calculateRogainingResults(li.lp.selection); - } - else if (li.lp.useControlIdResultTo > 0 || li.lp.useControlIdResultFrom > 0) - calculateSplitResults(li.lp.useControlIdResultFrom, li.lp.useControlIdResultTo); - else if (li.sortOrder == CourseResult) { - sortRunners(li.sortOrder); - calculateResults(li.lp.selection, ResultType::CourseResult); - } - else { - calculateTeamResults(false); - sortRunners(li.sortOrder); - calculateResults(li.lp.selection, ResultType::ClassResult); - } - } - else - sortRunners(li.sortOrder); - vector rlist; - rlist.reserve(Runners.size()); - for (auto &r : Runners) { - if (!li.filterRunner(r)) - rlist.push_back(&r); - } + if (li.sortOrder != Custom) + sortRunners(li.sortOrder, rlist); GeneralResult *gResult = li.applyResultModule(*this, rlist); @@ -2865,38 +2961,19 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form } ++printPostInfo.counter; } - } else if (li.listType == li.EBaseTypeTeam) { - if (li.calcResults) - calculateTeamResults(false); - if (li.calcTotalResults) - calculateTeamResults(true); - if (li.rogainingResults && li.resultModule.empty()) - throw std::exception("Not implemented"); - if (li.calcCourseClassResults) - calculateResults(li.lp.selection, ResultType::ClassCourseResult); - - if (li.resultModule.empty()) { + if (li.sortOrder != SortOrder::Custom) { pair legInfo = li.lp.getLegInfo(sampleClass); - sortTeams(li.sortOrder, legInfo.first, legInfo.second); - } - vector tlist; - tlist.reserve(Teams.size()); - for (oTeamList::iterator it = Teams.begin(); it != Teams.end(); ++it) { - if (it->isRemoved() || it->tStatus == StatusNotCompetiting) - continue; - - if (!li.lp.selection.empty() && li.lp.selection.count(it->getClassId(true)) == 0) - continue; - tlist.push_back(&*it); + sortTeams(li.sortOrder, legInfo.first, legInfo.second, tlist); } + GeneralResult *gResult = 0; if (!li.resultModule.empty()) { wstring src; - gResult = &getGeneralResult(li.resultModule, src); + gResult = getGeneralResult(li.resultModule, src).get(); oListInfo::ResultType resType = li.getResultType(); - gResult->calculateTeamResults(tlist, resType, li.sortOrder == Custom, li.getParam().getInputNumber()); + gResult->calculateTeamResults(tlist, false, resType, li.sortOrder == Custom, li.getParam().getInputNumber()); } // Range of runners to include int parLegRangeMin = 0, parLegRangeMax = 1000; @@ -2926,26 +3003,37 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form if (li.filter(EFilterHasResult)) { if (gResult) { - if (it->getTempResult(0).getStatus() == StatusUnknown) + RunnerStatus st = it->getTempResult(0).getStatus(); + if (st == StatusUnknown || (isPossibleResultStatus(st) && it->getTempResult(0).getRunningTime()<=0)) continue; } else { - if (it->getLegStatus(linearLegSpec, false) == StatusUnknown) - continue; - else if (li.calcTotalResults && it->getLegStatus(linearLegSpec, true) == StatusUnknown) + RunnerStatus st = it->getLegStatus(linearLegSpec, true, false); + if (st == StatusUnknown || (isPossibleResultStatus(st) && it->getLegRunningTime(linearLegSpec, true, false) <=0)) continue; + else if (li.calcTotalResults) { + st = it->getLegStatus(linearLegSpec, true, true); + if (st == StatusUnknown || (isPossibleResultStatus(st) && it->getLegRunningTime(linearLegSpec, true, true) <= 0)) + continue; + } } } else if (li.filter(EFilterHasPrelResult)) { if (gResult) { - if (it->getTempResult(0).getStatus() == StatusUnknown && it->getTempResult(0).getRunningTime() <= 0) + RunnerStatus st = it->getTempResult(0).getStatus(); + if ((st == StatusUnknown || isPossibleResultStatus(st)) && it->getTempResult(0).getRunningTime() <= 0) continue; } else { - if (it->getLegStatus(linearLegSpec, false) == StatusUnknown && it->getLegRunningTime(linearLegSpec, false) <= 0) - continue; - else if (li.calcTotalResults && it->getLegStatus(linearLegSpec, true) == StatusUnknown && it->getTotalRunningTime() <= 0) + RunnerStatus st = it->getLegStatus(linearLegSpec, true, false); + if ((st == StatusUnknown || isPossibleResultStatus(st)) && it->getLegRunningTime(linearLegSpec, true, false) <= 0) continue; + else if (li.calcTotalResults) { + RunnerStatus st = it->getLegStatus(linearLegSpec, true, true); + + if ((st == StatusUnknown || isPossibleResultStatus(st)) && it->getLegRunningTime(linearLegSpec, true, true) <= 0) + continue; + } } } @@ -3014,7 +3102,7 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form bool cancelled = false; if (gResult == 0) { noResult = it->Runners[k]->tStatus == StatusUnknown; - noPrelResult = it->Runners[k]->tStatus == StatusUnknown && it->Runners[k]->getRunningTime() <= 0; + noPrelResult = it->Runners[k]->tStatus == StatusUnknown && it->Runners[k]->getRunningTime(false) <= 0; noStart = it->Runners[k]->tStatus == StatusDNS || it->Runners[k]->tStatus == StatusCANCEL; if (it->Runners[k]->Class && it->Runners[k]->Class->isQualificationFinalBaseClass()) { if (k > 0 && it->Runners[k]->getClassRef(true) == it->Runners[k]->Class) @@ -3099,25 +3187,6 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form } } else if (li.listType == li.EBaseTypeClub) { - if (li.calcResults) { - calculateTeamResults(true); - calculateTeamResults(false); - } - if (li.calcCourseClassResults) - calculateResults(li.lp.selection, ResultType::ClassCourseResult); - - pair info = li.lp.getLegInfo(sampleClass); - sortTeams(li.sortOrder, info.first, info.second); - if (li.calcResults) { - if (li.lp.useControlIdResultTo > 0 || li.lp.useControlIdResultFrom > 0) - calculateSplitResults(li.lp.useControlIdResultFrom, li.lp.useControlIdResultTo); - else { - sortRunners(li.sortOrder); - calculateResults(li.lp.selection, ResultType::ClassResult); - } - } - else sortRunners(li.sortOrder); - Clubs.sort(); oClubList::iterator it; oRunnerList::iterator rit; @@ -3130,7 +3199,8 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form if (!li.filterRunner(r)) rlist.push_back(&r); } - + if (li.sortOrder != Custom) + sortRunners(li.sortOrder, rlist); GeneralResult *gResult = li.applyResultModule(*this, rlist); for (pRunner r : rlist) { @@ -3759,7 +3829,7 @@ void oEvent::generateListInfo(oListParam &par, int lineHeight, oListInfo &li) { void oEvent::generateListInfo(vector &par, int lineHeight, oListInfo &li) { li.getParam().sourceParam = -1;// Reset source - loadGeneralResults(false); + loadGeneralResults(false, false); lineHeight = 14; for (size_t k = 0; k < par.size(); k++) { par[k].cb = 0; diff --git a/code/oListInfo.h b/code/oListInfo.h index 23d3dcc..bfa380f 100644 --- a/code/oListInfo.h +++ b/code/oListInfo.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -77,6 +77,7 @@ enum EPostType lRunnerTotalPlace, lRunnerPlaceDiff, lRunnerClassCoursePlace, + lRunnerCoursePlace, lRunnerTotalTimeAfter, lRunnerClassCourseTimeAfter, lRunnerTimeAfterDiff, @@ -324,6 +325,7 @@ struct oListParam { bool operator==(const oListParam& a) const { return a.listCode == listCode && a.selection == selection && + a.ageFilter == ageFilter && a.useControlIdResultFrom == useControlIdResultFrom && a.useControlIdResultTo == useControlIdResultTo && a.filterMaxPer == filterMaxPer && @@ -370,6 +372,14 @@ struct oListParam { int nextList; // 1-based index of next list (in the container, MetaListParam::listParam) for linked lists int previousList; // 1-based index of previous list (in the container, MetaListParam::listParam) for linked lists. Not serialized + enum class AgeFilter { + All, + OnlyYouth, + ExludeYouth, + }; + + AgeFilter ageFilter = AgeFilter::All; + mutable bool lineBreakControlList = false; mutable int relayLegIndex; // Current index of leg (or -1 for entire team) mutable wstring defaultName; // Initialized when generating list @@ -480,6 +490,8 @@ protected: bool calcResults; bool calcCourseClassResults; + bool calcCourseResults; + bool calcTotalResults; bool rogainingResults; bool calculateLiveResults; @@ -526,6 +538,7 @@ public: // Result type ResultType resType; + void replaceType(EPostType find, EPostType replace, bool onlyFirst); PunchMode needPunchCheck() const {return needPunches;} void setCallback(GUICALLBACK cb); diff --git a/code/oPunch.cpp b/code/oPunch.cpp index 2afcf89..763b2c7 100644 --- a/code/oPunch.cpp +++ b/code/oPunch.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/oPunch.h b/code/oPunch.h index ed954da..03cb79a 100644 --- a/code/oPunch.h +++ b/code/oPunch.h @@ -7,7 +7,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -61,7 +61,6 @@ protected: mutable int previousPunchTime; /// Note that this is not valid in general public: - virtual int getControlId() const {return tMatchControlId;} bool isUsedInCourse() const {return isUsed;} diff --git a/code/oReport.cpp b/code/oReport.cpp index f2e9e41..4d60365 100644 --- a/code/oReport.cpp +++ b/code/oReport.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/oRunner.cpp b/code/oRunner.cpp index f0e8c82..9ca6774 100644 --- a/code/oRunner.cpp +++ b/code/oRunner.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -46,8 +46,9 @@ #include "oListInfo.h" #include "qualification_final.h" #include "metalist.h" +#include "generalresult.h" -oRunner::RaceIdFormatter oRunner::raceIdFormatter; +char RunnerStatusOrderMap[100]; bool oAbstractRunner::DynamicValue::isOld(const oEvent &oe) const { return oe.dataRevision != dataRevision; @@ -58,24 +59,155 @@ void oAbstractRunner::DynamicValue::update(const oEvent &oe, int v) { dataRevision = oe.dataRevision; } +const wstring &oAbstractRunner::encodeStatus(RunnerStatus st, bool allowError) { + wstring &res = StringCache::getInstance().wget(); + switch (st) { + case StatusOK: + res = L"OK"; + break; + case StatusUnknown: + res = L"UN"; + break; + case StatusDNS: + res = L"NS"; + break; + case StatusCANCEL: + res = L"CC"; + break; + case StatusOutOfCompetition: + res = L"OC"; + break; + case StatusNoTiming: + res = L"NT"; + break; + case StatusMP: + res = L"MP"; + break; + case StatusDNF: + res = L"NF"; + break; + case StatusDQ: + res = L"DQ"; + break; + case StatusMAX: + res = L"MX"; + break; + case StatusNotCompetiting: + res = L"NC"; + break; + default: + if (allowError) + res = L"ERROR"; + else + throw std::exception("Unknown status"); + } + + return res; +} + +RunnerStatus oAbstractRunner::decodeStatus(const wstring &stat) { + wstring ustat = stat; + for (wchar_t &t : ustat) { + t = toupper(t); + } + for (RunnerStatus st : getAllRunnerStatus()) + if (encodeStatus(st) == stat) + return st; + + return StatusUnknown; +} const wstring &oRunner::RaceIdFormatter::formatData(const oBase *ob) const { return itow(dynamic_cast(*ob).getRaceIdentifier()); } -wstring oRunner::RaceIdFormatter::setData(oBase *ob, const wstring &input) const { +pair oRunner::RaceIdFormatter::setData(oBase *ob, const wstring &input, wstring &output, int inputId) const { int rid = _wtoi(input.c_str()); if (input == L"0") ob->getDI().setInt("RaceId", 0); else if (rid>0 && rid != dynamic_cast(ob)->getRaceIdentifier()) ob->getDI().setInt("RaceId", rid); - return formatData(ob); + output = formatData(ob); + return make_pair(0, false); } int oRunner::RaceIdFormatter::addTableColumn(Table *table, const string &description, int minWidth) const { return table->addColumn(description, max(minWidth, 90), true, true); } +const wstring &oRunner::RunnerReference::formatData(const oBase *obj) const { + int id = obj->getDCI().getInt("Reference"); + if (id > 0) { + pRunner r = obj->getEvent()->getRunner(id, 0); + if (r) + return r->getUIName(); + else { + return lang.tl("Okänd"); + } + } + return _EmptyWString; + } + + +pair oRunner::RunnerReference::setData(oBase *obj, const wstring &input, wstring &output, int inputId) const { + int oldRef = obj->getDCI().getInt("Reference"); + obj->getDI().setInt("Reference", inputId); + bool clearAll = false; + if (inputId != oldRef) { + if (oldRef != 0) { + pRunner oldRefR = obj->getEvent()->getRunner(oldRef, 0); + if (oldRefR) { + oldRefR->setReference(0); + clearAll = true; + } + } + + if (inputId != 0) { + pRunner newRefR = obj->getEvent()->getRunner(inputId, 0); + if (newRefR) + newRefR->setReference(obj->getId()); + } + } + + output = formatData(obj); + return make_pair(inputId, clearAll); +} + +void oRunner::RunnerReference::fillInput(const oBase *obj, vector> &out, size_t &selected) const { + const oRunner *r = static_cast(obj); + int cls = r->getClassId(true); + vector runners; + r->oe->getRunners(cls, 0, runners, true); + int id = obj->getDCI().getInt("Reference"); + selected = id; + out.reserve(runners.size() + 2); + out.emplace_back(lang.tl("Ingen"), 0); + for (auto rr : runners) { + if (rr->Id == id) + id = 0; + if (rr->Id == r->Id) + continue; // No self reference + + out.emplace_back(rr->getUIName(), rr->Id); + } + + if (id != 0) { + pRunner rr = obj->getEvent()->getRunner(id, 0); + if (rr) + out.emplace_back(rr->getUIName(), id); + else + out.emplace_back(lang.tl("Okänd"), id); + } +} + +int oRunner::RunnerReference::addTableColumn(Table *table, const string &description, int minWidth) const { + return table->addColumn(description, max(minWidth, 200), true, true); +} + +CellType oRunner::RunnerReference::getCellType() const { + return CellType::cellSelection; +} + ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// @@ -115,7 +247,7 @@ void oAbstractRunner::setFinishTimeS(const wstring &t) void oAbstractRunner::setStartTimeS(const wstring &t) { - setStartTime(oe->getRelativeTime(t), true, false); + setStartTime(oe->getRelativeTime(t), true, ChangeType::Update); } oRunner::oRunner(oEvent *poe) :oAbstractRunner(poe, false) @@ -420,11 +552,11 @@ void oRunner::decodeMultiR(const string &r) } void oAbstractRunner::setClassId(int id, bool isManualUpdate) { - pClass pc=Class; - Class=id ? oe->getClass(id):0; + pClass pc = Class; + Class = id ? oe->getClass(id) : nullptr; if (Class!=pc) { - apply(false, 0, false); + apply(ChangeType::Update, 0); if (Class) { Class->clearCache(true); } @@ -447,6 +579,8 @@ void oRunner::setClassId(int id, bool isManualUpdate) { pClass nPc = id>0 ? oe->getClass(id) : 0; if (Class == nPc) return; + oe->classIdToRunnerHash.reset(); + if (Class && Class->getQualificationFinal() && isManualUpdate && nPc && nPc->parentClass == Class) { int heat = Class->getQualificationFinal()->getHeatFromClass(id, Class->getId()); if (heat >= 0) { @@ -459,7 +593,7 @@ void oRunner::setClassId(int id, bool isManualUpdate) { oldHeatClass->clearCache(true); newHeatClass->clearCache(true); tSplitRevision = 0; - apply(false, 0, false); + apply(ChangeType::Quiet, nullptr); } } return; @@ -498,12 +632,12 @@ void oRunner::setClassId(int id, bool isManualUpdate) { Class->isSingleRunnerMultiStage()) { if (!isTemporaryObject) { pTeam t = oe->addTeam(getName(), getClubId(), getClassId(false)); - t->setStartNo(StartNo, false); + t->setStartNo(StartNo, ChangeType::Update); t->setRunner(0, this, true); } } - apply(false, 0, false); //We may get old class back from team. + apply(ChangeType::Quiet, nullptr); //We may get old class back from team. for (size_t k=0;kClass) { @@ -550,12 +684,7 @@ void oRunner::setCourseId(int id) } } -bool oAbstractRunner::setStartTime(int t, bool updateSource, bool tmpOnly, bool recalculate) { - assert(!(updateSource && tmpOnly)); - if (tmpOnly) { - tmpStore.startTime = t; - return false; - } +bool oAbstractRunner::setStartTime(int t, bool updateSource, ChangeType changeType, bool recalculate) { int tOST=tStartTime; if (t>0) @@ -567,7 +696,7 @@ bool oAbstractRunner::setStartTime(int t, bool updateSource, bool tmpOnly, bool startTime = tStartTime; if (OST!=startTime) { - updateChanged(); + updateChanged(changeType); } } @@ -647,17 +776,21 @@ const wstring &oAbstractRunner::getFinishTimeS() const else return makeDash(L"-"); } -int oAbstractRunner::getRunningTime() const { - int rt = FinishTime-tStartTime; - if (rt > 0) - return getTimeAdjustment() + rt; +int oAbstractRunner::getRunningTime(bool computedTime) const { + if (!computedTime || tComputedTime == 0) { + int rt = FinishTime - tStartTime; + if (rt > 0) + return getTimeAdjustment() + rt; + else + return 0; + } else - return 0; + return tComputedTime; } -const wstring &oAbstractRunner::getRunningTimeS() const +const wstring &oAbstractRunner::getRunningTimeS(bool computedTime) const { - return formatTime(getRunningTime()); + return formatTime(getRunningTime(computedTime)); } const wstring &oAbstractRunner::getTotalRunningTimeS() const @@ -666,7 +799,7 @@ const wstring &oAbstractRunner::getTotalRunningTimeS() const } int oAbstractRunner::getTotalRunningTime() const { - int t = getRunningTime(); + int t = getRunningTime(true); if (t > 0 && inputTime>=0) return t + inputTime; else @@ -674,13 +807,15 @@ int oAbstractRunner::getTotalRunningTime() const { } int oRunner::getTotalRunningTime() const { - return getTotalRunningTime(getFinishTime(), true); + return getTotalRunningTime(getFinishTime(), true, true); } - -const wstring &oAbstractRunner::getStatusS(bool formatForPrint) const +const wstring &oAbstractRunner::getStatusS(bool formatForPrint, bool computedStatus) const { - return oEvent::formatStatus(tStatus, formatForPrint); + if (computedStatus) + return oEvent::formatStatus(getStatusComputed(), formatForPrint); + else + return oEvent::formatStatus(tStatus, formatForPrint); } const wstring &oAbstractRunner::getTotalStatusS(bool formatForPrint) const @@ -706,8 +841,11 @@ const wstring &oAbstractRunner::getTotalStatusS(bool formatForPrint) const restrictions - Cancelled */ -const wchar_t *formatIOFStatus(RunnerStatus s) { +const wchar_t *formatIOFStatus(RunnerStatus s, bool hasTime) { switch(s) { + case StatusNoTiming: + if (!hasTime) + break; case StatusOK: return L"OK"; case StatusDNS: @@ -722,18 +860,23 @@ const wchar_t *formatIOFStatus(RunnerStatus s) { return L"Disqualified"; case StatusMAX: return L"OverTime"; + case StatusOutOfCompetition: + if (!hasTime) + break; + case StatusNotCompetiting: + return L"NotCompeting"; } return L"Inactive"; } wstring oAbstractRunner::getIOFStatusS() const { - return formatIOFStatus(tStatus); + return formatIOFStatus(getStatusComputed(), getFinishTime()> 0); } wstring oAbstractRunner::getIOFTotalStatusS() const { - return formatIOFStatus(getTotalStatus()); + return formatIOFStatus(getTotalStatus(), getFinishTime()> 0); } void oRunner::addPunches(pCard card, vector &missingPunches) { @@ -797,8 +940,10 @@ void oRunner::addPunches(pCard card, vector &missingPunches) { if (Card) Card->tOwner=this; - evaluateCard(true, missingPunches, 0, true); + evaluateCard(true, missingPunches, 0, ChangeType::Update); + synchronizeAll(true); + if (oe->isClient() && oe->getPropertyInt("UseDirectSocket", true)!=0) { if (oldStatus != getStatus() || oldFinishTime != getFinishTime()) { SocketPunchInfo pi; @@ -941,13 +1086,14 @@ const wstring &oRunner::getCourseName() const } #define NOTATIME 0xF0000000 -void oAbstractRunner::resetTmpStore() { +/*void oAbstractRunner::resetTmpStore() { tmpStore.startTime = startTime; tmpStore.status = status; tmpStore.startNo = StartNo; tmpStore.bib = getBib(); } - +*/ +/* bool oAbstractRunner::setTmpStore() { bool res = false; setStartNo(tmpStore.startNo, false); @@ -955,15 +1101,15 @@ bool oAbstractRunner::setTmpStore() { res |= setStatus(tmpStore.status, false, false, false); setBib(tmpStore.bib, 0, false, false); return res; -} +}*/ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, - int addpunch, bool sync) { + int addpunch, ChangeType changeType) { if (unsigned(status) >= 100u) status = StatusUnknown; //Reset bad input pClass clz = getClassRef(true); MissingPunches.clear(); - int oldFT = FinishTime; + const int oldFT = FinishTime; int oldStartTime; RunnerStatus oldStatus; int *refStartTime; @@ -977,17 +1123,16 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, refStartTime = &tStartTime; refStatus = &tStatus; - resetTmpStore(); - apply(sync, 0, true); + apply(changeType, nullptr); } else { // tmp initialized from outside. Do not change tStatus, tStartTime. Work with tmpStore instead! oldStartTime = tStartTime; oldStatus = tStatus; - refStartTime = &tmpStore.startTime; - refStatus = &tmpStore.status; + refStartTime = &tStartTime; + refStatus = &tStatus; - createMultiRunner(false, sync); + createMultiRunner(false, changeType == ChangeType::Update); } // Reset card data @@ -1015,10 +1160,10 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, if (!Card) { if ((inTeam || !tUseStartPunch) && doApply) - apply(sync, 0, false); //Post apply. Update start times. + apply(changeType, nullptr); //Post apply. Update start times. - if (storeTimes() && clz && sync) { - oe->reEvaluateAll({ clz->getId() }, sync); + if (storeTimes() && clz && changeType == ChangeType::Update) { + oe->reEvaluateAll({ clz->getId() }, true); } normalizedSplitTimes.clear(); if (oldTimes.size() > 0 && Class) @@ -1043,7 +1188,7 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, setFinishTime(p_it->Time); } if ((inTeam || !tUseStartPunch) && doApply) - apply(sync, 0, false); //Post apply. Update start times. + apply(changeType, nullptr); //Post apply. Update start times. storeTimes(); // No course mode @@ -1054,7 +1199,7 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, if (clz) { int mt = clz->getMaximumRunnerTime(); if (mt>0) { - if (getRunningTime() > mt) + if (getRunningTime(false) > mt) maxTimeStatus = 1; else maxTimeStatus = 2; @@ -1104,10 +1249,10 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, } } if ((inTeam || !tUseStartPunch) && doApply) - apply(sync, 0, false); //Post apply. Update start times. + apply(changeType, nullptr); //Post apply. Update start times. - if (storeTimes() && clz && sync) { - oe->reEvaluateAll({ clz->getId() }, sync); + if (storeTimes() && clz && changeType == ChangeType::Update) { + oe->reEvaluateAll({ clz->getId() }, true); } normalizedSplitTimes.clear(); @@ -1353,7 +1498,7 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, if (clz && FinishTime>0) { int mt = clz->getMaximumRunnerTime(); if (mt>0) { - if (getRunningTime() > mt) + if (getRunningTime(false) > mt) maxTimeStatus = 1; else maxTimeStatus = 2; @@ -1362,10 +1507,12 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, maxTimeStatus = 2; } - if (*refStatus == StatusMAX && maxTimeStatus == 2) + if ( (*refStatus == StatusMAX && maxTimeStatus == 2) || + *refStatus == StatusOutOfCompetition || + *refStatus == StatusNoTiming) *refStatus = StatusUnknown; - - if (OK && (*refStatus==0 || *refStatus==StatusDNS || *refStatus == StatusCANCEL || *refStatus==StatusMP || *refStatus==StatusOK || *refStatus==StatusDNF)) + + if (OK && (*refStatus == 0 || *refStatus == StatusDNS || *refStatus == StatusCANCEL || *refStatus == StatusMP || *refStatus == StatusOK || *refStatus == StatusDNF)) *refStatus = StatusOK; else *refStatus = RunnerStatus(max(int(StatusMP), int(*refStatus))); @@ -1405,6 +1552,12 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, tProblemDescription += L"."; } + if (*refStatus == StatusOK) { + if (hasFlag(TransferFlags::FlagOutsideCompetition)) + *refStatus = StatusOutOfCompetition; + else if (hasFlag(TransferFlags::FlagNoTiming)) + *refStatus = StatusNoTiming; + } // Adjust times on course, including finish time doAdjustTimes(course); @@ -1414,8 +1567,11 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, clearSplitAnalysis = true; } + if (oldFT != FinishTime) + updateChanged(changeType); + if ((inTeam || !tUseStartPunch) && doApply) - apply(sync, 0, false); //Post apply. Update start times. + apply(changeType, nullptr); //Post apply. Update start times. if (tCachedRunningTime != FinishTime - *refStartTime) { tCachedRunningTime = FinishTime - *refStartTime; @@ -1423,7 +1579,7 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, } if (time_limit > 0) { - int rt = getRunningTime(); + int rt = getRunningTime(false); if (rt > 0) { int overTime = rt - time_limit; if (overTime > 0) { @@ -1435,8 +1591,6 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, } } - - // Clear split analysis data if necessary bool clear = splitTimes.size() != oldTimes.size() || clearSplitAnalysis; for (size_t k = 0; !clear && k & MissingPunches, if (doApply) storeTimes(); - if (clz && sync) { + if (clz && changeType == ChangeType::Update) { bool update = false; if (tInTeam) { - int t1 = clz->getTotalLegLeaderTime(tLeg, false); - int t2 = tInTeam->getLegRunningTime(tLeg, false); + int t1 = clz->getTotalLegLeaderTime(tLeg, false, false); + int t2 = tInTeam->getLegRunningTime(tLeg, false, false); if (t2<=t1 && t2>0) update = true; - int t3 = clz->getTotalLegLeaderTime(tLeg, true); - int t4 = tInTeam->getLegRunningTime(tLeg, true); + int t3 = clz->getTotalLegLeaderTime(tLeg, false, true); + int t4 = tInTeam->getLegRunningTime(tLeg, false, true); if (t4<=t3 && t4>0) update = true; } if (!update) { - int t1 = clz->getBestLegTime(tLeg); - int t2 = getRunningTime(); + int t1 = clz->getBestLegTime(tLeg, false); + int t2 = getRunningTime(false); if (t2<=t1 && t2>0) update = true; } if (update) { - oe->reEvaluateAll({ clz->getId() }, sync); + oe->reEvaluateAll({ clz->getId() }, true); } } return true; @@ -1586,7 +1740,7 @@ bool oRunner::storeTimesAux(pClass targetClass) { for (int leg = firstLeg; legtLeaderTime[leg].bestTimeOnLeg; - int rt=getRunningTime(); + int rt=getRunningTime(false); if (rt > 0 && (bt == 0 || rt < bt)) { bt=rt; updated = true; @@ -1603,8 +1757,8 @@ bool oRunner::storeTimesAux(pClass targetClass) { int ix = basePLeg; while (ix < nleg && (ix == basePLeg || targetClass->legInfo[ix].isParallel()) ) { - updateTotal = updateTotal && tInTeam->getLegStatus(ix, false)==StatusOK; - updateTotalInput = updateTotalInput && tInTeam->getLegStatus(ix, true)==StatusOK; + updateTotal = updateTotal && tInTeam->getLegStatus(ix, false, false)==StatusOK; + updateTotalInput = updateTotalInput && tInTeam->getLegStatus(ix, false, true)==StatusOK; ix++; } @@ -1612,7 +1766,7 @@ bool oRunner::storeTimesAux(pClass targetClass) { int rt = 0; int ix = basePLeg; while (ix < nleg && (ix == basePLeg || targetClass->legInfo[ix].isParallel()) ) { - rt = max(rt, tInTeam->getLegRunningTime(ix, false)); + rt = max(rt, tInTeam->getLegRunningTime(ix, false, false)); ix++; } @@ -1629,7 +1783,7 @@ bool oRunner::storeTimesAux(pClass targetClass) { int rt = 0; int ix = basePLeg; while (ix < nleg && (ix == basePLeg || targetClass->legInfo[ix].isParallel()) ) { - rt = max(rt, tInTeam->getLegRunningTime(ix, true)); + rt = max(rt, tInTeam->getLegRunningTime(ix, false, true)); ix++; } for (int leg = firstLeg; legtLeaderTime.size()) { if (tStatus == StatusOK) { int &bt = targetClass->tLeaderTime[dupLeg].bestTimeOnLeg; - int rt = getRunningTime(); + int rt = getRunningTime(false); if (rt > 0 && (bt == 0 || rt < bt)) { bt = rt; updated = true; @@ -1674,7 +1828,7 @@ bool oRunner::storeTimesAux(pClass targetClass) { } if (targetClass && tStatus==StatusOK) { - int rt = getRunningTime(); + int rt = getRunningTime(false); pCourse pCrs = getCourse(false); if (pCrs && rt > 0) { map::iterator res = targetClass->tBestTimePerCourse.find(pCrs->getId()); @@ -1701,7 +1855,7 @@ int oRunner::getRaceRunningTime(int leg) const if (leg==0) { if (getTotalStatus() == StatusOK) - return getRunningTime() + inputTime; + return getRunningTime(false) + inputTime; else return 0; } leg--; @@ -1714,23 +1868,23 @@ int oRunner::getRaceRunningTime(int leg) const switch(lt) { case LTNormal: - if (r->statusOK()) { - int dt=leg>0 ? r->getRaceRunningTime(leg)+r->getRunningTime():0; + if (r->statusOK(false)) { + int dt=leg>0 ? r->getRaceRunningTime(leg)+r->getRunningTime(false):0; return max(r->getFinishTime()-tStartTime, dt); // ### Luckor, jaktstart??? } else return 0; break; case LTSum: - if (r->statusOK()) - return r->getRunningTime()+getRaceRunningTime(leg); + if (r->statusOK(false)) + return r->getRunningTime(false)+getRaceRunningTime(leg); else return 0; default: return 0; } } - else return getRunningTime(); + else return getRunningTime(false); } return 0; } @@ -1788,7 +1942,7 @@ bool oRunner::operator<(const oRunner &c) const { } } } - else if (oe->CurrentSortOrder == ClassResult) { + else if (oe->CurrentSortOrder == ClassDefaultResult) { RunnerStatus stat = tStatus == StatusUnknown ? StatusOK : tStatus; RunnerStatus cstat = c.tStatus == StatusUnknown ? StatusOK : c.tStatus; @@ -1812,12 +1966,52 @@ bool oRunner::operator<(const oRunner &c) const { if (s != cs) return s < cs; - int t = getRunningTime(); + int t = getRunningTime(false); if (t <= 0) - t = 3600 * 100; - int ct = c.getRunningTime(); + t = 3600 * 1000; + int ct = c.getRunningTime(false); if (ct <= 0) - ct = 3600 * 100; + ct = 3600 * 1000; + + if (t != ct) + return t < ct; + } + } + } + else if (oe->CurrentSortOrder == ClassResult) { + + RunnerStatus stat = getStatusComputed(); + RunnerStatus cstat = c.getStatusComputed(); + + stat = stat == StatusUnknown ? StatusOK : stat; + cstat = cstat == StatusUnknown ? StatusOK : cstat; + + if (myClass != cClass) + return myClass->tSortIndex < cClass->tSortIndex || (myClass->tSortIndex == cClass->tSortIndex && myClass->Id < cClass->Id); + else if (tLegEquClass != c.tLegEquClass) + return tLegEquClass < c.tLegEquClass; + else if (tDuplicateLeg != c.tDuplicateLeg) + return tDuplicateLeg < c.tDuplicateLeg; + else if (stat != cstat) + return RunnerStatusOrderMap[stat] < RunnerStatusOrderMap[cstat]; + else { + if (stat == StatusOK) { + if (Class->getNoTiming()) { + return CompareString(LOCALE_USER_DEFAULT, 0, + tRealName.c_str(), tRealName.length(), + c.tRealName.c_str(), c.tRealName.length()) == CSTR_LESS_THAN; + } + int s = getNumShortening(); + int cs = c.getNumShortening(); + if (s != cs) + return s < cs; + + int t = getRunningTime(true); + if (t <= 0) + t = 3600 * 1000; + int ct = c.getRunningTime(true); + if (ct <= 0) + ct = 3600 * 1000; if (t != ct) return t < ct; @@ -1830,6 +2024,9 @@ bool oRunner::operator<(const oRunner &c) const { const pCourse crs1 = getCourse(false); const pCourse crs2 = c.getCourse(false); + RunnerStatus stat = getStatusComputed(); + RunnerStatus cstat = c.getStatusComputed(); + if (crs1 != crs2) { int id1 = crs1 ? crs1->getId() : 0; int id2 = crs2 ? crs2->getId() : 0; @@ -1837,10 +2034,10 @@ bool oRunner::operator<(const oRunner &c) const { } else if (tDuplicateLeg != c.tDuplicateLeg) return tDuplicateLeg < c.tDuplicateLeg; - else if (tStatus != c.tStatus) - return RunnerStatusOrderMap[tStatus] < RunnerStatusOrderMap[c.tStatus]; + else if (stat != cstat) + return RunnerStatusOrderMap[stat] < RunnerStatusOrderMap[cstat]; else { - if (tStatus == StatusOK) { + if (stat == StatusOK) { if (Class->getNoTiming()) { return CompareString(LOCALE_USER_DEFAULT, 0, tRealName.c_str(), tRealName.length(), @@ -1851,8 +2048,8 @@ bool oRunner::operator<(const oRunner &c) const { if (s != cs) return s < cs; - int t = getRunningTime(); - int ct = c.getRunningTime(); + int t = getRunningTime(true); + int ct = c.getRunningTime(true); if (t != ct) return t < ct; } @@ -1884,12 +2081,15 @@ bool oRunner::operator<(const oRunner &c) const { } } else if (oe->CurrentSortOrder == SortByFinishTime) { - if (tStatus != c.tStatus) - return RunnerStatusOrderMap[tStatus] < RunnerStatusOrderMap[c.tStatus]; + RunnerStatus stat = getStatusComputed(); + RunnerStatus cstat = c.getStatusComputed(); + + if (stat != cstat) + return RunnerStatusOrderMap[stat] < RunnerStatusOrderMap[cstat]; else { int ft = getFinishTimeAdjusted(); int cft = c.getFinishTimeAdjusted(); - if (tStatus == StatusOK && ft != cft) + if (stat == StatusOK && ft != cft) return ft < cft; } } @@ -1902,12 +2102,16 @@ bool oRunner::operator<(const oRunner &c) const { else if (oe->CurrentSortOrder == ClassFinishTime) { if (myClass != cClass) return myClass->tSortIndex < cClass->tSortIndex || (myClass->tSortIndex == cClass->tSortIndex && myClass->Id < cClass->Id); - if (tStatus != c.tStatus) - return RunnerStatusOrderMap[tStatus] < RunnerStatusOrderMap[c.tStatus]; + + RunnerStatus stat = getStatusComputed(); + RunnerStatus cstat = c.getStatusComputed(); + + if (stat != cstat) + return RunnerStatusOrderMap[stat] < RunnerStatusOrderMap[cstat]; else { int ft = getFinishTimeAdjusted(); int cft = c.getFinishTimeAdjusted(); - if (tStatus == StatusOK && ft != cft) + if (stat == StatusOK && ft != cft) return ft < cft; } } @@ -1952,10 +2156,13 @@ bool oRunner::operator<(const oRunner &c) const { return RunnerStatusOrderMap[tStatus] < RunnerStatusOrderMap[c.tStatus]; else { if (tStatus == StatusOK) { - if (tRogainingPoints != c.tRogainingPoints) - return tRogainingPoints > c.tRogainingPoints; - int t = getRunningTime(); - int ct = c.getRunningTime(); + int myP = getRogainingPoints(true, false); + int otherP = c.getRogainingPoints(true, false); + + if (myP != otherP) + return myP > otherP; + int t = getRunningTime(true); + int ct = c.getRunningTime(true); if (t != ct) return t < ct; } @@ -1978,8 +2185,8 @@ bool oRunner::operator<(const oRunner &c) const { tRealName.c_str(), tRealName.length(), c.tRealName.c_str(), c.tRealName.length()) == CSTR_LESS_THAN; } - int t = getTotalRunningTime(FinishTime, true); - int ct = c.getTotalRunningTime(c.FinishTime, true); + int t = getTotalRunningTime(FinishTime, true, true); + int ct = c.getTotalRunningTime(c.FinishTime, true, true); if (t != ct) return t < ct; } @@ -1988,23 +2195,26 @@ bool oRunner::operator<(const oRunner &c) const { else if (oe->CurrentSortOrder == CourseResult) { const pCourse crs1 = getCourse(false); const pCourse crs2 = c.getCourse(false); + RunnerStatus stat = getStatusComputed(); + RunnerStatus cstat = c.getStatusComputed(); + if (crs1 != crs2) { int id1 = crs1 ? crs1->getId() : 0; int id2 = crs2 ? crs2->getId() : 0; return id1 < id2; } - else if (tStatus != c.tStatus) - return RunnerStatusOrderMap[tStatus] < RunnerStatusOrderMap[c.tStatus]; + else if (stat != cstat) + return RunnerStatusOrderMap[stat] < RunnerStatusOrderMap[cstat]; else { - if (tStatus == StatusOK) { + if (stat == StatusOK) { int s = getNumShortening(); int cs = c.getNumShortening(); if (s != cs) return s < cs; - int t = getRunningTime(); - int ct = c.getRunningTime(); + int t = getRunningTime(true); + int ct = c.getRunningTime(true); if (t != ct) { return t < ct; } @@ -2147,38 +2357,32 @@ void oRunner::propagateClub() { } } -void oAbstractRunner::setStartNo(int no, bool tmpOnly) { - if (tmpOnly) { - tmpStore.startNo = no; - return; - } - +void oAbstractRunner::setStartNo(int no, ChangeType changeType) { if (no!=StartNo) { if (oe) oe->bibStartNoToRunnerTeam.clear(); StartNo=no; - updateChanged(); + updateChanged(changeType); } } -void oRunner::setStartNo(int no, bool tmpOnly) -{ +void oRunner::setStartNo(int no, ChangeType changeType) { if (tInTeam) { if (tInTeam->getStartNo() == 0) - tInTeam->setStartNo(no, tmpOnly); + tInTeam->setStartNo(no, changeType); else { // Do not allow different from team no = tInTeam->getStartNo(); } } if (tParentRunner) - tParentRunner->setStartNo(no, tmpOnly); + tParentRunner->setStartNo(no, changeType); else { - oAbstractRunner::setStartNo(no, tmpOnly); + oAbstractRunner::setStartNo(no, changeType); for (size_t k=0;koAbstractRunner::setStartNo(no, tmpOnly); + multiRunner[k]->oAbstractRunner::setStartNo(no, changeType); } } @@ -2191,10 +2395,10 @@ void oRunner::updateStartNo(int no) { } } - tInTeam->setStartNo(no, false); + tInTeam->setStartNo(no, ChangeType::Update); for (pRunner r : tInTeam->Runners) { if (r) { - r->setStartNo(no, false); + r->setStartNo(no, ChangeType::Update); } } @@ -2205,35 +2409,50 @@ void oRunner::updateStartNo(int no) { } } else { - setStartNo(no, false); + setStartNo(no, ChangeType::Update); synchronize(true); } } - int oRunner::getPlace() const { if (tPlace.isOld(*oe)) { if (Class) { oEvent::ResultType rt = oEvent::ResultType::ClassResult; - if (Class->isRogaining()) - oe->calculateRogainingResults({ getClassId(true) }); - else - oe->calculateResults({ getClassId(true) }, rt, false); + oe->calculateResults({ getClassId(true) }, rt, false); } } return tPlace.value; } -int oRunner::getCoursePlace() const { - return tCoursePlace; +int oRunner::getCoursePlace(bool perClass) const { + if (perClass) { + if (tCourseClassPlace.isOld(*oe) && Class) { + oEvent::ResultType rt = oEvent::ResultType::ClassCourseResult; + oe->calculateResults({ getClassId(true) }, rt, false); + } + return tCourseClassPlace.value; + + } + else { + if (tCoursePlace.isOld(*oe) && Class) { + oEvent::ResultType rt = oEvent::ResultType::CourseResult; + oe->calculateResults({ getClassId(true) }, rt, false); + } + return tCoursePlace.value; + } } int oRunner::getTotalPlace() const { if (tInTeam) - return tInTeam->getLegPlace(tLeg, true); - else - return tTotalPlace; + return tInTeam->getLegPlace(getParResultLeg(), true); + else { + if (tTotalPlace.isOld(*oe) && Class) { + oEvent::ResultType rt = oEvent::ResultType::TotalResult; + oe->calculateResults({ getClassId(true) }, rt, false); + } + return tTotalPlace.value; + } } wstring oAbstractRunner::getPlaceS() const @@ -2362,7 +2581,7 @@ int oRunner::setCard(int cardId) assert(otherR != this); otherR->Card = 0; otherR->updateChanged(); - otherR->setStatus(StatusUnknown, true, false); + otherR->setStatus(StatusUnknown, true, ChangeType::Update); otherR->synchronize(true); } c->tOwner = this; @@ -2370,7 +2589,7 @@ int oRunner::setCard(int cardId) } Card = c; vector mp; - evaluateCard(true, mp); + evaluateCard(true, mp, 0, ChangeType::Update); updateChanged(); synchronize(true); } @@ -2449,20 +2668,39 @@ const wstring &oRunner::getNameLastFirst() const { return res; } -void oRunner::getRealName(const wstring &input, wstring &output) { +void oRunner::getRealName(const wstring &input, wstring &output) const { size_t comma = input.find_first_of(','); - if (comma == string::npos) - output = input; - else - output = trim(input.substr(comma+1) + L" " + input.substr(0, comma)); + if (oe->getNameMode() != oEvent::NameMode::LastFirst) { + if (comma == string::npos) + output = input; + else + output = trim(input.substr(comma + 1) + L" " + input.substr(0, comma)); + } + else { + if (comma != string::npos) + output = input; + else + output = getNameLastFirst(); + } } -bool oAbstractRunner::setStatus(RunnerStatus st, bool updateSource, bool tmpOnly, bool recalculate) { - assert(!(updateSource && tmpOnly)); - if (tmpOnly) { - tmpStore.status = st; - return false; +bool oAbstractRunner::isResultStatus(RunnerStatus st) { + switch (st) { + case StatusDNS: + case StatusCANCEL: + case StatusOutOfCompetition: + case StatusNotCompetiting: + case StatusUnknown: + case StatusNoTiming: + return false; + default: + return true; } +} + +bool oAbstractRunner::setStatus(RunnerStatus st, bool updateSource, ChangeType changeType, bool recalculate) { + assert(!(updateSource && changeType == ChangeType::Quiet)); + bool ch = false; if (tStatus!=st) { ch = true; @@ -2476,8 +2714,20 @@ bool oAbstractRunner::setStatus(RunnerStatus st, bool updateSource, bool tmpOnly if (st != status) { status = st; - if (updateSource) - updateChanged(); + if (updateSource) { + updateChanged(changeType); + if (st == StatusOutOfCompetition) + setFlag(TransferFlags::FlagOutsideCompetition, true); + else { + setFlag(TransferFlags::FlagOutsideCompetition, false); + } + + if (st == StatusNoTiming) + setFlag(TransferFlags::FlagNoTiming, true); + else { + setFlag(TransferFlags::FlagNoTiming, false); + } + } else changedObject(); } @@ -2488,45 +2738,137 @@ bool oAbstractRunner::setStatus(RunnerStatus st, bool updateSource, bool tmpOnly int oAbstractRunner::getPrelRunningTime() const { if (FinishTime>0 && tStatus!=StatusDNS && tStatus != StatusCANCEL && tStatus!=StatusDNF && tStatus!=StatusNotCompetiting) - return getRunningTime(); + return getRunningTime(true); else if (tStatus==StatusUnknown) return oe->getComputerTime()-tStartTime; else return 0; } -wstring oAbstractRunner::getPrelRunningTimeS() const -{ - int rt=getPrelRunningTime(); - return formatTime(rt); -} - oDataContainer &oRunner::getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const { data = (pvoid)oData; olddata = (pvoid)oDataOld; - strData = 0; + strData = const_cast(&dynamicData); return *oe->oRunnerData; } void oEvent::getRunners(int classId, int courseId, vector &r, bool sort) { if (sort) { synchronizeList(oListId::oLRunnerId); - sortRunners(SortByName); + + if (classId > 0 && classIdToRunnerHash) { + sortRunners(SortByName, (*classIdToRunnerHash)[classId]); + } + else + sortRunners(SortByName); } + r.clear(); - if (Classes.size() > 0) + + if (classId > 0 && classIdToRunnerHash) { + auto &rh = (*classIdToRunnerHash)[classId]; + r.reserve(rh.size()); + for (pRunner rr : rh) { + if (!rr->isRemoved() && rr->getClassId(true) == classId) { + + bool skip = false; + if (courseId > 0) { + pCourse pc = rr->getCourse(false); + if (pc == 0 || pc->getId() != courseId) + skip = true; + } + + if (!skip) + r.push_back(rr); + } + } + return; + } + + if (classId <= 0) + r.reserve(Runners.size()); + else if (Classes.size() > 0) r.reserve((Runners.size()*min(Classes.size(), 4)) / Classes.size()); + bool hash = false; + if (!classIdToRunnerHash) { + classIdToRunnerHash = make_shared>>(); + hash = true; + } + for (oRunnerList::iterator it = Runners.begin(); it != Runners.end(); ++it) { if (it->isRemoved()) continue; + + bool skip = false; if (courseId > 0) { pCourse pc = it->getCourse(false); if (pc == 0 || pc->getId() != courseId) - continue; + skip = true; } - - if (classId <= 0 || it->getClassId(true) == classId) + int clsId = 0; + if (!skip && classId <= 0 || (clsId = it->getClassId(true)) == classId) r.push_back(&*it); + + if (hash) { + if (clsId == 0) + clsId = it->getClassId(true); + + if (clsId != 0) + (*classIdToRunnerHash)[clsId].push_back(&*it); + } + } +} + +void oEvent::getRunners(const set &classId, vector &r, bool synchRunners) { + if (classId.size() == Classes.size() || classId.size() == 0) { + getRunners(0, 0, r, synchRunners); + return; + } + + if (synchRunners) { + synchronizeList(oListId::oLRunnerId); + } + + getRunners(classId, r); +} + +void oEvent::getRunners(const set &classId, vector &r) const { + if (classId.size() == Classes.size() || classId.size() == 0) { + const_cast(this)->getRunners(0, 0, r, false); + return; + } + + r.clear(); + + if (classIdToRunnerHash) { + size_t s = 0; + for (int cid : classId) + s += (*classIdToRunnerHash)[cid].size(); + r.reserve(s); + + for (int cid : classId) { + auto &rh = (*classIdToRunnerHash)[cid]; + for (pRunner rr : rh) { + if (!rr->isRemoved() && rr->getClassId(true) == cid) + r.push_back(rr); + } + } + return; + } + + r.reserve(Runners.size()); + classIdToRunnerHash = make_shared>>(); + + for (auto it = Runners.begin(); it != Runners.end(); ++it) { + if (it->isRemoved()) + continue; + int clsId = it->getClassId(true); + pRunner rr = const_cast(&*it); + if (classId.count(clsId)) + r.push_back(rr); + + if (clsId != 0) + (*classIdToRunnerHash)[clsId].push_back(rr); } } @@ -2551,7 +2893,7 @@ pRunner oRunner::nextNeedReadout() const { // For a runner in a team, first the team for the card for (size_t k = 0; k < tInTeam->Runners.size(); k++) { pRunner tr = tInTeam->Runners[k]; - if (tr && tr->getCardNo() == getCardNo() && !tr->Card && !tr->statusOK()) + if (tr && tr->getCardNo() == getCardNo() && !tr->Card && !tr->statusOK(false)) return tr; } } @@ -3118,26 +3460,21 @@ pRunner oRunner::getPredecessor() const return tParentRunner->multiRunner[tDuplicateLeg-2]; } -bool oRunner::apply(bool sync, pRunner src, bool setTmpOnly) { +void oRunner::apply(ChangeType changeType, pRunner src) { for (size_t k = 0; k < multiRunner.size(); k++) { if (multiRunner[k] && multiRunner[k]->isRemoved()) { multiRunner[k]->tParentRunner = nullptr; multiRunner[k] = nullptr; - } - } - - createMultiRunner(false, sync); - if (sync) { - for (size_t k = 0; k < multiRunner.size(); k++) { - if (multiRunner[k]) - multiRunner[k]->synchronize(true); } } + + createMultiRunner(false, false); + tLeg = -1; tLegEquClass = 0; - tUseStartPunch=true; + tUseStartPunch = true; if (tInTeam) { - tInTeam->apply(sync, this, setTmpOnly); + tInTeam->apply(changeType, this); if (Class && Class->isQualificationFinalBaseClass()) { if (tLeg > 0 && Class == getClassRef(true)) tNeedNoCard = true; // Not qualified @@ -3145,72 +3482,70 @@ bool oRunner::apply(bool sync, pRunner src, bool setTmpOnly) { } else { if (Class && Class->hasMultiCourse()) { - pClass pc=Class; - StartTypes st=pc->getStartType(tDuplicateLeg); - if (st==STTime) { + pClass pc = Class; + StartTypes st = pc->getStartType(tDuplicateLeg); + if (st == STTime) { pCourse crs = getCourse(false); int startType = crs ? crs->getStartPunchType() : oPunch::PunchStart; if (!Card || Card->getPunchByType(startType) == 0 || !pc->hasFreeStart()) { - setStartTime(pc->getStartData(tDuplicateLeg), false, setTmpOnly); + setStartTime(pc->getStartData(tDuplicateLeg), false, changeType); tUseStartPunch = false; } } - else if (st==STChange) { - pRunner r=getPredecessor(); - int lastStart=0; - if (r && r->FinishTime>0) + else if (st == STChange) { + pRunner r = getPredecessor(); + int lastStart = 0; + if (r && r->FinishTime > 0) lastStart = r->FinishTime; - int restart=pc->getRestartTime(tDuplicateLeg); - int rope=pc->getRopeTime(tDuplicateLeg); + int restart = pc->getRestartTime(tDuplicateLeg); + int rope = pc->getRopeTime(tDuplicateLeg); - if (restart && rope && (lastStart>rope || lastStart==0)) - lastStart=restart; //Runner in restart + if (restart && rope && (lastStart > rope || lastStart == 0)) + lastStart = restart; //Runner in restart - setStartTime(lastStart, false, setTmpOnly); - tUseStartPunch=false; + setStartTime(lastStart, false, changeType); + tUseStartPunch = false; } - else if (st==STHunting) { - pRunner r=getPredecessor(); - int lastStart=0; + else if (st == STHunting) { + pRunner r = getPredecessor(); + int lastStart = 0; - if (r && r->FinishTime>0 && r->statusOK()) { - int rt=r->getRaceRunningTime(tDuplicateLeg-1); - int timeAfter=rt-pc->getTotalLegLeaderTime(r->tDuplicateLeg, true); - if (rt>0 && timeAfter>=0) - lastStart=pc->getStartData(tDuplicateLeg)+timeAfter; + if (r && r->FinishTime > 0 && r->statusOK(false)) { + int rt = r->getRaceRunningTime(tDuplicateLeg - 1); + int timeAfter = rt - pc->getTotalLegLeaderTime(r->tDuplicateLeg, false, true); + if (rt > 0 && timeAfter >= 0) + lastStart = pc->getStartData(tDuplicateLeg) + timeAfter; } - int restart=pc->getRestartTime(tDuplicateLeg); - int rope=pc->getRopeTime(tDuplicateLeg); + int restart = pc->getRestartTime(tDuplicateLeg); + int rope = pc->getRopeTime(tDuplicateLeg); - if (restart && rope && (lastStart>rope || lastStart==0)) - lastStart=restart; //Runner in restart + if (restart && rope && (lastStart > rope || lastStart == 0)) + lastStart = restart; //Runner in restart - setStartTime(lastStart, false, setTmpOnly); - tUseStartPunch=false; + setStartTime(lastStart, false, changeType); + tUseStartPunch = false; } } } - if (tLeg==-1) { - tLeg=0; - tInTeam=0; - return false; + if (tLeg == -1) { + tLeg = 0; + tInTeam = nullptr; } - else return true; } void oRunner::cloneStartTime(const pRunner r) { if (tParentRunner) tParentRunner->cloneStartTime(r); else { - setStartTime(r->getStartTime(), true, false); + setStartTime(r->getStartTime(), true, ChangeType::Update); for (size_t k=0; k < min(multiRunner.size(), r->multiRunner.size()); k++) { if (multiRunner[k]!=0 && r->multiRunner[k]!=0) - multiRunner[k]->setStartTime(r->multiRunner[k]->getStartTime(), true, false); + multiRunner[k]->setStartTime(r->multiRunner[k]->getStartTime(), true, ChangeType::Update); } - apply(false, 0, false); + apply(ChangeType::Update, nullptr); } } @@ -3223,11 +3558,22 @@ void oRunner::cloneData(const pRunner r) { } } +unsigned static nStageMaxStored = -1; -Table *oEvent::getRunnersTB()//Table mode -{ - if (tables.count("runner") == 0) { - Table *table=new Table(this, 20, L"Deltagare", "runners"); +const shared_ptr
    &oRunner::getTable(oEvent *oe) { + int sn = oe->getStageNumber(); + vector runners; + oe->getRunners(0, 0, runners, false); + for (pRunner r : runners) { + const wstring &raw = r->getDCI().getString("InputResult"); + int ns = count(raw.begin(), raw.end(), ';'); + sn = max(sn, (ns + 1) / 3); + } + sn = min(10, sn); + + if (nStageMaxStored != sn || !oe->hasTable("runner")) { + nStageMaxStored = sn; + auto table = make_shared
    (oe, 20, L"Deltagare", "runners"); table->addColumn("Id", 70, true, true); table->addColumn("Ändrad", 70, false); @@ -3246,25 +3592,35 @@ Table *oEvent::getRunnersTB()//Table mode table->addColumn("MÃ¥l", 70, false, true); table->addColumn("Status", 70, false); table->addColumn("Tid", 70, false, true); + table->addColumn("Poäng", 70, true, true); + table->addColumn("Plac.", 70, true, true); table->addColumn("Start nr.", 70, true, false); - oe->oRunnerData->buildTableCol(table); + oe->oRunnerData->buildTableCol(table.get()); + + for (unsigned k = 1; k < nStageMaxStored; k++) { + table->addColumn(lang.tl("Tid E[stageno]") + itow(k), 70, false, true); + table->addColumn(lang.tl("Status E[stageno]") + itow(k), 70, false, true); + table->addColumn(lang.tl("Poäng E[stageno]") + itow(k), 70, true); + table->addColumn(lang.tl("Plac. E[stageno]") + itow(k), 70, true); + } table->addColumn("Tid in", 70, false, true); table->addColumn("Status in", 70, false, true); table->addColumn("Poäng in", 70, true); table->addColumn("Placering in", 70, true); - tables["runner"] = table; - table->addOwnership(); + oe->setTable("runner", table); } - tables["runner"]->update(); - return tables["runner"]; + + return oe->getTable("runner"); } void oEvent::generateRunnerTableData(Table &table, oRunner *addRunner) { + oe->calculateResults({}, ResultType::ClassResult, false); + if (addRunner) { addRunner->addTableRow(table); return; @@ -3307,7 +3663,6 @@ const wstring &oRunner::getUIName() const { } } - void oRunner::addTableRow(Table &table) const { oRunner &it = *pRunner(this); @@ -3333,22 +3688,48 @@ void oRunner::addTableRow(Table &table) const table.set(row++, it, TID_START, getStartTimeS(), true); table.set(row++, it, TID_FINISH, getFinishTimeS(), true); - table.set(row++, it, TID_STATUS, getStatusS(false), true, cellSelection); - table.set(row++, it, TID_RUNNINGTIME, getRunningTimeS(), false); + table.set(row++, it, TID_STATUS, getStatusS(false, true), true, cellSelection); + table.set(row++, it, TID_RUNNINGTIME, getRunningTimeS(true), false); + int rp = getRogainingPoints(true, false); + table.set(row++, it, TID_POINTS, rp ? itow(rp) : L"", false); table.set(row++, it, TID_PLACE, getPlaceS(), false); table.set(row++, it, TID_STARTNO, itow(getStartNo()), true); row = oe->oRunnerData->fillTableCol(it, table, true); + + if (nStageMaxStored > 1) { + const wstring &raw = getDCI().getString("InputResult"); + vector spvec; + split(raw, L";", spvec); + for (unsigned j = 0; j + 1 < nStageMaxStored; j++) { + size_t k = j * 4; + int rawStat = StatusUnknown; + int rawTime = 0; + int rawPoints = 0; + int place = 0; + + if (k + 3 < spvec.size()) { + rawStat = _wtoi(spvec[k].c_str()); + rawTime = _wtoi(spvec[k + 1].c_str()); + rawPoints = _wtoi(spvec[k + 2].c_str()); + place = _wtoi(spvec[k + 3].c_str()); + } + table.set(row++, it, 200 + j, formatTime(rawTime)); + table.set(row++, it, 300 + j, oEvent::formatStatus(RunnerStatus(rawStat), false), true, cellSelection); + table.set(row++, it, 400 + j, rawPoints > 0 ? itow(rawPoints) : _EmptyWString); + table.set(row++, it, 500 + j, place > 0 ? itow(place) : _EmptyWString); + } + } table.set(row++, it, TID_INPUTTIME, getInputTimeS(), true); table.set(row++, it, TID_INPUTSTATUS, getInputStatusS(), true, cellSelection); table.set(row++, it, TID_INPUTPOINTS, itow(inputPoints), true); table.set(row++, it, TID_INPUTPLACE, itow(inputPlace), true); } -bool oRunner::inputData(int id, const wstring &input, - int inputId, wstring &output, bool noUpdate) +pair oRunner::inputData(int id, const wstring &input, + int inputId, wstring &output, bool noUpdate) { int t,s; vector mp; @@ -3358,12 +3739,58 @@ bool oRunner::inputData(int id, const wstring &input, return oe->oRunnerData->inputData(this, id, input, inputId, output, noUpdate); } + else if (id >= 200 && id <= 600) { + int type = id / 100; + int stage = id % 100; + + const wstring &raw = getDCI().getString("InputResult"); + vector spvec; + split(raw, L";", spvec); + + int nStageNow = spvec.size() / 4; + int numStage = max(nStageNow, stage + 1); + spvec.resize(numStage * 4); + + switch (type) { + case 2: + { + int time = ::convertAbsoluteTimeHMS(input, -1); + spvec[4 * stage + 1] = itow(time); + output = formatTimeHMS(time); + } + break; + case 3: { + spvec[4 * stage + 0] = itow(inputId); + output = oEvent::formatStatus(RunnerStatus(inputId), false); + } + break; + case 4: + { + int points = _wtoi(input.c_str()); + output = spvec[4 * stage + 2] = itow(points); + } + break; + case 5: + { + int place = _wtoi(input.c_str()); + output = spvec[4 * stage + 3] = itow(place); + } + break; + } + + wstring out; + unsplit(spvec, L";", out); + getDI().setString("InputResult", out); + + return make_pair(0, false); + } + switch(id) { case TID_CARD: setCardNo(_wtoi(input.c_str()), true); synchronizeAll(); output = itow(getCardNo()); - return true; + break; case TID_RUNNER: if (trim(input).empty()) throw std::exception("Tomt namn inte tillÃ¥tet."); @@ -3374,31 +3801,31 @@ bool oRunner::inputData(int id, const wstring &input, synchronizeAll(); } output = getName(); - return true; + break; break; case TID_START: setStartTimeS(input); t=getStartTime(); - evaluateCard(true, mp); + evaluateCard(true, mp, 0, ChangeType::Update); s=getStartTime(); if (s!=t) throw std::exception("Starttiden är definerad genom klassen eller löparens startstämpling."); synchronize(true); output = getStartTimeS(); - return true; + break; break; case TID_FINISH: setFinishTimeS(input); t=getFinishTime(); - evaluateCard(true, mp); + evaluateCard(true, mp, 0, ChangeType::Update); s=getFinishTime(); if (s!=t) throw std::exception("För att ändra mÃ¥ltiden mÃ¥ste löparens mÃ¥lstämplingstid ändras."); synchronize(true); output = getStartTimeS(); - return true; + break; break; case TID_COURSE: @@ -3430,18 +3857,18 @@ bool oRunner::inputData(int id, const wstring &input, break; case TID_STATUS: { - setStatus(RunnerStatus(inputId), true, false); + setStatus(RunnerStatus(inputId), true, ChangeType::Update); int s = getStatus(); - evaluateCard(true, mp); + evaluateCard(true, mp, 0, ChangeType::Update); if (s!=getStatus()) throw std::exception("Status matchar inte data i löparbrickan."); synchronize(true); - output = getStatusS(false); + output = getStatusS(false, true); } break; case TID_STARTNO: - setStartNo(_wtoi(input.c_str()), false); + setStartNo(_wtoi(input.c_str()), ChangeType::Update); synchronize(true); output = itow(getStartNo()); break; @@ -3471,13 +3898,13 @@ bool oRunner::inputData(int id, const wstring &input, break; } - return false; + return make_pair(0,false); } void oRunner::fillInput(int id, vector< pair > &out, size_t &selected) { if (id>1000) { - oe->oRunnerData->fillInput(oData, id, 0, out, selected); + oe->oRunnerData->fillInput(this, id, 0, out, selected); return; } @@ -3504,6 +3931,17 @@ void oRunner::fillInput(int id, vector< pair > &out, size_t &se oe->fillStatus(out); selected = inputStatus; } + else if (id >= 300 && id < 400) { + size_t sIndex = id - 300; + vector rs; + vector times, points, places; + getInputResults(rs, times, points, places); + oe->fillStatus(out); + if (sIndex < rs.size()) + selected = rs[sIndex]; + else + selected = StatusUnknown; + } } int oRunner::getSplitTime(int controlNumber, bool normalized) const @@ -3636,7 +4074,7 @@ bool oRunner::isAnnonumousTeamMember() const { } bool oRunner::needNoCard() const { - const_cast(this)->apply(false, 0, false); + const_cast(this)->apply(ChangeType::Quiet, nullptr); return tNeedNoCard; } @@ -3657,7 +4095,7 @@ void oRunner::getSplitTime(int courseControlId, RunnerStatus &stat, int &rt) con rt=p->getAdjustedTime(); stat = StatusOK; } - else if (p && p->Time == -1 && statusOK()) { + else if (p && p->Time == -1 && statusOK(true)) { rt = getFinishTimeAdjusted(); if (rt > 0) stat = StatusOK; @@ -3699,7 +4137,7 @@ void oRunner::fillSpeakerObject(int leg, int courseControlId, int previousContro spk.names.push_back(getName()); spk.club = getClub(); - spk.finishStatus=totalResult ? getTotalStatus() : getStatus(); + spk.finishStatus=totalResult ? getTotalStatus() : getStatusComputed(); spk.startTimeS=getStartTimeCompact(); spk.missingStartTime = tStartTime<=0; @@ -3842,11 +4280,10 @@ int oRunner::getTimeAfter(int leg) const if (t<=0) return -1; - return t-Class->getTotalLegLeaderTime(leg, true); + return t-Class->getTotalLegLeaderTime(leg, true, true); } -int oRunner::getTimeAfter() const -{ +int oRunner::getTimeAfter() const { int leg=0; if (tInTeam) leg=tLeg; @@ -3856,12 +4293,12 @@ int oRunner::getTimeAfter() const if (!Class || Class->tLeaderTime.size()<=unsigned(leg)) return -1; - int t=getRunningTime(); + int t=getRunningTime(true); if (t<=0) return -1; - return t-Class->getBestLegTime(leg); + return t - Class->getBestLegTime(leg, true); } int oRunner::getTimeAfterCourse() const @@ -3873,7 +4310,7 @@ int oRunner::getTimeAfterCourse() const if (!crs) return -1; - int t = getRunningTime(); + int t = getRunningTime(true); if (t<=0) return -1; @@ -3886,19 +4323,18 @@ int oRunner::getTimeAfterCourse() const return t - bt; } -bool oRunner::synchronizeAll() +bool oRunner::synchronizeAll(bool writeOnly) { if (tParentRunner) tParentRunner->synchronizeAll(); else { - synchronize(); + synchronize(writeOnly); for (size_t k=0;ksynchronize(); + multiRunner[k]->synchronize(writeOnly); } - if (Class && Class->isSingleRunnerMultiStage()) - if (tInTeam) - tInTeam->synchronize(false); + if (tInTeam) + tInTeam->synchronize(writeOnly); } return true; } @@ -3908,22 +4344,18 @@ const wstring &oAbstractRunner::getBib() const return getDCI().getString("Bib"); } -void oRunner::setBib(const wstring &bib, int bibNumerical, bool updateStartNo, bool tmpOnly) { - if (!tmpOnly && getBib() == bib) +void oRunner::setBib(const wstring &bib, int bibNumerical, bool updateStartNo) { + if (getBib() == bib) return; const bool freeBib = !Class || Class->getBibMode() == BibMode::BibFree; if (tParentRunner && !freeBib) - tParentRunner->setBib(bib, bibNumerical, updateStartNo, tmpOnly); + tParentRunner->setBib(bib, bibNumerical, updateStartNo); else { if (updateStartNo) - setStartNo(bibNumerical, tmpOnly); // Updates multi too. + setStartNo(bibNumerical, ChangeType::Update); // Updates multi too. - if (tmpOnly) { - tmpStore.bib = bib; - return; - } if (getDI().setString("Bib", bib)) { if (oe) oe->bibStartNoToRunnerTeam.clear(); @@ -3938,7 +4370,6 @@ void oRunner::setBib(const wstring &bib, int bibNumerical, bool updateStartNo, b } } - void oEvent::analyseDNS(vector &unknown_dns, vector &known_dns, vector &known, vector &unknown, bool &hasSetDNS) { @@ -4127,13 +4558,13 @@ void oRunner::printSplits(gdioutput &gdi) const { gdi.addStringUT(normal, lang.tl("Start: ") + getStartTimeS() + lang.tl(", MÃ¥l: ") + getFinishTimeS()); if (cls && cls->isRogaining()) { gdi.addStringUT(normal, lang.tl("Poäng: ") + - itow(getRogainingPoints(false)) + - +L" (" + lang.tl("Avdrag: ") + itow(getRogainingReduction()) + L")"); + itow(getRogainingPoints(true, false)) + + +L" (" + lang.tl("Avdrag: ") + itow(getRogainingReduction(true)) + L")"); } - wstring statInfo = lang.tl("Status: ") + getStatusS(true) + lang.tl(", Tid: ") + getRunningTimeS(); + wstring statInfo = lang.tl("Status: ") + getStatusS(true, true) + lang.tl(", Tid: ") + getRunningTimeS(true); if (withSpeed && pc && pc->getLength() > 0) { - int kmt = (getRunningTime() * 1000) / pc->getLength(); + int kmt = (getRunningTime(false) * 1000) / pc->getLength(); statInfo += L" (" + formatTime(kmt) + lang.tl(" min/km") + L")"; } if (pc && withSpeed) { @@ -4153,7 +4584,7 @@ void oRunner::printSplits(gdioutput &gdi) const { totMax = max(totMax, getPunchTime(n, false)); } } - bool moreThanHour = max(totMax, getRunningTime()) >= 3600; + bool moreThanHour = max(totMax, getRunningTime(true)) >= 3600; bool moreThanHourSplit = spMax >= 3600; const int c1=35; @@ -4186,226 +4617,289 @@ void oRunner::printSplits(gdioutput &gdi) const { set headerPos; set checkedIndex; - if (Card && pc) { - bool hasRogaining = pc->hasRogaining(); + if (Card) { + bool hasRogaining = pc ? pc->hasRogaining() : false; const int cyHead = cy; cy += int(gdi.getLineHeight()*0.9); int xcol = 0; int baseY = cy; - oPunchList &p=Card->punches; - for (oPunchList::iterator it=p.begin();it!=p.end();++it) { - if (headerPos.count(cx) == 0) { - headerPos.insert(cx); - gdi.addString("", cyHead, cx, italicSmall, "Kontroll"); - gdi.addString("", cyHead, cx+c2-55, italicSmall, "Tid"); - if (withSpeed) - gdi.addString("", cyHead, cx+c5, italicSmall|textRight, "min/km"); - } - - bool any = false; - if (it->tRogainingIndex>=0) { - const pControl c = pc->getControl(it->tRogainingIndex); - string point = c ? itos(c->getRogainingPoints()) + "p." : ""; - - gdi.addStringUT(cy, cx + c1 + 10, fontSmall, point); - any = true; - - sprintf_s(bf, "%d", it->Type); - gdi.addStringUT(cy, cx, fontSmall, bf); - int st = Card->getSplitTime(getStartTime(), &*it); - - if (st>0) - gdi.addStringUT(cy, cx + c2, fontSmall|textRight, formatTime(st)); - - gdi.addStringUT(cy, cx+c3, fontSmall, it->getTime()); - - int pt = it->getAdjustedTime(); - st = getStartTime(); - if (st>0 && pt>0 && pt>st) { - wstring punchTime = formatTime(pt-st); - gdi.addStringUT(cy, cx+c4, fontSmall|textRight, punchTime); + if (pc) { + oPunchList &p = Card->punches; + for (oPunchList::iterator it = p.begin(); it != p.end(); ++it) { + if (headerPos.count(cx) == 0) { + headerPos.insert(cx); + gdi.addString("", cyHead, cx, italicSmall, "Kontroll"); + gdi.addString("", cyHead, cx + c2 - 55, italicSmall, "Tid"); + if (withSpeed) + gdi.addString("", cyHead, cx + c5, italicSmall | textRight, "min/km"); } - cy+=int(gdi.getLineHeight()*0.9); - continue; - } + bool any = false; + if (it->tRogainingIndex >= 0) { + const pControl c = pc->getControl(it->tRogainingIndex); + string point = c ? itos(c->getRogainingPoints()) + "p." : ""; - int cid = it->tMatchControlId; - wstring punchTime; - int sp; - int controlLegIndex = -1; - if (it->isFinish(finishType)) { - // Check if the last normal control was missing, and indicate this - for (int j = pc->getNumControls() - 1; j >= 0; j--) { - pControl ctrl = pc->getControl(j); - if (ctrl && ctrl->isSingleStatusOK()) { - if (checkedIndex.count(j) == 0) { - addMissingControl(wideFormat, gdi, xcol, cx, cy, colDeltaX, numCol, baseCX); - } - break; - } - } + gdi.addStringUT(cy, cx + c1 + 10, fontSmall, point); + any = true; - gdi.addString("", cy, cx, fontSmall, "MÃ¥l"); - sp = getSplitTime(splitTimes.size(), false); - if (sp>0) { - gdi.addStringUT(cy, cx+c2, fontSmall|textRight, formatTime(sp)); - punchTime = formatTime(getRunningTime()); - } - gdi.addStringUT(cy, cx+c3, fontSmall, oe->getAbsTime(it->Time + adjust)); - any = true; - if (!punchTime.empty()) { - gdi.addStringUT(cy, cx+c4, fontSmall|textRight, punchTime); - } - controlLegIndex = pc->getNumControls(); - } - else if (it->Type>10) { //Filter away check and start - int index = -1; - if (cid>0) - index = findNextControl(ctrl, lastIndex+1, cid, offset, hasRogaining); - if (index>=0) { - if (index > lastIndex + 1) { - addMissingControl(wideFormat, gdi, xcol, cx, cy, colDeltaX, numCol, baseCX); - - /*int xx = cx; - string str = MakeDash("-"); - int posy = wideFormat ? cy : cy-int(gdi.getLineHeight()*0.4); - const int endx = cx+c5 + 5; - - while (xx < endx) { - gdi.addStringUT(posy, xx, fontSmall, str); - xx += 20; - } - - // Make a thin line for list format, otherwise, take a full place - if (wideFormat) { - gotoNextLine(gdi, xcol, cx, cy, colDeltaX, numCol, baseCX); - } - else - cy+=int(gdi.getLineHeight()*0.3);*/ - } - lastIndex = index; - - if (it->Type == startType && (index+offset) == 1) - continue; // Skip start control - - sprintf_s(bf, "%d.", index+offset+startOffset); + sprintf_s(bf, "%d", it->Type); gdi.addStringUT(cy, cx, fontSmall, bf); - sprintf_s(bf, "(%d)", it->Type); - gdi.addStringUT(cy, cx+c1, fontSmall, bf); + int st = Card->getSplitTime(getStartTime(), &*it); - controlLegIndex = it->tIndex; - checkedIndex.insert(controlLegIndex); - adjust = getTimeAdjust(controlLegIndex); - sp = getSplitTime(controlLegIndex, false); - if (sp>0) { - punchTime = getPunchTimeS(controlLegIndex, false); - gdi.addStringUT(cy, cx+c2, fontSmall|textRight, formatTime(sp)); + if (st > 0) + gdi.addStringUT(cy, cx + c2, fontSmall | textRight, formatTime(st)); + + gdi.addStringUT(cy, cx + c3, fontSmall, it->getTime()); + + int pt = it->getAdjustedTime(); + st = getStartTime(); + if (st > 0 && pt > 0 && pt > st) { + wstring punchTime = formatTime(pt - st); + gdi.addStringUT(cy, cx + c4, fontSmall | textRight, punchTime); } - } - else { - if (!it->isUsed) { - gdi.addStringUT(cy, cx, fontSmall, makeDash(L"-")); - } - sprintf_s(bf, "(%d)", it->Type); - gdi.addStringUT(cy, cx+c1, fontSmall, bf); - } - if (it->Time > 0) - gdi.addStringUT(cy, cx+c3, fontSmall, oe->getAbsTime(it->Time + adjust)); - else { - wstring str = makeDash(L"-"); - gdi.addStringUT(cy, cx+c3, fontSmall, str); + + cy += int(gdi.getLineHeight()*0.9); + continue; } - if (!punchTime.empty()) { - gdi.addStringUT(cy, cx+c4, fontSmall|textRight, punchTime); + int cid = it->tMatchControlId; + wstring punchTime; + int sp; + int controlLegIndex = -1; + if (it->isFinish(finishType)) { + // Check if the last normal control was missing, and indicate this + for (int j = pc->getNumControls() - 1; j >= 0; j--) { + pControl ctrl = pc->getControl(j); + if (ctrl && ctrl->isSingleStatusOK()) { + if (checkedIndex.count(j) == 0) { + addMissingControl(wideFormat, gdi, xcol, cx, cy, colDeltaX, numCol, baseCX); + } + break; + } + } + + gdi.addString("", cy, cx, fontSmall, "MÃ¥l"); + sp = getSplitTime(splitTimes.size(), false); + if (sp > 0) { + gdi.addStringUT(cy, cx + c2, fontSmall | textRight, formatTime(sp)); + punchTime = formatTime(getRunningTime(true)); + } + gdi.addStringUT(cy, cx + c3, fontSmall, oe->getAbsTime(it->Time + adjust)); + any = true; + if (!punchTime.empty()) { + gdi.addStringUT(cy, cx + c4, fontSmall | textRight, punchTime); + } + controlLegIndex = pc->getNumControls(); + } + else if (it->Type > 10) { //Filter away check and start + int index = -1; + if (cid > 0) + index = findNextControl(ctrl, lastIndex + 1, cid, offset, hasRogaining); + if (index >= 0) { + if (index > lastIndex + 1) { + addMissingControl(wideFormat, gdi, xcol, cx, cy, colDeltaX, numCol, baseCX); + + /*int xx = cx; + string str = MakeDash("-"); + int posy = wideFormat ? cy : cy-int(gdi.getLineHeight()*0.4); + const int endx = cx+c5 + 5; + + while (xx < endx) { + gdi.addStringUT(posy, xx, fontSmall, str); + xx += 20; + } + + // Make a thin line for list format, otherwise, take a full place + if (wideFormat) { + gotoNextLine(gdi, xcol, cx, cy, colDeltaX, numCol, baseCX); + } + else + cy+=int(gdi.getLineHeight()*0.3);*/ + } + lastIndex = index; + + if (it->Type == startType && (index + offset) == 1) + continue; // Skip start control + + sprintf_s(bf, "%d.", index + offset + startOffset); + gdi.addStringUT(cy, cx, fontSmall, bf); + sprintf_s(bf, "(%d)", it->Type); + gdi.addStringUT(cy, cx + c1, fontSmall, bf); + + controlLegIndex = it->tIndex; + checkedIndex.insert(controlLegIndex); + adjust = getTimeAdjust(controlLegIndex); + sp = getSplitTime(controlLegIndex, false); + if (sp > 0) { + punchTime = getPunchTimeS(controlLegIndex, false); + gdi.addStringUT(cy, cx + c2, fontSmall | textRight, formatTime(sp)); + } + } + else { + if (!it->isUsed) { + gdi.addStringUT(cy, cx, fontSmall, makeDash(L"-")); + } + sprintf_s(bf, "(%d)", it->Type); + gdi.addStringUT(cy, cx + c1, fontSmall, bf); + } + if (it->Time > 0) + gdi.addStringUT(cy, cx + c3, fontSmall, oe->getAbsTime(it->Time + adjust)); + else { + wstring str = makeDash(L"-"); + gdi.addStringUT(cy, cx + c3, fontSmall, str); + } + + if (!punchTime.empty()) { + gdi.addStringUT(cy, cx + c4, fontSmall | textRight, punchTime); + } + any = true; + } + + if (withSpeed && controlLegIndex >= 0 && size_t(controlLegIndex) < pc->legLengths.size()) { + int length = pc->legLengths[controlLegIndex]; + if (length > 0) { + int tempo = (sp * 1000) / length; + gdi.addStringUT(cy, cx + c5, fontSmall | textRight, formatTime(tempo)); + } + } + + if (any) { + if (!wideFormat) { + cy += int(gdi.getLineHeight()*0.9); + } + else { + gotoNextLine(gdi, xcol, cx, cy, colDeltaX, numCol, baseCX); + } } - any = true; } - - if (withSpeed && controlLegIndex>=0 && size_t(controlLegIndex) < pc->legLengths.size()) { - int length = pc->legLengths[controlLegIndex]; - if (length > 0) { - int tempo=(sp*1000)/length; - gdi.addStringUT(cy, cx+c5, fontSmall|textRight, formatTime(tempo)); + gdi.dropLine(); + if (wideFormat) { + for (int i = 0; i < numCol - 1; i++) { + RECT rc; + rc.top = baseY; + rc.bottom = cy; + rc.left = baseCX + colDeltaX * (i + 1) - 10; + rc.right = rc.left + 1; + gdi.addRectangle(rc, colorBlack); } } - if (any) { - if (!wideFormat) { - cy+=int(gdi.getLineHeight()*0.9); + if (withAnalysis) { + vector misses; + int last = ctrl.size(); + if (pc->useLastAsFinish()) + last--; + + for (int k = pc->useFirstAsStart() ? 1 : 0; k < last; k++) { + int missed = getMissedTime(k); + if (missed > 0) { + misses.push_back(pc->getControlOrdinal(k) + L"/" + formatTime(missed)); + } + } + if (misses.size() == 0) { + vector rOut; + oe->getRunners(0, pc->getId(), rOut, false); + int count = 0; + for (size_t k = 0; k < rOut.size(); k++) { + if (rOut[k]->getCard()) + count++; + } + + if (count < 3) + gdi.addString("", normal, "Underlag saknas för bomanalys."); + else + gdi.addString("", normal, "Inga bommar registrerade."); } else { - gotoNextLine(gdi, xcol, cx, cy, colDeltaX, numCol, baseCX); + wstring out = lang.tl("Tidsförluster (kontroll-tid): "); + for (size_t k = 0; k < misses.size(); k++) { + if (out.length() > (wideFormat ? 80u : (withSpeed ? 40u : 35u))) { + gdi.addStringUT(normal, out); + out.clear(); + } + out += misses[k]; + if (k < misses.size() - 1) + out += L", "; + else + out += L"."; + } + gdi.addStringUT(fontSmall, out); } } } - gdi.dropLine(); - if (wideFormat) { - for (int i = 0; i < numCol-1; i++) { - RECT rc; - rc.top = baseY; - rc.bottom = cy; - rc.left = baseCX + colDeltaX*(i+1)-10; - rc.right = rc.left + 1; - gdi.addRectangle(rc, colorBlack); + else { + int index = 0; + int lastTime = 0; + + for (auto &it : Card->punches) { + if (headerPos.count(cx) == 0) { + headerPos.insert(cx); + gdi.addString("", cyHead, cx, italicSmall, "Kontroll"); + gdi.addString("", cyHead, cx + c2 - 55, italicSmall, "Tid"); + } + + bool any = false; + wstring punchTime; + if (it.isFinish(finishType)) { + gdi.addString("", cy, cx, fontSmall, "MÃ¥l"); + int rt = it.Time - tStartTime; + if (rt > 0) { + gdi.addStringUT(cy, cx + c2, fontSmall | textRight, formatTime(rt - lastTime)); + punchTime = formatTime(getRunningTime(true)); + } + gdi.addStringUT(cy, cx + c3, fontSmall, oe->getAbsTime(it.Time)); + any = true; + if (!punchTime.empty()) { + gdi.addStringUT(cy, cx + c4, fontSmall | textRight, punchTime); + } + } + else if (it.Type > 10 && it.Type != startType) { //Filter away check and start + sprintf_s(bf, "%d.", ++index); + gdi.addStringUT(cy, cx, fontSmall, bf); + sprintf_s(bf, "(%d)", it.Type); + gdi.addStringUT(cy, cx + c1, fontSmall, bf); + + if (it.Time > 0) { + int rt = it.Time - tStartTime; + punchTime = formatTime(rt); + gdi.addStringUT(cy, cx + c2, fontSmall | textRight, formatTime(rt - lastTime)); + lastTime = rt; + } + + if (it.Time > 0) + gdi.addStringUT(cy, cx + c3, fontSmall, oe->getAbsTime(it.Time)); + else { + wstring str = makeDash(L"-"); + gdi.addStringUT(cy, cx + c3, fontSmall, str); + } + + if (!punchTime.empty()) { + gdi.addStringUT(cy, cx + c4, fontSmall | textRight, punchTime); + } + any = true; + } + + if (any) { + if (!wideFormat) { + cy += int(gdi.getLineHeight()*0.9); + } + else { + gotoNextLine(gdi, xcol, cx, cy, colDeltaX, numCol, baseCX); + } + } } } - if (withAnalysis) { - vector misses; - int last = ctrl.size(); - if (pc->useLastAsFinish()) - last--; + oe->calculateResults({ getClassId(true) }, oEvent::ResultType::ClassResult); + if (hasInputData()) + oe->calculateResults({ getClassId(true) }, oEvent::ResultType::TotalResult); + if (tInTeam) + oe->calculateTeamResults({ getClassId(true) }, oEvent::ResultType::ClassResult); - for (int k = pc->useFirstAsStart() ? 1 : 0; k < last; k++) { - int missed = getMissedTime(k); - if (missed>0) { - misses.push_back(pc->getControlOrdinal(k) + L"/" + formatTime(missed)); - } - } - if (misses.size()==0) { - vector rOut; - oe->getRunners(0, pc->getId(), rOut, false); - int count = 0; - for (size_t k = 0; k < rOut.size(); k++) { - if (rOut[k]->getCard()) - count++; - } - - if (count < 3) - gdi.addString("", normal, "Underlag saknas för bomanalys."); - else - gdi.addString("", normal, "Inga bommar registrerade."); - } - else { - wstring out = lang.tl("Tidsförluster (kontroll-tid): "); - for (size_t k = 0; k (wideFormat ? 80u : (withSpeed ? 40u : 35u))) { - gdi.addStringUT(normal, out); - out.clear(); - } - out += misses[k]; - if (k < misses.size()-1) - out += L", "; - else - out += L"."; - } - gdi.addStringUT(fontSmall, out); - } - } - - if (withResult && statusOK()) { + if (withResult && statusOK(true)) { gdi.dropLine(0.5); - oe->calculateResults({ getClassId(true) }, oEvent::ResultType::ClassResult); - if (hasInputData()) - oe->calculateResults({ getClassId(true) }, oEvent::ResultType::TotalResult); - if (tInTeam) - oe->calculateTeamResults(tLeg, true); - if (cls && cls->isRogaining()) - oe->calculateRogainingResults({cls->getId()}); wstring place = oe->formatListString(lRunnerGeneralPlace, pRunner(this), L"%s"); wstring timestatus; if (tInTeam || hasInputData()) { @@ -4429,33 +4923,13 @@ void oRunner::printSplits(gdioutput &gdi) const { gdi.popX(); } } + gdi.dropLine(); vector< pair > lines; oe->getExtraLines("SPExtra", lines); for (size_t k = 0; k < lines.size(); k++) { - /*wstring ws = lines[k].first, wsOut; - size_t parS = ws.find_first_of('['); - while (parS != wstring::npos) { - size_t parE = ws.find_first_of(']'); - if (parE != wstring::npos && parE > parS) { - wsOut += ws.substr(0, parS); - wstring cmd = ws.substr(parS + 1, parE-parS-1); - ws = ws.substr(parE + 1); - parS = ws.find_first_of('['); - - auto type = MetaList::getTypeFromSymbol(cmd); - if (type == EPostType::lNone) - wsOut += L"{Error: " + cmd + L"}"; - else { - wsOut += oe->formatListString(type, pRunner(this)); - } - } - else - break; // Error - } - wsOut += ws;*/ gdi.addStringUT(lines[k].second, formatExtraLine(pRunner(this), lines[k].first)); } if (lines.size()>0) @@ -4752,7 +5226,7 @@ oRunner::BibAssignResult oRunner::autoAssignBib() { wstring nBib = bib; if (allBibs.count(nBib)) return BibAssignResult::Failed; // Bib already use. Do not allow duplicates. - setBib(nBib, maxbib+1, true, false); + setBib(nBib, maxbib+1, true); return BibAssignResult::Assigned; } return BibAssignResult::NoBib; @@ -5143,7 +5617,7 @@ int oRunner::getLegTimeAfterAcc(int ctrlNo) const { } int oRunner::getTimeWhenPlaceFixed() const { - if (!Class || !statusOK()) + if (!Class || !statusOK(true)) return -1; #ifndef MEOSDB @@ -5155,7 +5629,7 @@ int oRunner::getTimeWhenPlaceFixed() const { #endif int lst = Class->tResultInfo[tLeg].lastStartTime; - return lst > 0 ? lst + getRunningTime() : lst; + return lst > 0 ? lst + getRunningTime(false) : lst; } @@ -5222,12 +5696,12 @@ pRunner oRunner::getMatchedRunner(const SICard &sic) const { return pRunner(this); } -int oRunner::getTotalRunningTime(int time, bool includeInput) const { +int oRunner::getTotalRunningTime(int time, bool computedTime, bool includeInput) const { if (tStartTime < 0) return 0; if (tInTeam == 0 || tLeg == 0) { if (time == FinishTime) - return getRunningTime() + (includeInput ? inputTime : 0); + return getRunningTime(computedTime) + (includeInput ? inputTime : 0); else return time-tStartTime + (includeInput ? inputTime : 0); } @@ -5236,7 +5710,7 @@ int oRunner::getTotalRunningTime(int time, bool includeInput) const { return 0; if (time == FinishTime) { - return tInTeam->getLegRunningTime(tLeg, includeInput); // Use the official running time in this case (which works with parallel legs) + return tInTeam->getLegRunningTime(getParResultLeg(), computedTime, includeInput); // Use the official running time in this case (which works with parallel legs) } int baseleg = tLeg; @@ -5250,7 +5724,7 @@ int oRunner::getTotalRunningTime(int time, bool includeInput) const { leg--; } - int pt = leg>=0 ? tInTeam->getLegRunningTime(leg, includeInput) : 0; + int pt = leg>=0 ? tInTeam->getLegRunningTime(leg, computedTime, includeInput) : 0; if (pt>0) return pt + time - tStartTime; else if (tInTeam->tStartTime > 0) @@ -5298,32 +5772,35 @@ wstring oRunner::getCompleteIdentification(bool includeExtra) const { } RunnerStatus oAbstractRunner::getTotalStatus() const { - if (tStatus == StatusUnknown && inputStatus != StatusNotCompetiting) + RunnerStatus st = getStatusComputed(); + if (st == StatusUnknown && inputStatus != StatusNotCompetiting) return StatusUnknown; else if (inputStatus == StatusUnknown) return StatusDNS; - return max(tStatus, inputStatus); + return max(st, inputStatus); } RunnerStatus oRunner::getTotalStatus() const { - if (tStatus == StatusUnknown && inputStatus != StatusNotCompetiting) + RunnerStatus stm = getStatusComputed(); + if (stm == StatusUnknown && inputStatus != StatusNotCompetiting) return StatusUnknown; else if (inputStatus == StatusUnknown) return StatusDNS; + int leg = getParResultLeg(); - if (tInTeam == 0 || tLeg == 0) - return max(tStatus, inputStatus); + if (tInTeam == 0 || leg == 0) + return max(stm, inputStatus); else { - RunnerStatus st = tInTeam->getLegStatus(tLeg-1, true); + RunnerStatus st = tInTeam->getLegStatus(leg-1, true, true); - if (tLeg + 1 == tInTeam->getNumRunners()) - st = max(st, tInTeam->getStatus()); + if (leg + 1 == tInTeam->getNumRunners()) + st = max(st, tInTeam->getStatusComputed()); if (st == StatusOK || st == StatusUnknown) - return tStatus; + return stm; else - return max(max(tStatus, st), inputStatus); + return max(max(stm, st), inputStatus); } } @@ -5391,14 +5868,14 @@ void oRunner::setInputData(const oRunner &r) { oDataConstInterface src = r.getDCI(); if (r.tStatus != StatusNotCompetiting) { - inputTime = r.getTotalRunningTime(r.FinishTime, true); + inputTime = r.getTotalRunningTime(r.FinishTime, true, true); inputStatus = r.getTotalStatus(); if (r.tInTeam) { // If a team has not status ok, transfer this status to all team members. if (r.tInTeam->getTotalStatus() > StatusOK) inputStatus = r.tInTeam->getTotalStatus(); } - inputPoints = r.getRogainingPoints(true) + r.inputPoints; - inputPlace = r.tTotalPlace < 99000 ? r.tTotalPlace : 0; + inputPoints = r.getRogainingPoints(true, true); + inputPlace = r.tTotalPlace.value; } else { // Copy input @@ -5426,6 +5903,9 @@ void oRunner::setInputData(const oRunner &r) { dest.setInt("Fee", src.getInt("Fee")); dest.setInt("Paid", src.getInt("Paid")); dest.setInt("Taxable", src.getInt("Taxable")); + + int sn = r.getEvent()->getStageNumber(); + addToInputResult(sn-1, &r); } } @@ -5654,22 +6134,35 @@ void oAbstractRunner::setPointAdjustment(int adjust) { getDI().setInt("PointAdjust", adjust); } -int oRunner::getRogainingPoints(bool multidayTotal) const { +int oRunner::getRogainingPoints(bool computed, bool multidayTotal) const { + int pb = tRogainingPoints; + if (computed && tComputedPoints >= 0) + pb = tComputedPoints; + if (multidayTotal) - return inputPoints + tRogainingPoints; + return inputPoints + pb; else - return tRogainingPoints; + return pb; } -int oRunner::getRogainingReduction() const { +int oRunner::getRogainingReduction(bool computed) const { + if (computed && tComputedPoints >= 0 && tRogainingPointsGross >= tComputedPoints) + return tRogainingPointsGross - tComputedPoints; return tReduction; } -int oRunner::getRogainingPointsGross() const { +int oRunner::getRogainingPointsGross(bool computed) const { return tRogainingPointsGross; } -int oRunner::getRogainingOvertime() const { +int oRunner::getRogainingOvertime(bool computed) const { + if (computed) { + int rt = getRunningTime(true); + pCourse pc = getCourse(false); + if (pc && rt > 0 && pc->getMaximumRogainingTime() > 0) { + return max(0, rt - pc->getMaximumRogainingTime()); + } + } return tRogainingOvertime; } @@ -5680,7 +6173,8 @@ void oAbstractRunner::TempResult::reset() { place = 0; startTime = 0; status = StatusUnknown; - internalScore = 0; + internalScore.first = 0; + internalScore.second = 0; } oAbstractRunner::TempResult::TempResult() { @@ -5707,7 +6201,6 @@ oAbstractRunner::TempResult &oAbstractRunner::getTempResult() { return tmpResult; } - void oAbstractRunner::setTempResultZero(const TempResult &tr) { tmpResult = tr; } @@ -5820,10 +6313,57 @@ bool oAbstractRunner::isEntryTouched() const { return tEntryTouched; } + +// Get results from all previous stages +void oAbstractRunner::getInputResults(vector &st, + vector ×, + vector &points, + vector &places) { + const wstring &raw = getDCI().getString("InputResult"); + vector spvec; + split(raw, L";", spvec); + + int nStageNow = spvec.size() / 4; + st.resize(nStageNow); + times.resize(nStageNow); + points.resize(nStageNow); + places.resize(nStageNow); + for (int j = 0; j < nStageNow; j++) { + st[j] = RunnerStatus(_wtoi(spvec[j * 4 + 0].c_str())); + times[j] = _wtoi(spvec[j * 4 + 1].c_str()); + points[j] = _wtoi(spvec[j * 4 + 2].c_str()); + places[j] = _wtoi(spvec[j * 4 + 3].c_str()); + } +} + +// Add current result to input result. Only use when transferring to next stage +void oAbstractRunner::addToInputResult(int thisStageNo, const oAbstractRunner *src) { + int p = src->getPlace(); + int rt = src->getRunningTime(true); + RunnerStatus st = src->getStatusComputed(); + int pt = src->getRogainingPoints(true, false); + + const wstring &raw = src->getDCI().getString("InputResult"); + vector spvec; + split(raw, L";", spvec); + + int nStageNow = spvec.size() / 4; + int numStage = max(nStageNow, thisStageNo + 1); + spvec.resize(numStage * 4); + spvec[4*thisStageNo] = itow(st); + spvec[4*thisStageNo+1] = itow(rt); + spvec[4*thisStageNo+2] = itow(pt); + spvec[4*thisStageNo+3] = itow(p); + + wstring out; + unsplit(spvec, L";", out); + getDI().setString("InputResult", out); +} + int oRunner::getTotalTimeInput() const { if (tInTeam) { if (getLegNumber()>0) { - return tInTeam->getLegRunningTime(getLegNumber()-1, true); + return tInTeam->getLegRunningTime(getLegNumber()-1, true, true); } else { return tInTeam->getInputTime(); @@ -5840,7 +6380,7 @@ RunnerStatus oRunner::getTotalStatusInput() const { if (tInTeam) { const pTeam t = tInTeam; if (getLegNumber()>0) { - inStatus = t->getLegStatus(getLegNumber()-1, true); + inStatus = t->getLegStatus(getLegNumber()-1, true, true); } else { inStatus = t->getInputStatus(); @@ -6050,3 +6590,88 @@ int oRunner::getCheckTime() const { return 0; } + +const pair oRunner::getRaceInfo() { + pair res; + RunnerStatus baseStatus = getStatus(); + if (baseStatus != StatusUnknown) { + int p = getPlace(); + int rtComp = getRunningTime(true); + int rtActual = getRunningTime(false); + int pointsActual = getRogainingPoints(false, false); + int pointsComp = getRogainingPoints(true, false); + RunnerStatus compStatus = getStatusComputed(); + bool ok = compStatus == StatusOK || compStatus == StatusOutOfCompetition + || compStatus == StatusNoTiming; + res.second = ok ? 1 : -1; + if (compStatus == baseStatus && rtComp == rtActual && pointsComp == pointsActual) { + res.first = lang.tl(getProblemDescription()); + if (ok && p > 0) + res.first = lang.tl("Placering: ") + itow(p) + L"."; + } + else { + if (ok && rtComp != rtActual || pointsActual != pointsComp) { + res.first += lang.tl("Resultat: "); + if (compStatus != baseStatus) + res.first = oe->formatStatus(compStatus, true) + L", "; + if (pointsActual != pointsComp) + res.first += itow(pointsComp) + L", "; + + res.first += formatTime(rtComp); + + if (p > 0) + res.first += L" (" + itow(p) + L")"; + } + else if (!ok && compStatus != baseStatus) { + res.first = lang.tl("Resultat: ") + oe->formatStatus(compStatus, true); + } + + if (ok && getRogainingReduction(true) > 0) { + tProblemDescription = L"Tidsavdrag: X poäng.#" + itow(getRogainingReduction(true)); + } + + if (!getProblemDescription().empty()) { + if (!res.first.empty()) { + if (res.first.back() != ')') + res.first += L", "; + else + res.first += L" "; + } + res.first += lang.tl(getProblemDescription()); + } + } + } + else { + vector pl; + oe->synchronizeList(oListId::oLPunchId); + oe->getPunchesForRunner(Id, true, pl); + if (!pl.empty()) { + res.first = lang.tl(L"Senast sedd: X vid Y.#" + + oe->getAbsTime(pl.back()->Time) + + L"#" + pl.back()->getType()); + } + } + + return res; +} + +int oRunner::getParResultLeg() const { + if (!tInTeam || !Class) + return 0; + + size_t leg = tLeg; + while (leg < tInTeam->Runners.size()) { + if (Class->isParallel(leg + 1) && tInTeam->getRunner(leg + 1)) + leg++; + else + break; + } + return leg; +} + +bool oRunner::isResultUpdated(bool totalResult) const { + if (totalResult) + return !tPlace.isOld(*oe); + else + return !tTotalPlace.isOld(*oe); +} diff --git a/code/oRunner.h b/code/oRunner.h index 038b62b..17b18f6 100644 --- a/code/oRunner.h +++ b/code/oRunner.h @@ -11,7 +11,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -37,9 +37,57 @@ #include "oClub.h" #include "oClass.h" #include "oCard.h" -#include "oSpeaker.h" #include "oDataContainer.h" +enum RunnerStatus { + StatusOK = 1, StatusDNS = 20, StatusCANCEL = 21, StatusOutOfCompetition = 15, StatusMP = 3, + StatusDNF = 4, StatusDQ = 5, StatusMAX = 6, StatusNoTiming = 2, + StatusUnknown = 0, StatusNotCompetiting = 99 +}; + +/** Returns true for a status that might or might not indicate a result. */ +template +bool isPossibleResultStatus(RunnerStatus st) { + return st == StatusNoTiming || st == StatusOutOfCompetition; +} + +template +vector getAllRunnerStatus() { + return { StatusOK, StatusDNS, StatusCANCEL, StatusOutOfCompetition, StatusMP, + StatusDNF, StatusDQ, StatusMAX, + StatusUnknown, StatusNotCompetiting , StatusNoTiming}; +} + +#include "oSpeaker.h" + +extern char RunnerStatusOrderMap[100]; + +enum SortOrder { + ClassStartTime, + ClassTeamLeg, + ClassResult, + ClassDefaultResult, + ClassCourseResult, + ClassTotalResult, + ClassTeamLegResult, + ClassFinishTime, + ClassStartTimeClub, + ClassPoints, + ClassLiveResult, + ClassKnockoutTotalResult, + SortByName, + SortByLastName, + SortByFinishTime, + SortByFinishTimeReverse, + SortByStartTime, + SortByStartTimeClass, + CourseResult, + CourseStartTime, + SortByEntryTime, + Custom, + SortEnumLastItem +}; + class oRunner; typedef oRunner* pRunner; typedef const oRunner* cRunner; @@ -62,22 +110,26 @@ protected: int tStartTime; int FinishTime; + mutable int tComputedTime = 0; + RunnerStatus status; RunnerStatus tStatus; + mutable RunnerStatus tComputedStatus = RunnerStatus::StatusUnknown; + mutable int tComputedPoints = -1; - /** Temporary storage for set operations to be applied later*/ - struct TempSetStore { - int startTime; - RunnerStatus status; - int startNo; - wstring bib; - }; - - TempSetStore tmpStore; - - void resetTmpStore(); - + vector> dynamicData; public: + /** Encode status as a two-letter code, non-translated*/ + static const wstring &encodeStatus(RunnerStatus st, bool allowError = false); + + /** Decode the two-letter code of above. Returns unknown if not understood*/ + static RunnerStatus decodeStatus(const wstring &stat); + + /** Return true if the status is of type indicating a result. */ + static bool isResultStatus(RunnerStatus rs); + + /** Returns true if the result in the current state is up-to-date. */ + virtual bool isResultUpdated(bool totalResult) const = 0; struct TempResult { private: @@ -86,7 +138,7 @@ public: int timeAfter; int points; int place; - int internalScore; + pair internalScore; vector outputTimes; vector outputNumbers; RunnerStatus status; @@ -100,7 +152,10 @@ public: int getPoints() const {return points;} int getPlace() const {return place;} RunnerStatus getStatus() const {return status;} - + bool isStatusOK() const { + return status == StatusOK || + ((status == StatusOutOfCompetition || status == StatusNoTiming) && runningTime > 0); + } const wstring &getStatusS(RunnerStatus inputStatus) const; const wstring &getPrintPlaceS(bool withDot) const; const wstring &getRunningTimeS(int inputTime) const; @@ -121,9 +176,6 @@ public: protected: TempResult tmpResult; - // Sets result to object. Returns true if status/time changed - bool setTmpStore(); - mutable int tTimeAdjustment; mutable int tPointAdjustment; mutable int tAdjustDataRevision; @@ -183,6 +235,8 @@ public: FlagUpdateName = 32, FlagAutoDNS = 64, // The competitor was set to DNS by the in-forest algorithm FlagAddedViaAPI = 128, // Added by the REST api entry. + FlagOutsideCompetition = 256, + FlagNoTiming = 512, // No timing requested }; bool hasFlag(TransferFlags flag) const; @@ -245,7 +299,7 @@ public: wstring getInfo() const; - virtual bool apply(bool sync, pRunner src, bool setTmpOnly) = 0; + virtual void apply(ChangeType ct, pRunner src) = 0; //Get time after on leg/for race virtual int getTimeAfter(int leg) const = 0; @@ -264,7 +318,7 @@ public: /** Sets start time, if updatePermanent is true, the stored start time is updated, otherwise the value is considered deduced. */ - bool setStartTime(int t, bool updatePermanent, bool setTmpOnly, bool recalculate = true); + bool setStartTime(int t, bool updatePermanent, ChangeType changeType, bool recalculate = true); void setStartTimeS(const wstring &t); const pClub getClubRef() const {return Club;} @@ -293,12 +347,12 @@ public: virtual void setClassId(int id, bool isManualUpdate); virtual int getStartNo() const {return StartNo;} - virtual void setStartNo(int no, bool storeTmpOnly); + virtual void setStartNo(int no, ChangeType changeType); - // Start number is equal to bib-no, but bib + // Do not assume start number is equal to bib-no, Bib // is only set when it should be shown in lists etc. const wstring &getBib() const; - virtual void setBib(const wstring &bib, int numericalBib, bool updateStartNo, bool setTmpOnly) = 0; + virtual void setBib(const wstring &bib, int numericalBib, bool updateStartNo) = 0; int getEncodedBib() const; virtual int getStartTime() const {return tStartTime;} @@ -306,43 +360,58 @@ public: int getFinishTimeAdjusted() const {return getFinishTime() + getTimeAdjustment();} - virtual int getRogainingPoints(bool multidayTotal) const = 0; - virtual int getRogainingReduction() const = 0; - virtual int getRogainingOvertime() const = 0; - virtual int getRogainingPointsGross() const = 0; + virtual int getRogainingPoints(bool computed, bool multidayTotal) const = 0; + virtual int getRogainingReduction(bool computed) const = 0; + virtual int getRogainingOvertime(bool computed) const = 0; + virtual int getRogainingPointsGross(bool computed) const = 0; virtual const wstring &getStartTimeS() const; virtual const wstring &getStartTimeCompact() const; virtual const wstring &getFinishTimeS() const; - virtual const wstring &getTotalRunningTimeS() const; - virtual const wstring &getRunningTimeS() const; - virtual int getRunningTime() const; + const wstring &getTotalRunningTimeS() const; + const wstring &getRunningTimeS(bool computedTime) const; + virtual int getRunningTime(bool computedTime) const; /// Get total running time (including earlier stages / races) virtual int getTotalRunningTime() const; virtual int getPrelRunningTime() const; - virtual wstring getPrelRunningTimeS() const; + + wstring getPlaceS() const; + wstring getPrintPlaceS(bool withDot) const; - virtual wstring getPlaceS() const; - virtual wstring getPrintPlaceS(bool withDot) const; - - virtual wstring getTotalPlaceS() const; - virtual wstring getPrintTotalPlaceS(bool withDot) const; + wstring getTotalPlaceS() const; + wstring getPrintTotalPlaceS(bool withDot) const; virtual int getPlace() const = 0; virtual int getTotalPlace() const = 0; - virtual RunnerStatus getStatus() const {return tStatus;} - inline bool statusOK() const {return tStatus==StatusOK;} - inline bool prelStatusOK() const {return tStatus==StatusOK || (tStatus == StatusUnknown && getRunningTime() > 0);} - + RunnerStatus getStatusComputed() const { return tComputedStatus != StatusUnknown ? tComputedStatus : tStatus; } + virtual RunnerStatus getStatus() const { return tStatus;} + inline bool statusOK(bool computed) const {return (computed ? getStatusComputed() : tStatus) == StatusOK;} + inline bool prelStatusOK(bool computed, bool includeOutsideCompetition) const { + bool ok = statusOK(computed) || (tStatus == StatusUnknown && getRunningTime(false) > 0); + if (!ok && includeOutsideCompetition) { + RunnerStatus st = (computed ? getStatusComputed() : tStatus); + ok = (st == StatusOutOfCompetition || st == StatusNoTiming) && getRunningTime(false) > 0; + } + return ok; + } + // Returns true if the competitor has a definite result + bool hasResult() const { + RunnerStatus st = this->getStatusComputed(); + if (st == StatusUnknown || st == StatusNotCompetiting) + return false; + if (isPossibleResultStatus(st)) + return getRunningTime(false) > 0; + else + return true; + } /** Sets the status. If updatePermanent is true, the stored start time is updated, otherwise the value is considered deduced. - if setTmp is true, the status is only stored in a temporary container. */ - virtual bool setStatus(RunnerStatus st, bool updatePermanent, bool setTmp, bool recalculate = true); + bool setStatus(RunnerStatus st, bool updatePermanent, ChangeType changeType, bool recalculate = true); /** Returns the ranking of the runner or the team (first runner in it?) */ virtual int getRanking() const = 0; @@ -350,7 +419,12 @@ public: /// Get total status for this running (including team/earlier races) virtual RunnerStatus getTotalStatus() const; - const wstring &getStatusS(bool formatForPrint) const; + // Get results from all previous stages + void getInputResults(vector &st, vector ×, vector &points, vector &places); + // Add current result to input result. Only use when transferring to next stage. ThisStageNumber is zero indexed. + void addToInputResult(int thisStageNo, const oAbstractRunner *src); + + const wstring &getStatusS(bool formatForPrint, bool computedStatus) const; wstring getIOFStatusS() const; const wstring &getTotalStatusS(bool formatForPrint) const; @@ -427,8 +501,9 @@ protected: //Can be changed by apply mutable DynamicValue tPlace; - mutable int tCoursePlace; - mutable int tTotalPlace; + mutable DynamicValue tCoursePlace; + mutable DynamicValue tCourseClassPlace; + mutable DynamicValue tTotalPlace; mutable int tLeg; mutable int tLegEquClass; mutable pTeam tInTeam; @@ -499,7 +574,7 @@ protected: mutable vector tAfterLeg; mutable vector tPlaceLegAcc; mutable vector tAfterLegAcc; - + // Used to calculate temporary split time results struct OnCourseResult { OnCourseResult(int courseControlId, @@ -544,13 +619,20 @@ protected: class RaceIdFormatter : public oDataDefiner { public: - const wstring &formatData(const oBase *obj) const; - wstring setData(oBase *obj, const wstring &input) const; - int addTableColumn(Table *table, const string &description, int minWidth) const; + const wstring &formatData(const oBase *obj) const override; + pair setData(oBase *obj, const wstring &input, wstring &output, int inputId) const override; + int addTableColumn(Table *table, const string &description, int minWidth) const override; }; - static RaceIdFormatter raceIdFormatter; - + class RunnerReference : public oDataDefiner { + public: + const wstring &formatData(const oBase *obj) const override; + pair setData(oBase *obj, const wstring &input, wstring &output, int inputId) const override; + void fillInput(const oBase *obj, vector> &out, size_t &selected) const override; + int addTableColumn(Table *table, const string &description, int minWidth) const override; + CellType getCellType() const override; + }; + /** Get internal data buffers for DI */ oDataContainer &getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const; @@ -564,8 +646,14 @@ protected: bool isHiredCard(int card) const; public: + + static const shared_ptr
    &getTable(oEvent *oe); + + // Get the leg defineing parallel results for this runner (in a team) + int getParResultLeg() const; + // Returns true if there are radio control results, provided result calculation oEvent::ResultType::PreliminarySplitResults was invoked. - bool hasOnCourseResult() const { return !tOnCourseResults.empty() || getFinishTime() > 0 || getStatus() != RunnerStatus::StatusUnknown; } + bool hasOnCourseResult() const { return !tOnCourseResults.empty() || getFinishTime() > 0 || hasResult(); } /** Returns a check time (or zero for no time). */ int getCheckTime() const; @@ -582,7 +670,7 @@ public: const wstring &getNameRaw() const {return sName;} virtual const wstring &getName() const; const wstring &getNameLastFirst() const; - static void getRealName(const wstring &input, wstring &output); + void getRealName(const wstring &input, wstring &output) const; /** Returns true if this runner can use the specified card, or false if it conflicts with the card of the other runner. */ @@ -592,8 +680,10 @@ public: int getRanking() const; + bool isResultUpdated(bool totalResult) const override; + /** Returns true if the team / runner has a valid start time available*/ - virtual bool startTimeAvailable() const; + bool startTimeAvailable() const override; /** Get a total input time from previous legs and stages*/ int getTotalTimeInput() const; @@ -634,10 +724,10 @@ public: pTeam getTeam() {return tInTeam;} /// Get total running time for multi/team runner at the given time - int getTotalRunningTime(int time, bool includeInput) const; + int getTotalRunningTime(int time, bool computedTime, bool includeInput) const; - // Get total running time at finish time (@override) - int getTotalRunningTime() const; + // Get total running time at finish time + int getTotalRunningTime() const override; //Get total running time after leg int getRaceRunningTime(int leg) const; @@ -646,18 +736,18 @@ public: wstring getCompleteIdentification(bool includeExtra = true) const; /// Get total status for this running (including team/earlier races) - RunnerStatus getTotalStatus() const; + RunnerStatus getTotalStatus() const override; // Return the runner in a multi-runner set matching the card, if course type is extra pRunner getMatchedRunner(const SICard &sic) const; - int getRogainingPoints(bool multidayTotal) const; - int getRogainingPointsGross() const; - int getRogainingReduction() const; - int getRogainingOvertime() const; + int getRogainingPoints(bool computed, bool multidayTotal) const override; + int getRogainingPointsGross(bool computed) const override; + int getRogainingReduction(bool computed) const override; + int getRogainingOvertime(bool computed) const override; const wstring &getProblemDescription() const {return tProblemDescription;} - + const pair getRaceInfo(); // Leg statistics access methods wstring getMissedTimeS() const; wstring getMissedTimeS(int ctrlNo) const; @@ -714,17 +804,17 @@ public: // is only set when it should be shown in lists etc. // Need not be so for teams. Course depends on start number, // which should be more stable. - void setBib(const wstring &bib, int bibNumerical, bool updateStartNo, bool setTmpOnly); - void setStartNo(int no, bool setTmpOnly); + void setBib(const wstring &bib, int bibNumerical, bool updateStartNo) override; + void setStartNo(int no, ChangeType changeType) override; // Update and synch start number for runner and team. void updateStartNo(int no); pRunner nextNeedReadout() const; // Synchronize this runner and parents/sibllings and team - bool synchronizeAll(); + bool synchronizeAll(bool writeOnly = false); - void setFinishTime(int t); + void setFinishTime(int t) override; int getTimeAfter(int leg) const; int getTimeAfter() const; int getTimeAfterCourse() const; @@ -742,7 +832,7 @@ public: bool needNoCard() const; int getPlace() const; - int getCoursePlace() const; + int getCoursePlace(bool perClass) const; int getTotalPlace() const; // Normalized = true means permuted to the unlooped version of the course @@ -769,11 +859,11 @@ public: wstring getNamedSplitS(int controlNumber) const; void addTableRow(Table &table) const; - bool inputData(int id, const wstring &input, - int inputId, wstring &output, bool noUpdate); - void fillInput(int id, vector< pair > &elements, size_t &selected); + pair inputData(int id, const wstring &input, + int inputId, wstring &output, bool noUpdate) override; + void fillInput(int id, vector< pair > &elements, size_t &selected) override; - bool apply(bool sync, pRunner src, bool setTmpOnly); + void apply(ChangeType changeType, pRunner src) override; void resetPersonalData(); //Local user data. No Update. @@ -794,7 +884,7 @@ public: bool operator<(const oRunner &c) const; bool static CompareCardNumber(const oRunner &a, const oRunner &b) { return a.cardNumber < b.cardNumber; } - bool evaluateCard(bool applyTeam, vector &missingPunches, int addpunch=0, bool synchronize=false); + bool evaluateCard(bool applyTeam, vector &missingPunches, int addPunch, ChangeType changeType); void addPunches(pCard card, vector &missingPunches); /** Get split time for a controlId and optionally controlIndex on course (-1 means unknown, uses the first occurance on course)*/ diff --git a/code/oTeam.cpp b/code/oTeam.cpp index bca0e41..8592545 100644 --- a/code/oTeam.cpp +++ b/code/oTeam.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -261,7 +261,7 @@ void oTeam::setRunner(unsigned i, pRunner r, bool sync) if (r) { if (tStatus == StatusDNS) - setStatus(StatusUnknown, true, false); + setStatus(StatusUnknown, true, ChangeType::Update); r->getDI().setInt("RaceId", oldRaceId); r->tInTeam=this; r->tLeg=i; @@ -297,8 +297,10 @@ void oTeam::setRunnerInternal(int k, pRunner r) return; } + int specifiedCourse = 0; pRunner rOld = Runners[k]; if (rOld) { + int specifiedCourse = rOld->getCourseId(); assert(rOld->tInTeam == 0 || rOld->tInTeam == this); rOld->tInTeam = 0; rOld->tLeg = 0; @@ -317,6 +319,8 @@ void oTeam::setRunnerInternal(int k, pRunner r) Runners[k] = r; if (r) { + if (specifiedCourse) + r->setCourseId(specifiedCourse); r->tInTeam = this; r->tLeg = k; if (Class && (r->Class==nullptr || Class->getLegType(k) != LTGroup)) @@ -434,11 +438,26 @@ int oTeam::getLegFinishTime(int leg) const else return 0; } -int oTeam::getRunningTime() const { - return getLegRunningTime(-1, false); +int oTeam::getRunningTime(bool computedTime) const { + return getLegRunningTime(-1, computedTime, false); } -int oTeam::getLegRunningTime(int leg, bool multidayTotal) const { +int oTeam::getLegRunningTime(int leg, bool computedTime, bool multidayTotal) const { + if (computedTime) { + auto &cr = getComputedResult(leg); + + int addon = 0; + if (multidayTotal) + addon = inputTime; + + if (cr.version == oe->dataRevision) { + if (cr.time > 0) + return cr.time + addon + getTimeAdjustment(); + else + return 0; + } + } + return getLegRunningTimeUnadjusted(leg, multidayTotal) + getTimeAdjustment(); } @@ -481,8 +500,8 @@ int oTeam::getLegRunningTimeUnadjusted(int leg, bool multidayTotal) const { switch(lt) { case LTNormal: - if (Runners[leg]->prelStatusOK()) { - int dt = leg>0 ? getLegRunningTimeUnadjusted(leg-1, false)+Runners[leg]->getRunningTime():0; + if (Runners[leg]->prelStatusOK(false, true)) { + int dt = leg>0 ? getLegRunningTimeUnadjusted(leg-1, false)+Runners[leg]->getRunningTime(false):0; return addon + max(Runners[leg]->getFinishTimeAdjusted()-(tStartTime + getLegRestingTime(leg)), dt); } else return 0; @@ -490,7 +509,7 @@ int oTeam::getLegRunningTimeUnadjusted(int leg, bool multidayTotal) const { case LTParallelOptional: case LTParallel: //Take the longest time of this runner and the previous - if (Runners[leg]->prelStatusOK()) { + if (Runners[leg]->prelStatusOK(false, false)) { int pt=leg>0 ? getLegRunningTimeUnadjusted(leg-1, false) : 0; int rest = getLegRestingTime(leg); int finishT = Runners[leg]->getFinishTimeAdjusted(); @@ -517,9 +536,9 @@ int oTeam::getLegRunningTimeUnadjusted(int leg, bool multidayTotal) const { bool bad = false; do { if (Runners[cLeg] && Runners[cLeg]->getFinishTime() > 0) { - int rt = Runners[cLeg]->getRunningTime(); + int rt = Runners[cLeg]->getRunningTime(false); if (legTime == 0 || rt < legTime) { - bad = !Runners[cLeg]->prelStatusOK(); + bad = !Runners[cLeg]->prelStatusOK(false, false); legTime = rt; } } @@ -535,15 +554,15 @@ int oTeam::getLegRunningTimeUnadjusted(int leg, bool multidayTotal) const { break; case LTSum: - if (Runners[leg]->prelStatusOK()) { + if (Runners[leg]->prelStatusOK(false, false)) { if (leg==0) - return addon + Runners[leg]->getRunningTime(); + return addon + Runners[leg]->getRunningTime(false); else { int prev = getLegRunningTimeUnadjusted(leg-1, multidayTotal); if (prev == 0) return 0; else - return Runners[leg]->getRunningTime() + prev; + return Runners[leg]->getRunningTime(false) + prev; } } else return 0; @@ -557,7 +576,14 @@ int oTeam::getLegRunningTimeUnadjusted(int leg, bool multidayTotal) const { break; case LTGroup: - return 0; + if (Class->getResultModuleTag().empty()) + return 0; + else { + int dt = Runners[leg]->getRunningTime(false); + if (leg > 0) + dt += getLegRunningTimeUnadjusted(leg - 1, multidayTotal); + return dt; + } } } else { @@ -565,7 +591,7 @@ int oTeam::getLegRunningTimeUnadjusted(int leg, bool multidayTotal) const { int dt2=0; if (leg>0) - dt2=getLegRunningTimeUnadjusted(leg-1, multidayTotal)+Runners[leg]->getRunningTime(); + dt2=getLegRunningTimeUnadjusted(leg-1, multidayTotal)+Runners[leg]->getRunningTime(false); return max(dt, dt2); } @@ -573,12 +599,12 @@ int oTeam::getLegRunningTimeUnadjusted(int leg, bool multidayTotal) const { return 0; } -wstring oTeam::getLegRunningTimeS(int leg, bool multidayTotal) const +wstring oTeam::getLegRunningTimeS(int leg, bool computedTime, bool multidayTotal) const { if (leg==-1) leg = Runners.size()-1; - int rt=getLegRunningTime(leg, multidayTotal); + int rt=getLegRunningTime(leg, computedTime, multidayTotal); const wstring &bf = formatTime(rt); if (rt>0) { if ((unsigned(leg)0 && Class->getLegType(leg)==LTIgnore) leg--; + if (computed) { + auto &cr = getComputedResult(leg); + if (cr.version == oe->dataRevision) + return cr.status; + } + for (int i=0;i<=leg;i++) { // Ignore runners to be ignored while(igetLegType(i)==LTIgnore) @@ -656,28 +688,39 @@ RunnerStatus oTeam::getLegStatus(int leg, bool multidayTotal) const return RunnerStatus(s); } -const wstring &oTeam::getLegStatusS(int leg, bool multidayTotal) const +const wstring &oTeam::getLegStatusS(int leg, bool computed, bool multidayTotal) const { - return oe->formatStatus(getLegStatus(leg, multidayTotal), true); + return oe->formatStatus(getLegStatus(leg, computed, multidayTotal), true); } int oTeam::getLegPlace(int leg, bool multidayTotal) const { + if (leg == -1) + leg = Runners.size() - 1; + if (Class) { - while(size_t(leg) < Class->legInfo.size() && Class->legInfo[leg].legMethod == LTIgnore) + while (size_t(leg) < Class->legInfo.size() && Class->legInfo[leg].legMethod == LTIgnore) leg--; } - if (!multidayTotal) - return leg>=0 && leg=0 && legcalculateTeamResults({getClassId(true)}, oEvent::ResultType::ClassResult); + } + return p.p.value; + } + else { + if (Class && p.totalP.isOld(*oe)) { + oe->calculateTeamResults({ getClassId(true) }, oEvent::ResultType::TotalResult); + } + return p.totalP.value; + } } - wstring oTeam::getLegPlaceS(int leg, bool multidayTotal) const { - int p=getLegPlace(leg, multidayTotal); + int p = getLegPlace(leg, multidayTotal); wchar_t bf[16]; - if (p>0 && p<10000){ + if (p > 0 && p < 10000) { swprintf_s(bf, L"%d", p); return bf; } @@ -777,23 +820,15 @@ bool oTeam::isRunnerUsed(int rId) const { return false; } -void oTeam::setTeamNoStart(bool dns, RunnerStatus dnsStatus) +void oTeam::setTeamMemberStatus(RunnerStatus dnsStatus) { - if (dns) { - assert(dnsStatus == StatusCANCEL || dnsStatus == StatusDNS); - setStatus(dnsStatus, true, false); - for(unsigned i=0;igetStatus()==StatusUnknown) { - Runners[i]->setStatus(dnsStatus, true, false); - } - } - } - else { - setStatus(StatusUnknown, true, false); - for(unsigned i=0;igetStatus()==StatusDNS || Runners[i]->getStatus() == StatusCANCEL)) { - Runners[i]->setStatus(StatusUnknown, true, false); - } + assert(!isResultStatus(dnsStatus) || dnsStatus == StatusOK); + setStatus(dnsStatus, true, ChangeType::Update); + for (unsigned i = 0; i < Runners.size(); i++) { + if (Runners[i] && !isResultStatus(Runners[i]->getStatus()) || + ((dnsStatus == StatusOutOfCompetition || dnsStatus == StatusNoTiming) && + Runners[i]->statusOK(false))) { + Runners[i]->setStatus(dnsStatus, true, ChangeType::Update); } } // Do not sync here @@ -839,7 +874,8 @@ void oTeam::quickApply() { tr->tLeg = 0; tr->tLegEquClass = 0; if (tr->Class == Class) - tr->Class = 0; + tr->Class = nullptr; + oe->classIdToRunnerHash.reset(); tr->updateChanged(); } } @@ -865,7 +901,7 @@ void oTeam::quickApply() { } } -bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { +void oTeam::apply(ChangeType changeType, pRunner source) { if (unsigned(status) >= 100) status = StatusUnknown; // Enforce correct status @@ -882,14 +918,14 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { tr->tLeg = 0; tr->tLegEquClass = 0; if (tr->Class == Class) - tr->Class = 0; - tr->updateChanged(); + tr->Class = nullptr; + oe->classIdToRunnerHash.reset(); + if (changeType == ChangeType::Update) + tr->updateChanged(); } } Runners.resize(Class->getNumStages()); } - const wstring &bib = getBib(); - BibMode bibMode = (BibMode)-1; tNumRestarts = 0; vector availableStartTimes; for (size_t i_c=0;i_c0 && source!=0 && Runners[i-1] == source) - return true; + if (changeType == ChangeType::Quiet && i > 0 && source != nullptr && Runners[i - 1] == source) + return; + if (!Runners[i] && Class) { unsigned lr = Class->getLegRunner(i); if (lrcreateMultiRunner(false, sync); + Runners[lr]->createMultiRunner(false, false); int dup=Class->getLegRunnerIndex(i); Runners[i] = Runners[lr]->getMultiRunner(dup); } } - if (sync && Runners[i] && Class) { + if (changeType == ChangeType::Update && Runners[i] && Class) { unsigned lr = Class->getLegRunner(i); if (lr == i && Runners[i]->tParentRunner) { pRunner parent = Runners[i]->tParentRunner; @@ -961,27 +998,13 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { } if (actualClass == Class) - tr->setStartNo(StartNo, setTmpOnly); - if (!bib.empty() && tr->isChanged()) { - if (bibMode == -1 && Class) - bibMode = Class->getBibMode(); - if (bibMode == BibSame) - tr->setBib(bib, 0, false, setTmpOnly); - else if (bibMode == BibAdd) { - wchar_t pattern[32], bf[32]; - int ibib = oClass::extractBibPattern(bib, pattern) + i; - swprintf_s(bf, pattern, ibib); - tr->setBib(bf, 0, false, setTmpOnly); - } - else if (bibMode == BibLeg) { - wstring rbib = bib + L"-" + Class->getLegNumber(i); - tr->setBib(rbib, 0, false, setTmpOnly); - } - } + tr->setStartNo(StartNo, changeType);//XXX + LegTypes legType = Class ? Class->getLegType(i) : LTIgnore; - if (tr->Class!=Class && legType != LTGroup) { - tr->Class=Class; + if (tr->Class != Class && legType != LTGroup) { + tr->Class = Class; + oe->classIdToRunnerHash.reset(); tr->updateChanged(); } @@ -993,7 +1016,7 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { if (legType == LTIgnore) { tr->tNeedNoCard=true; if (lastStatus != StatusUnknown) { - tr->setStatus(max(tr->tStatus, lastStatus), false, setTmpOnly); + tr->setStatus(max(tr->tStatus, lastStatus), false, changeType); } } else @@ -1008,14 +1031,14 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { } if (lt==LTIgnore || lt==LTExtra) { if (st != STDrawn) - tr->setStartTime(lastStartTime, false, setTmpOnly); + tr->setStartTime(lastStartTime, false, changeType); tr->tUseStartPunch = (st == STDrawn); } else { //Calculate start time. switch (st) { case STDrawn: //Do nothing if (lt==LTParallel || lt==LTParallelOptional) { - tr->setStartTime(lastStartTime, false, setTmpOnly); + tr->setStartTime(lastStartTime, false, changeType); tr->tUseStartPunch=false; } else @@ -1041,7 +1064,7 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { else lastStartTime = actualClass->getStartData(0); // Qualification/final classes } - tr->setStartTime(lastStartTime, false, setTmpOnly); + tr->setStartTime(lastStartTime, false, changeType); tr->tUseStartPunch=false; } } @@ -1107,12 +1130,12 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { } if (ft > 0) - tr->setStartTime(ft, false, setTmpOnly); + tr->setStartTime(ft, false, changeType); tr->tUseStartPunch=false; lastStartTime=ft; } else {//The else below should only be run by mistake (for an incomplete team) - tr->setStartTime(Class->getRestartTime(i), false, setTmpOnly); + tr->setStartTime(Class->getRestartTime(i), false, changeType); tr->tUseStartPunch=false; } } @@ -1126,7 +1149,7 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { if (rt>0) setStart = true; - int leaderTime = pc->getTotalLegLeaderTime(i-1, false); + int leaderTime = pc->getTotalLegLeaderTime(i-1, false, false); int timeAfter = leaderTime > 0 ? rt - leaderTime : 0; if (rt>0 && timeAfter>=0) @@ -1135,7 +1158,7 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { int restart=pc->getRestartTime(i); int rope=pc->getRopeTime(i); - RunnerStatus hst = getLegStatus(i-1, false); + RunnerStatus hst = getLegStatus(i-1, false, false); if (hst != StatusUnknown && hst != StatusOK) { setStart = true; lastStartTime = restart; @@ -1173,7 +1196,7 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { lastStartTime=0; tr->tUseStartPunch=false; - tr->setStartTime(lastStartTime, false, setTmpOnly); + tr->setStartTime(lastStartTime, false, changeType); } break; } @@ -1227,54 +1250,76 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { } } } - if (sync) - tr->synchronize(true); } } if (!Runners.empty() && Runners[0]) { - if (setTmpOnly) - setStartTime(Runners[0]->tmpStore.startTime, false, setTmpOnly); - else - setStartTime(Runners[0]->getStartTime(), false, setTmpOnly); + setStartTime(Runners[0]->getStartTime(), false, changeType); } else if (Class && Class->getStartType(0) != STDrawn) - setStartTime(Class->getStartData(0), false, setTmpOnly); + setStartTime(Class->getStartData(0), false, changeType); setFinishTime(getLegFinishTime(-1)); - setStatus(getLegStatus(-1, false), false, setTmpOnly); //XXX Maybe getLegStatus needs to work agains tmp store? - - if (sync) - synchronize(true); - return true; + setStatus(getLegStatus(-1, false, false), false, changeType); } -void oTeam::evaluate(bool sync) -{ - for(unsigned i=0;iresetTmpStore(); - } - resetTmpStore(); +void oTeam::applyBibs() { + BibMode bibMode = BibUndefined; + wstring bib = getBib(); - apply(false, 0, true); + for (size_t i = 0; i < Runners.size(); i++) { + pRunner tr = Runners[i]; + if (tr) { + pClass actualClass = tr->getClassRef(true); + if (actualClass == nullptr) + actualClass = Class; + + if (actualClass == Class) + tr->setStartNo(StartNo, ChangeType::Update); + + if (bibMode == BibMode::BibUndefined && Class) + bibMode = Class->getBibMode(); + + if (!bib.empty()) { + if (bibMode == BibSame) + tr->setBib(bib, 0, false); + else if (bibMode == BibAdd) { + wchar_t pattern[32], bf[32]; + int ibib = oClass::extractBibPattern(bib, pattern) + i; + swprintf_s(bf, pattern, ibib); + tr->setBib(bf, 0, false); + } + else if (bibMode == BibLeg) { + wstring rbib = bib + L"-" + Class->getLegNumber(i); + tr->setBib(rbib, 0, false); + } + } + else { + if (bibMode == BibSame || bibMode == BibAdd || bibMode == BibLeg) + tr->setBib(bib, 0, false); + } + } + } +} + +void oTeam::evaluate(ChangeType changeType) { + apply(ChangeType::Quiet, nullptr); vector mp; for(unsigned i=0;ievaluateCard(false, mp); + Runners[i]->evaluateCard(false, mp, 0, changeType); } - apply(false, 0, true); + apply(changeType, nullptr); + if (changeType == ChangeType::Update) { + makeQuietChangePermanent(); - for(unsigned i=0;isetTmpStore(); - if (sync) + for (unsigned i = 0; i < Runners.size(); i++) { + if (Runners[i]) { Runners[i]->synchronize(true); + } } - } - setTmpStore(); - if (sync) synchronize(true); + } } void oTeam::correctRemove(pRunner r) { @@ -1388,7 +1433,13 @@ void oTeam::fillSpeakerObject(int leg, int courseControlId, int previousControlC } } // Add start number - spk.bib = getBib(); + if (spk.names.size() == 1 && Runners[firstLeg + 1]) + spk.bib = Runners[firstLeg + 1]->getBib(); + else + spk.bib.clear(); + + if (spk.bib.empty()) + spk.bib = getBib(); if (courseControlId == 2) { unsigned nextLeg = leg + 1; @@ -1415,8 +1466,8 @@ void oTeam::fillSpeakerObject(int leg, int courseControlId, int previousControlC int timeOffset=0; RunnerStatus inheritStatus = StatusUnknown; if (firstLeg>=0) { - timeOffset = getLegRunningTime(firstLeg, totalResult); - inheritStatus = getLegStatus(firstLeg, totalResult); + timeOffset = getLegRunningTime(firstLeg, true, totalResult); + inheritStatus = getLegStatus(firstLeg, true, totalResult); } else if (totalResult) { timeOffset = getInputTime(); @@ -1454,7 +1505,7 @@ void oTeam::fillSpeakerObject(int leg, int courseControlId, int previousControlC spk.timeSinceChange = oe->getComputerTime() - (spk.runningTimeLeg.time + Runners[leg]->tStartTime); spk.owner=Runners[leg]; - spk.finishStatus=getLegStatus(specifiedLeg, totalResult); + spk.finishStatus=getLegStatus(specifiedLeg, true, totalResult); spk.missingStartTime = false; int stMax = 0; @@ -1503,7 +1554,7 @@ void oTeam::fillSpeakerObject(int leg, int courseControlId, int previousControlC if (spk.status==StatusOK) { if (courseControlId == 2) - spk.runningTime.time = getLegRunningTime(requestedLeg, totalResult); // Get official time + spk.runningTime.time = getLegRunningTime(requestedLeg, true, totalResult); // Get official time if (spk.runningTime.time == 0) spk.runningTime.time = spk.runningTimeLeg.time + timeOffset; //Preliminary time @@ -1530,12 +1581,12 @@ int oTeam::getTimeAfter(int leg) const { if (!Class || Class->tLeaderTime.size()<=unsigned(leg)) return -1; - int t=getLegRunningTime(leg, false); + int t=getLegRunningTime(leg, true, false); if (t<=0) return -1; - return t-Class->getTotalLegLeaderTime(leg, false); + return t-Class->getTotalLegLeaderTime(leg, true, false); } int oTeam::getLegStartTime(int leg) const @@ -1568,31 +1619,23 @@ wstring oTeam::getLegStartTimeCompact(int leg) const else return makeDash(L"-"); } -void oTeam::setBib(const wstring &bib, int bibnumerical, bool updateStartNo, bool setTmpOnly) { +void oTeam::setBib(const wstring &bib, int bibnumerical, bool updateStartNo) { if (updateStartNo) updateStartNo = !Class || !Class->lockedForking(); - if (setTmpOnly) { - tmpStore.bib = bib; - if (updateStartNo) - setStartNo(bibnumerical, true); - return; - } - if (getDI().setString("Bib", bib)) { if (oe) oe->bibStartNoToRunnerTeam.clear(); } - if (updateStartNo) { - setStartNo(bibnumerical, false); - } + if (updateStartNo) + setStartNo(bibnumerical, ChangeType::Update); } oDataContainer &oTeam::getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const { data = (pvoid)oData; olddata = (pvoid)oDataOld; - strData = 0; + strData = const_cast(&dynamicData); return *oe->oTeamData; } @@ -1603,13 +1646,16 @@ pRunner oTeam::getRunner(unsigned leg) const { return leg 0) + return tComputedPoints; + int pt = 0; bool simpleSum = false; if (simpleSum) { for (size_t k = 0; k < Runners.size(); k++) { if (Runners[k]) - pt += Runners[k]->tRogainingPoints; + pt += Runners[k]->getRogainingPoints(computed, false); } } else { @@ -1629,7 +1675,7 @@ int oTeam::getRogainingPoints(bool multidayTotal) const { } } } - pt = max(pt - getRogainingReduction(), 0); + pt = max(pt - getRogainingReduction(true), 0); // Manual point adjustment pt = max(0, pt + getPointAdjustment()); @@ -1638,17 +1684,24 @@ int oTeam::getRogainingPoints(bool multidayTotal) const { return pt; } -int oTeam::getRogainingOvertime() const { +int oTeam::getRogainingOvertime(bool computed) const { + pCourse sampleCourse = 0; int overTime = 0; for (size_t k = 0; k < Runners.size(); k++) { if (Runners[k]) { + if (sampleCourse == 0 && (Runners[k]->tRogainingPoints > 0 || Runners[k]->tReduction > 0)) + sampleCourse = Runners[k]->getCourse(false); overTime += Runners[k]->tRogainingOvertime; } } + if (sampleCourse && computed) { + if (tComputedTime > 0) + overTime = max(0, tComputedTime - sampleCourse->getMaximumRogainingTime()); + } return overTime; } -int oTeam::getRogainingPointsGross() const { +int oTeam::getRogainingPointsGross(bool computed) const { int gross = 0; for (size_t k = 0; k < Runners.size(); k++) { if (Runners[k]) { @@ -1659,7 +1712,7 @@ int oTeam::getRogainingPointsGross() const { } -int oTeam::getRogainingReduction() const { +int oTeam::getRogainingReduction(bool computed) const { pCourse sampleCourse = 0; int overTime = 0; for (size_t k = 0; k < Runners.size(); k++) { @@ -1669,7 +1722,14 @@ int oTeam::getRogainingReduction() const { overTime += Runners[k]->tRogainingOvertime; } } - return sampleCourse->calculateReduction(overTime); + if (sampleCourse && computed) { + if (tComputedTime > 0) + overTime = max(0, tComputedTime - sampleCourse->getMaximumRogainingTime()); + + return sampleCourse->calculateReduction(overTime); + } + else + return 0; } void oTeam::remove() @@ -1724,9 +1784,9 @@ wstring oTeam::getDisplayClub() const { void oTeam::setInputData(const oTeam &t) { inputTime = t.getTotalRunningTime(); inputStatus = t.getTotalStatus(); - inputPoints = t.getRogainingPoints(true); + inputPoints = t.getRogainingPoints(true, true); int tp = t.getTotalPlace(); - inputPlace = tp < 99000 ? tp : 0; + inputPlace = tp; oDataInterface dest = getDI(); oDataConstInterface src = t.getDCI(); @@ -1738,21 +1798,14 @@ void oTeam::setInputData(const oTeam &t) { dest.setInt("Fee", src.getInt("Fee")); dest.setInt("Paid", src.getInt("Paid")); dest.setInt("Taxable", src.getInt("Taxable")); -} -RunnerStatus oTeam::getTotalStatus() const { - if (tStatus == StatusUnknown) - return StatusUnknown; - else if (inputStatus == StatusUnknown) - return StatusDNS; - - return max(tStatus, inputStatus); + int sn = t.getEvent()->getStageNumber(); + addToInputResult(sn - 1, &t); } void oEvent::getTeams(int classId, vector &t, bool sort) { if (sort) { synchronizeList(oListId::oLTeamId); - //sortTeams(SortByName); } t.clear(); if (Classes.size() > 0) @@ -1767,6 +1820,26 @@ void oEvent::getTeams(int classId, vector &t, bool sort) { } } +void oEvent::getTeams(const set &classId, vector &t, bool synch) { + if (synch) + synchronizeList(oListId::oLTeamId); + + getTeams(classId, t); +} + +void oEvent::getTeams(const set &classId, vector &t) const { + t.clear(); + t.reserve(Teams.size()); + + for (auto it = Teams.begin(); it != Teams.end(); ++it) { + if (it->isRemoved()) + continue; + + if (classId.empty() || classId.count(it->getClassId(false))) + t.push_back(const_cast(&*it)); + } +} + wstring oTeam::getEntryDate(bool dummy) const { oDataConstInterface dci = getDCI(); int date = dci.getInt("EntryDate"); @@ -1780,9 +1853,7 @@ wstring oTeam::getEntryDate(bool dummy) const { unsigned static nRunnerMaxStored = -1; -Table *oEvent::getTeamsTB() //Table mode -{ - +const shared_ptr
    &oTeam::getTable(oEvent *oe) { vector cls; oe->getClasses(cls, true); unsigned nRunnerMax = 0; @@ -1792,16 +1863,9 @@ Table *oEvent::getTeamsTB() //Table mode bool forceUpdate = nRunnerMax != nRunnerMaxStored; - if (forceUpdate && tables.count("team")) { - tables["team"]->releaseOwnership(); - tables.erase("team"); - } + if (forceUpdate || !oe->hasTable("team")) { - if (tables.count("team") == 0) { - - oCardList::iterator it; - - Table *table=new Table(this, 20, L"Lag(flera)", "teams"); + auto table = make_shared
    (oe, 20, L"Lag(flera)", "teams"); table->addColumn("Id", 70, true, true); table->addColumn("Ändrad", 70, false); @@ -1814,32 +1878,29 @@ Table *oEvent::getTeamsTB() //Table mode table->addColumn("Mål", 70, false, true); table->addColumn("Status", 70, false); table->addColumn("Tid", 70, false, true); + table->addColumn("Poäng", 70, true, true); table->addColumn("Plac.", 70, true, true); table->addColumn("Start nr.", 70, true, false); for (unsigned k = 0; k < nRunnerMax; k++) { - table->addColumn("Sträcka X#" + itos(k+1), 200, false, false); - table->addColumn("Bricka X#" + itos(k+1), 70, true, true); + table->addColumn("Sträcka X#" + itos(k + 1), 200, false, false); + table->addColumn("Bricka X#" + itos(k + 1), 70, true, true); } nRunnerMaxStored = nRunnerMax; - oe->oTeamData->buildTableCol(table); - + oe->oTeamData->buildTableCol(table.get()); table->addColumn("Tid in", 70, false, true); table->addColumn("Status in", 70, false, true); table->addColumn("Poäng in", 70, true); table->addColumn("Placering in", 70, true); - tables["team"] = table; - table->addOwnership(); + oe->setTable("team", table); } - tables["team"]->update(); - return tables["team"]; + return oe->getTable("team"); } - void oEvent::generateTeamTableData(Table &table, oTeam *addTeam) { vector cls; @@ -1850,6 +1911,8 @@ void oEvent::generateTeamTableData(Table &table, oTeam *addTeam) nRunnerMax = max(nRunnerMax, cls[k]->getNumStages()); } + oe->calculateTeamResults(set({}), ResultType::ClassResult); + if (nRunnerMax != nRunnerMaxStored) throw meosException("Internal table error: Restart MeOS"); @@ -1882,8 +1945,10 @@ void oTeam::addTableRow(Table &table) const { table.set(row++, it, TID_START, getStartTimeS(), true); table.set(row++, it, TID_FINISH, getFinishTimeS(), true); - table.set(row++, it, TID_STATUS, getStatusS(false), true, cellSelection); - table.set(row++, it, TID_RUNNINGTIME, getRunningTimeS(), false); + table.set(row++, it, TID_STATUS, getStatusS(false, true), true, cellSelection); + table.set(row++, it, TID_RUNNINGTIME, getRunningTimeS(true), false); + int rp = getRogainingPoints(true, false); + table.set(row++, it, TID_POINTS, rp ? itow(rp) : L"", false); table.set(row++, it, TID_PLACE, getPlaceS(), false); table.set(row++, it, TID_STARTNO, itow(getStartNo()), true); @@ -1908,15 +1973,17 @@ void oTeam::addTableRow(Table &table) const { table.set(row++, it, TID_INPUTPLACE, itow(inputPlace), true); } -bool oTeam::inputData(int id, const wstring &input, - int inputId, wstring &output, bool noUpdate) -{ +pair oTeam::inputData(int id, const wstring &input, + int inputId, wstring &output, bool noUpdate) { int s,t; synchronize(false); if (id>1000) { - return oe->oTeamData->inputData(this, id, input, + auto res = oe->oTeamData->inputData(this, id, input, inputId, output, noUpdate); + applyBibs(); + evaluate(oBase::ChangeType::Update); + return res; } else if (id >= 100) { @@ -1962,14 +2029,13 @@ bool oTeam::inputData(int id, const wstring &input, setStartTimeS(input); if (getRunner(0)) getRunner(0)->setStartTimeS(input); - t=getStartTime(); - apply(false, 0, false); - s=getStartTime(); - if (s!=t) + t = getStartTime(); + apply(ChangeType::Update, nullptr); + s = getStartTime(); + if (s != t) throw std::exception("Starttiden är definerad genom klassen eller löparens startstämpling."); synchronize(true); output = getStartTimeS(); - return true; break; case TID_CLUB: @@ -1994,26 +2060,22 @@ bool oTeam::inputData(int id, const wstring &input, case TID_STATUS: { RunnerStatus sIn = RunnerStatus(inputId); - if (sIn != StatusDNS && sIn != StatusCANCEL) { - if ((getStatus() == StatusDNS || getStatus() == StatusCANCEL) && sIn == StatusUnknown) - setTeamNoStart(false, StatusUnknown); - else - setStatus(sIn, true, false); - } - else if (getStatus() != sIn) - setTeamNoStart(true, sIn); - - apply(true, 0, false); + if (!isResultStatus(sIn)) + setTeamMemberStatus(sIn); + else + setStatus(sIn, true, ChangeType::Update); + + apply(ChangeType::Update, nullptr); RunnerStatus sOut = getStatus(); if (sOut != sIn) throw meosException("Status matchar inte deltagarnas status."); - output = getStatusS(false); + output = getStatusS(false, true); } break; case TID_STARTNO: - setStartNo(_wtoi(input.c_str()), false); - synchronize(true); + setStartNo(_wtoi(input.c_str()), ChangeType::Update); + evaluate(oBase::ChangeType::Update); output = itow(getStartNo()); break; @@ -2042,13 +2104,13 @@ bool oTeam::inputData(int id, const wstring &input, break; } - return false; + return make_pair(0, false); } void oTeam::fillInput(int id, vector< pair > &out, size_t &selected) { if (id>1000) { - oe->oRunnerData->fillInput(oData, id, 0, out, selected); + oe->oRunnerData->fillInput(this, id, 0, out, selected); return; } else if (id==TID_CLASSNAME) { @@ -2198,8 +2260,8 @@ int oTeam::getRogainingPatrolPoints(bool multidayTotal) const { if (r) { pCourse pc = r->getCourse(false); if (r->getCard() && pc) { - reduction = max(reduction, r->getRogainingReduction()); - overtime = max(overtime, r->getRogainingOvertime()); + reduction = max(reduction, r->getRogainingReduction(false)); + overtime = max(overtime, r->getRogainingOvertime(false)); int rid = r->getId(); r->getCard()->getPunches(punches); for (auto p : punches) { @@ -2293,3 +2355,42 @@ void oTeam::propagateClub() { } } } + +const oTeam::ComputedLegResult &oTeam::getComputedResult(int leg) const { + if (size_t(leg) < tComputedResults.size()) + return tComputedResults[leg]; + + if (tComputedResults.empty()) + tComputedResults.resize(1); + return tComputedResults[0]; +} + +void oTeam::setComputedResult(int leg, ComputedLegResult &comp) const { + if (tComputedResults.size() < Runners.size()) + tComputedResults.resize(Runners.size()); + + if (size_t(leg) >= tComputedResults.size()) + return; + + tComputedResults[leg] = comp; +} + +oTeam::TeamPlace &oTeam::getTeamPlace(int leg) const { + if (tPlace.size() != Runners.size()) + tPlace.resize(Runners.size()); + + if (size_t(leg) < tPlace.size()) + return tPlace[leg]; + + if (tComputedResults.empty()) + tPlace.resize(1); + return tPlace[0]; +} + +bool oTeam::isResultUpdated(bool totalResult) const { + auto &p = getTeamPlace(Runners.size() - 1); + if (totalResult) + return !p.p.isOld(*oe); + else + return !p.totalP.isOld(*oe); +} diff --git a/code/oTeam.h b/code/oTeam.h index 21165f8..3c00fe6 100644 --- a/code/oTeam.h +++ b/code/oTeam.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -70,11 +70,23 @@ protected: void changeId(int newId); struct TeamPlace { - int p; // Day result - int totalP; // Total result + DynamicValue p; // Day result + DynamicValue totalP; // Total result }; - TeamPlace _places[maxRunnersTeam]; + mutable vector tPlace; + + TeamPlace &oTeam::getTeamPlace(int leg) const; + + struct ComputedLegResult { + int version = -1; + int time = 0; + RunnerStatus status = StatusUnknown; + + ComputedLegResult() {}; + }; + + mutable vector tComputedResults; mutable int _sortTime; mutable int _sortStatus; @@ -97,6 +109,9 @@ protected: }; mutable pair tTeamPatrolRogainingAndVersion; + const ComputedLegResult &getComputedResult(int leg) const; + void setComputedResult(int leg, ComputedLegResult &comp) const; + string getRunners() const; bool matchTeam(int number, const wchar_t *s_lc) const; int tNumRestarts; //Number of restarts for team @@ -107,15 +122,24 @@ protected: void addTableRow(Table &table) const; - bool inputData(int id, const wstring &input, - int inputId, wstring &output, bool noUpdate); + pair inputData(int id, const wstring &input, + int inputId, wstring &output, bool noUpdate); - void fillInput(int id, vector< pair > &out, size_t &selected); + void fillInput(int id, vector< pair > &out, size_t &selected) override; /** Get internal data buffers for DI */ oDataContainer &getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const; + void fillInSortData(SortOrder so, + int leg, + bool linearLeg, + map &classId2Linear, + bool &hasRunner) const; + public: + + static const shared_ptr
    &getTable(oEvent *oe); + /** Check the the main leg is set if any parallel is set. Returns true if corrections where made.*/ bool checkValdParSetup(); @@ -147,34 +171,36 @@ public: cTeam getTeam() const {return this;} pTeam getTeam() {return this;} - int getRunningTime() const; + int getRunningTime(bool computedTime) const override; /// Input data for multiday event void setInputData(const oTeam &t); - /// Get total status for multiday event - RunnerStatus getTotalStatus() const; - void remove(); bool canRemove() const; void prepareRemove(); bool skip() const {return isRemoved();} - void setTeamNoStart(bool dns, RunnerStatus dnsStatus); // Set DNS or CANCEL + + void setTeamMemberStatus(RunnerStatus memberStatus); // Set DNS or CANCEL, NotCompeting etc to team and its members // If apply is triggered by a runner, don't go further than that runner. - bool apply(bool sync, pRunner source, bool setTmpOnly); + void apply(ChangeType ct, pRunner source) override; + + // Set bibs on team members + void applyBibs(); void quickApply(); - void evaluate(bool sync); + // Evaluate card and times. Synchronize to server if changeType is update. + void evaluate(ChangeType changeType); - bool adjustMultiRunners(bool sync); + void adjustMultiRunners(); - int getRogainingPoints(bool multidayTotal) const; - int getRogainingReduction() const; - int getRogainingOvertime() const; - int getRogainingPointsGross() const; + int getRogainingPoints(bool computed, bool multidayTotal) const override; + int getRogainingReduction(bool computed) const override; + int getRogainingOvertime(bool computed) const override; + int getRogainingPointsGross(bool computed) const override; int getRogainingPatrolPoints(bool multidayTotal) const; int getRogainingPatrolReduction() const; @@ -208,7 +234,7 @@ public: wstring getDisplayName() const; wstring getDisplayClub() const; - void setBib(const wstring &bib, int numericalBib, bool updateStartNo, bool setTmpOnly); + void setBib(const wstring &bib, int numericalBib, bool updateStartNo) override; int getLegStartTime(int leg) const; wstring getLegStartTimeS(int leg) const; @@ -220,19 +246,19 @@ public: int getTimeAfter(int leg) const; //Get total running time after leg - wstring getLegRunningTimeS(int leg, bool multidayTotal) const; + wstring getLegRunningTimeS(int leg, bool computed, bool multidayTotal) const; - int getLegRunningTime(int leg, bool multidayTotal) const; - int getLegPrelRunningTime(int leg) const; - wstring getLegPrelRunningTimeS(int leg) const; - - RunnerStatus getLegStatus(int leg, bool multidayTotal) const; - const wstring &getLegStatusS(int leg, bool multidayTotal) const; + int getLegRunningTime(int leg, bool computed, bool multidayTotal) const; + + RunnerStatus getLegStatus(int leg, bool computed, bool multidayTotal) const; + const wstring &getLegStatusS(int leg, bool computed, bool multidayTotal) const; wstring getLegPlaceS(int leg, bool multidayTotal) const; wstring getLegPrintPlaceS(int leg, bool multidayTotal, bool withDot) const; int getLegPlace(int leg, bool multidayTotal) const; + bool isResultUpdated(bool totalResult) const override; + static bool compareSNO(const oTeam &a, const oTeam &b); static bool compareName(const oTeam &a, const oTeam &b) {return a.sNameStartNo = ++nextFreeStartNo; // Need not be unique pt->getEntryDate(false);// Store entry time - pt->apply(false, 0, false); + pt->apply(ChangeType::Quiet, nullptr); + pt->makeQuietChangePermanent(); pt->updateChanged(); return pt; } pTeam oEvent::addTeam(const oTeam &t, bool autoAssignStartNo) { - if (t.Id==0) + if (t.Id== 0) return 0; if (getTeam(t.Id)) return 0; @@ -692,8 +693,8 @@ void oEvent::adjustTeamMultiRunners(pClass cls) } disableRecalculate = true; try { - for (oTeamList::iterator it=Teams.begin(); it != Teams.end(); ++it) { - it->adjustMultiRunners(true); + for (auto &t : Teams) { + t.adjustMultiRunners(); } } catch(...) { @@ -703,10 +704,9 @@ void oEvent::adjustTeamMultiRunners(pClass cls) disableRecalculate = false; } -bool oTeam::adjustMultiRunners(bool sync) -{ +void oTeam::adjustMultiRunners() { if (!Class) - return false; + return; for (size_t k = Class->getNumStages(); k < Runners.size(); k++) { setRunnerInternal(k, 0); @@ -723,14 +723,14 @@ bool oTeam::adjustMultiRunners(bool sync) unsigned lr = Class->getLegRunner(i); if (lr < i && Runners[lr]) { - Runners[lr]->createMultiRunner(true, sync); + Runners[lr]->createMultiRunner(true, true); int dup = Class->getLegRunnerIndex(i); Runners[i] = Runners[lr]->getMultiRunner(dup); } } } - return apply(sync, 0, false); + evaluate(ChangeType::Update); } void oEvent::makeUniqueTeamNames() { @@ -843,3 +843,176 @@ void oTeam::convertClassWithReferenceToPatrol(oEvent &oe, const std::set &c } } } + +void oTeam::fillInSortData(SortOrder so, int leg, bool linearLeg, map &classId2Linear, bool &hasRunner) const { + if (so == ClassStartTime || so == ClassStartTimeClub) { + if (leg == -1) + leg = 0; + if (unsigned(leg) < Runners.size()) + hasRunner = true; + _cachedStatus = StatusUnknown; + _sortStatus = 0; + _sortTime = getLegStartTime(leg); + if (_sortTime <= 0) + _sortStatus = 1; + return; + } + else if (so == ClassPoints) { + bool totalResult = so == ClassTotalResult; + _sortTime = getRunningTime(true) - 7 * 24 * 3600 * getRogainingPoints(true, totalResult); + _cachedStatus = getLegStatus(-1, true, totalResult); + } + else if (so == ClassKnockoutTotalResult) { + hasRunner = true; + _cachedStatus = StatusUnknown; + _sortStatus = 0; + _sortTime = 0; + + // Count number of races with results + int numResult = 0; + int lastClassHeat = 0; + for (auto &r : Runners) { + if (r && (r->prelStatusOK(true, true) || + (r->tStatus != StatusUnknown && r->tStatus != StatusDNS && r->tStatus != StatusCANCEL))) { + + if (r->Class && r->tLeg > 0 && r->Class->isQualificationFinalBaseClass() && r->getClassRef(true) == r->Class) + continue; // Skip if not qualified. + + numResult++; + lastClassHeat = r->getDCI().getInt("Heat"); + _cachedStatus = r->tStatus; + _sortTime = r->getRunningTime(false); + } + } + if (lastClassHeat > 50 || lastClassHeat < 0) + lastClassHeat = 0; + + unsigned rawStatus = _cachedStatus; + _sortStatus = RunnerStatusOrderMap[rawStatus < 100u ? rawStatus : 0] - (numResult * 100 + lastClassHeat) * 1000; + + return; + } + else { + bool totalResult = so == ClassTotalResult; + bool legResult = so == ClassTeamLegResult; + + int lg = leg; + int clsId = Class->getId(); + + if (leg >= 0 && !linearLeg) { + auto res = classId2Linear.find(Class->getId()); + if (res == classId2Linear.end()) { + unsigned linLegBase = Class->getLegNumberLinear(leg, 0); + while (linLegBase + 1 < Class->getNumStages()) { + if (Class->isParallel(linLegBase + 1) || Class->isOptional(linLegBase + 1)) + linLegBase++; + else + break; + } + lg = linLegBase; + classId2Linear[clsId] = lg; + } + else { + lg = res->second; + } + } + + const int lastIndex = Class->getLastStageIndex(); + lg = min(lg, lastIndex); + + if (lg >= leg) + hasRunner = true; + if (legResult) { + pRunner r = getRunner(lg); + if (r) { + if (so == ClassDefaultResult) { + _sortTime = r->getRunningTime(false); + _cachedStatus = r->getStatus(); + } + else { + _sortTime = r->getRunningTime(true); + _cachedStatus = r->getStatusComputed(); + } + } + else { + _sortTime = 0; + _cachedStatus = StatusUnknown; + } + } + else { + if (so == ClassDefaultResult) { + _sortTime = getLegRunningTime(lg, false, totalResult) + getNumShortening(lg) * 3600 * 24 * 10; + _cachedStatus = getLegStatus(lg, false, totalResult); + } + else { + _sortTime = getLegRunningTime(lg, true, totalResult) + getNumShortening(lg) * 3600 * 24 * 10; + _cachedStatus = getLegStatus(lg, true, totalResult); + } + + // Ensure number of restarts has effect on final result + if (lg == lastIndex) + _sortTime += tNumRestarts * 24 * 3600; + } + } + unsigned rawStatus = _cachedStatus; + _sortStatus = RunnerStatusOrderMap[rawStatus < 100u ? rawStatus : 0]; +} + +bool oEvent::sortTeams(SortOrder so, int leg, bool linearLeg) { + reinitializeClasses(); + oTeamList::iterator it; + map classId2Linear; + bool hasRunner = (leg == -1); + for (it = Teams.begin(); it != Teams.end(); ++it) { + if (!it->isRemoved() && it->Class) + it->fillInSortData(so, leg, linearLeg, classId2Linear, hasRunner); + } + + if (!hasRunner) + return false; + + if (so == ClassStartTimeClub) + Teams.sort(oTeam::compareResultNoSno); + else + Teams.sort(oTeam::compareResult); + + return true; +} + +bool oEvent::sortTeams(SortOrder so, int leg, bool linearLeg, vector &teams) const { + reinitializeClasses(); + map classId2Linear; + bool hasRunner = (leg == -1); + for (auto it : teams) { + it->fillInSortData(so, leg, linearLeg, classId2Linear, hasRunner); + } + + if (!hasRunner) + return false; + + if (so != ClassStartTimeClub) + sort(teams.begin(), teams.end(), [](const oTeam * &a, const oTeam * &b)->bool {return oTeam::compareResult(*a, *b); }); + else + sort(teams.begin(), teams.end(), [](const oTeam * &a, const oTeam * &b)->bool {return oTeam::compareResultNoSno(*a, *b); }); + + return true; +} + +bool oEvent::sortTeams(SortOrder so, int leg, bool linearLeg, vector &teams) const { + reinitializeClasses(); + map classId2Linear; + bool hasRunner = (leg == -1); + for (auto it : teams) { + it->fillInSortData(so, leg, linearLeg, classId2Linear, hasRunner); + } + + if (!hasRunner) + return false; + + if (so != ClassStartTimeClub) + sort(teams.begin(), teams.end(), [](oTeam * &a, oTeam * &b)->bool {return oTeam::compareResult(*a, *b); }); + else + sort(teams.begin(), teams.end(), [](oTeam * &a, oTeam * &b)->bool {return oTeam::compareResultNoSno(*a, *b); }); + + return true; +} diff --git a/code/onlineinput.cpp b/code/onlineinput.cpp index c629a06..879665f 100644 --- a/code/onlineinput.cpp +++ b/code/onlineinput.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/onlineinput.h b/code/onlineinput.h index b6ba6bf..1f557b2 100644 --- a/code/onlineinput.h +++ b/code/onlineinput.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/onlineresults.cpp b/code/onlineresults.cpp index 3c85141..7d73030 100644 --- a/code/onlineresults.cpp +++ b/code/onlineresults.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -56,10 +56,10 @@ static int OnlineCB(gdioutput *gdi, int type, void *data) { case GUI_LISTBOX:{ ListBoxInfo lbi = *static_cast(data); if (lbi.id == "Format") { - if (gdi->hasField("IncludeTotal")) { + if (gdi->hasWidget("IncludeTotal")) { gdi->setInputStatus("IncludeTotal", lbi.data == 1); } - if (gdi->hasField("IncludeCourse")) { + if (gdi->hasWidget("IncludeCourse")) { gdi->setInputStatus("IncludeCourse", lbi.data == 1); } } @@ -264,8 +264,8 @@ void OnlineResults::save(oEvent &oe, gdioutput &gdi) { prefix = gdi.getText("Prefix"); exportScript = gdi.getText("ExportScript"); zipFile = gdi.isChecked("Zip"); - includeTotal = gdi.hasField("IncludeTotal") && gdi.isChecked("IncludeTotal"); - includeCourse = gdi.hasField("IncludeCourse") && gdi.isChecked("IncludeCourse"); + includeTotal = gdi.hasWidget("IncludeTotal") && gdi.isChecked("IncludeTotal"); + includeCourse = gdi.hasWidget("IncludeCourse") && gdi.isChecked("IncludeCourse"); ListBoxInfo lbi; gdi.getSelectedItem("Format", lbi); @@ -304,7 +304,7 @@ void OnlineResults::save(oEvent &oe, gdioutput &gdi) { ctrl[k]->getCourseControls(ids); for (size_t i = 0; i < ids.size(); i++) { string id = "C"+itos(ids[i]); - if (gdi.hasField(id)) { + if (gdi.hasWidget(id)) { bool st = gdi.isChecked(id); if (st != ctrl[k]->isValidRadio()) { ctrl[k]->setRadio(st); diff --git a/code/onlineresults.h b/code/onlineresults.h index 176bc1b..36f4bfc 100644 --- a/code/onlineresults.h +++ b/code/onlineresults.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/ospeaker.h b/code/ospeaker.h index d29babd..5336d64 100644 --- a/code/ospeaker.h +++ b/code/ospeaker.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/parser.cpp b/code/parser.cpp index bf5457d..236435c 100644 --- a/code/parser.cpp +++ b/code/parser.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/parser.h b/code/parser.h index 9fe4c84..5ef26fc 100644 --- a/code/parser.h +++ b/code/parser.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/pdfwriter.cpp b/code/pdfwriter.cpp index 0c656ab..d0988b0 100644 --- a/code/pdfwriter.cpp +++ b/code/pdfwriter.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/pdfwriter.h b/code/pdfwriter.h index b6602c4..4d067ba 100644 --- a/code/pdfwriter.h +++ b/code/pdfwriter.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/prefseditor.cpp b/code/prefseditor.cpp index 2efe346..ee5a446 100644 --- a/code/prefseditor.cpp +++ b/code/prefseditor.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -132,7 +132,7 @@ void PrefsEditor::handle(gdioutput &gdi, BaseInfo &data, GuiEventType type) { else if (bi.id.substr(0, 5) == "Save_") { string pref = bi.id.substr(5); wstring value; - if (gdi.hasField("ValueBoolean")) { + if (gdi.hasWidget("ValueBoolean")) { ListBoxInfo lbi; gdi.getSelectedItem("ValueBoolean", lbi); value = itow(lbi.data); diff --git a/code/prefseditor.h b/code/prefseditor.h index 58611d1..14de302 100644 --- a/code/prefseditor.h +++ b/code/prefseditor.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/printer.cpp b/code/printer.cpp index 50ca413..0bb6b67 100644 --- a/code/printer.cpp +++ b/code/printer.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/progress.cpp b/code/progress.cpp index be73ba9..db8dfd1 100644 --- a/code/progress.cpp +++ b/code/progress.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/progress.h b/code/progress.h index ac5d1dc..882443d 100644 --- a/code/progress.h +++ b/code/progress.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/qualification_final.cpp b/code/qualification_final.cpp index a2bc5cb..f5a9bc2 100644 --- a/code/qualification_final.cpp +++ b/code/qualification_final.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/qualification_final.h b/code/qualification_final.h index 63cdc7e..81d2f87 100644 --- a/code/qualification_final.h +++ b/code/qualification_final.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software -Copyright (C) 2009-2019 Melin Software HB +Copyright (C) 2009-2020 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 diff --git a/code/random.cpp b/code/random.cpp index 1e4637a..bd71143 100644 --- a/code/random.cpp +++ b/code/random.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/random.h b/code/random.h index e968492..96f22aa 100644 --- a/code/random.h +++ b/code/random.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/recorder.cpp b/code/recorder.cpp index b233034..c20ed90 100644 --- a/code/recorder.cpp +++ b/code/recorder.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/recorder.h b/code/recorder.h index 3c61590..4191723 100644 --- a/code/recorder.h +++ b/code/recorder.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/restserver.cpp b/code/restserver.cpp index 03c50d0..76e1940 100644 --- a/code/restserver.cpp +++ b/code/restserver.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software -Copyright (C) 2009-2019 Melin Software HB +Copyright (C) 2009-2020 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 @@ -624,6 +624,7 @@ void RestServer::getData(oEvent &oe, const string &what, const multimap ctrl; + oe.synchronizeList(oListId::oLControlId); oe.getControls(ctrl, true); vector< pair > prop(1); prop[0].first = "id"; @@ -649,6 +650,8 @@ void RestServer::getData(oEvent &oe, const string &what, const multimap tt; @@ -1095,11 +1098,11 @@ void RestServer::lookup(oEvent &oe, const string &what, const multimapgetClubId())) }, r->getClub()); xml.write("Class", { make_pair("id", itow(r->getClassId(true))) }, r->getClass(true)); xml.write("Card", r->getCardNo()); - xml.write("Status", {make_pair("code", itow(r->getStatus()))}, r->getStatusS(true)); + xml.write("Status", {make_pair("code", itow(r->getStatusComputed()))}, r->getStatusS(true, true)); xml.write("Start", r->getStartTimeS()); if (r->getFinishTime() > 0) { xml.write("Finish", r->getFinishTimeS()); - xml.write("RunningTime", r->getRunningTimeS()); + xml.write("RunningTime", r->getRunningTimeS(true)); xml.write("Place", r->getPlaceS()); xml.write("TimeAfter", formatTime(r->getTimeAfter())); } @@ -1274,6 +1277,7 @@ void RestServer::newEntry(oEvent &oe, const multimap ¶m, str permissionDenied = true; if (!permissionDenied) { + oe.synchronizeList({ oListId::oLClassId, oListId::oLRunnerId }); wstring name, club; long long extId = 0; int clubId = 0; @@ -1363,12 +1367,21 @@ void RestServer::newEntry(oEvent &oe, const multimap ¶m, str } if (r) { + int cf = 0; + if (cardNo > 0 && oe.isHiredCard(cardNo)) { + cf = oe.getBaseCardFee(); + r->getDI().setInt("CardFee", cf); + } r->setFlag(oRunner::FlagAddedViaAPI, true); r->addClassDefaultFee(true); r->synchronize(); r->markClassChanged(-1); xml.write("Status", "OK"); - xml.write("Fee", r->getDCI().getInt("Fee")); + vector < pair > rentCard; + if (cf != 0) { + rentCard.emplace_back("hiredCard", L"true"); + } + xml.write("Fee", rentCard, itow(r->getDCI().getInt("Fee") + max(cf, 0))); xml.write("Info", r->getClass(true) + L", " + r->getCompleteIdentification(false)); } } diff --git a/code/restserver.h b/code/restserver.h index f65efcd..cc8d8a6 100644 --- a/code/restserver.h +++ b/code/restserver.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software -Copyright (C) 2009-2019 Melin Software HB +Copyright (C) 2009-2020 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 diff --git a/code/socket.cpp b/code/socket.cpp index 35ad9f1..ca04a4a 100644 --- a/code/socket.cpp +++ b/code/socket.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/socket.h b/code/socket.h index 0408115..fa643b8 100644 --- a/code/socket.h +++ b/code/socket.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/speakermonitor.cpp b/code/speakermonitor.cpp index e09e16b..c5de455 100644 --- a/code/speakermonitor.cpp +++ b/code/speakermonitor.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -227,7 +227,7 @@ void SpeakerMonitor::renderResult(gdioutput &gdi, GDICOLOR color = colorLightRed; if (res.control == oPunch::PunchFinish) { - if (r && r->statusOK()) + if (r && r->statusOK(true)) color = colorLightGreen; else color = colorLightRed; @@ -291,7 +291,7 @@ void SpeakerMonitor::calculateResults() { // TODO Result modules for (size_t k = 0; k < results.size(); k++) { - results[k].runTime = results[k].r->getTotalRunningTime(results[k].time, totalResults); + results[k].runTime = results[k].r->getTotalRunningTime(results[k].time, true, totalResults); if (results[k].status == StatusOK && totalResults) results[k].status = results[k].r->getTotalStatus(); @@ -317,7 +317,7 @@ void SpeakerMonitor::calculateResults() { for (size_t k = 0; k < results.size(); k++) { if (results[k].partialCount > 0) continue; // Skip in result calculation - int totTime = results[k].r->getTotalRunningTime(results[k].time, totalResults); + int totTime = results[k].r->getTotalRunningTime(results[k].time, true, totalResults); assert(totTime == results[k].runTime); int leg = results[k].leg(); @@ -440,7 +440,7 @@ void SpeakerMonitor::getMessage(const oEvent::ResultEvent &res, while (leg > 0) { pRunner pr = res.r->getTeam()->getRunner(--leg); // TODO: Pursuit - if (pr && pr->prelStatusOK() && pr->getFinishTime() == res.r->getStartTime()) { + if (pr && pr->prelStatusOK(false, false) && pr->getFinishTime() == res.r->getStartTime()) { vector &rResultsP = runnerToTimeKey[pr->getId()]; if (!rResultsP.empty()) { preRes = &rResultsP.back(); diff --git a/code/speakermonitor.h b/code/speakermonitor.h index 023848b..8c1eb38 100644 --- a/code/speakermonitor.h +++ b/code/speakermonitor.h @@ -1,7 +1,7 @@ #pragma once /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/subcommand.h b/code/subcommand.h index c662d59..4588d61 100644 --- a/code/subcommand.h +++ b/code/subcommand.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software -Copyright (C) 2009-2019 Melin Software HB +Copyright (C) 2009-2020 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 diff --git a/code/swedish.lng b/code/swedish.lng index 6542ed7..926797a 100644 --- a/code/swedish.lng +++ b/code/swedish.lng @@ -1866,7 +1866,7 @@ Byt till rätt klass (behÃ¥ll eventuell starttid) = Byt till rätt klass (behÃ¥l Byt till vakansplats i rätt klass (om möjligt) = Byt till vakansplats i rätt klass (om möjligt) TillÃ¥t ny klass, behÃ¥ll resultat frÃ¥n annan klass = TillÃ¥t ny klass, behÃ¥ll resultat frÃ¥n annan klass TillÃ¥t ny klass, inget totalresultat = TillÃ¥t ny klass, inget totalresultat -tooltip_explain_status = - = Okänd status\nOK = Godkänt resultat\nEj start = Startade inte\nÃ…terbud = Kommer ej att starta, visas i startlistan.\nFelst. = Felstämplat\nUtgÃ¥tt = MÃ¥ltid saknas\nDisk. = Diskvalificerad\nDeltar ej = Springer inte tävlingen +tooltip_explain_status = - = Okänd status\nOK = Godkänt resultat\nEj start = Startade inte\nÃ…terbud = Startar inte men visas i startlistan.\nFelst. = Felstämplat\nUtgÃ¥tt = MÃ¥ltid saknas\nDisk. = Diskvalificerad\nUtom tävlan = PÃ¥verkar inte resultatlistan\nDeltar ej = Springer inte tävlingen Placering = Placering Resultat frÃ¥n tidigare etapper = Resultat frÃ¥n tidigare etapper Input Results = IngÃ¥ngsresultat @@ -2443,4 +2443,43 @@ Runner check time = Checktid ClassNumEntries = Antal anmälda i klassen Flera lopp i valfri ordning = Flera lopp i valfri ordning Knockout sammanställning = Knockout sammanställning +Utom tävlan = Utom tävlan +Status code for running out-of-competition = Statuskod för lopp utom tävlan X anmälda = X anmälda +LÃ¥s funktion = LÃ¥s funktion +LÃ¥s upp = LÃ¥s upp +MÃ¥lstämpling tillÃ¥ts inte (X) = MÃ¥lstämpling tillÃ¥ts inte (X) +Radio = Radio +Radio tillÃ¥ts inte (X) = Radio tillÃ¥ts inte (X) +Startstämpling tillÃ¥ts inte (X) = Startstämpling tillÃ¥ts inte (X) +TillÃ¥t = TillÃ¥t +warn:printmodeonly = Observera att du endast genererar visning/utskrift av brickans stämplingar.\n\nFör att spara resultatet i tävlingen, välj funktion avläsning/radiotider. +There is no result module with X as identifier = Det finns ingen resultatmodul med identifierare X +Senast sedd: X vid Y = Senast sedd: X vid Y +Referens = Referens +Poäng E[stageno] = Poäng E +Status E[stageno] = Status E +Tid E[stageno] = Tid E +Plac. E[stageno] = Plac. E +FrÃ¥n första = FrÃ¥n första +Kartor = Kartor +Poängreduktion = Poängreduktion +Stigning = Stigning +Till sista = Till sista +Heat = Heat +Kvalschema = Kvalschema +LÃ¥st gaffling = LÃ¥st gaffling +Result module = Resultatmodul +ask:usecourseinclass = Banan används inte av nÃ¥gon annan deltagare i klassen.\n\nVill du använda den i alla fall? +Inkludera bana = Inkludera bana +Rogaining points before automatic reduction = Poäng före tidsavdrag +Runner/team earlier stage places = Deltagare/lag: plarcering pÃ¥ etapper +Runner/team earlier stage points = Deltagare/lag: poäng pÃ¥ etapper +Runner/team earlier stage running times = Deltagare/lag: tid pÃ¥ etapper +Runner/team earlier stage statuses = Deltagare/lag: status pÃ¥ etapper +Status code for cancelled entry = Statuskod för Ã¥terbud +Ã…ldersfiltrering = Ã…ldersfiltrering +Age (on last day of current year) = Ã…lder vid Ã¥rets slut +Age above or equal implies senior/pensioner = Ã…ldersgräns äldre/pensionär +Age below or equal implies youth = Ã…ldersgräns ungdom +Status code for not no timing = Statuskod för utan tidtagning diff --git a/code/testmeos.cpp b/code/testmeos.cpp index 321d5a1..6a90b3d 100644 --- a/code/testmeos.cpp +++ b/code/testmeos.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -34,6 +34,7 @@ #include "SportIdent.h" #include "metalist.h" #include "Table.h" +#include "generalresult.h" void registerTests(TestMeOS &tm); diff --git a/code/testmeos.h b/code/testmeos.h index 9b9b0f0..f916270 100644 --- a/code/testmeos.h +++ b/code/testmeos.h @@ -2,7 +2,7 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/toolbar.cpp b/code/toolbar.cpp index 3674737..ecb3bce 100644 --- a/code/toolbar.cpp +++ b/code/toolbar.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -63,7 +63,7 @@ LRESULT CALLBACK ToolProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) const DWORD buttonStyles = BTNS_AUTOSIZE; const int bitmapSize = 24; -Toolbar::Toolbar(gdioutput &gdi_par) : gdi(gdi_par), data(0) +Toolbar::Toolbar(gdioutput &gdi_par) : gdi(gdi_par) { hwndFloater = 0; hwndToolbar = 0; @@ -127,7 +127,7 @@ void Toolbar::processCommand(int id, int code) { size_t ix = id - BASE_ID; if (ix < btn_id.size()) { - gdi.processToolbarMessage(btn_id[ix], data); + gdi.processToolbarMessage(btn_id[ix], table.get()); } } diff --git a/code/toolbar.h b/code/toolbar.h index 1076b83..b999e4b 100644 --- a/code/toolbar.h +++ b/code/toolbar.h @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 @@ -23,6 +23,7 @@ #include "stdafx.h" #include "gdioutput.h" +class Table; #pragma warning( disable : 4512 ) class Toolbar { @@ -37,7 +38,7 @@ class Toolbar { vector btn; vector btn_id; list tooltips; - void *data; + shared_ptr
    table; string toolbar_id; @@ -52,7 +53,7 @@ public: HWND getFloater() const; bool isVisible() const; - void setData(void *d) {data = d;} + void setData(const shared_ptr
    &d) {table = d;} void reset(); void addButton(const string &id, int imgList, int icon, const string &tooltip); diff --git a/code/xmlparser.cpp b/code/xmlparser.cpp index 294ac0e..cfb567c 100644 --- a/code/xmlparser.cpp +++ b/code/xmlparser.cpp @@ -1,6 +1,6 @@ /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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 diff --git a/code/xmlparser.h b/code/xmlparser.h index b4b24c7..0cc1a51 100644 --- a/code/xmlparser.h +++ b/code/xmlparser.h @@ -10,7 +10,7 @@ #endif // _MSC_VER > 1000 /************************************************************************ MeOS - Orienteering Software - Copyright (C) 2009-2019 Melin Software HB + Copyright (C) 2009-2020 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