Compare commits

...

1 Commits
onb ... develop

Author SHA1 Message Date
Erik Melin
a1a05bd56d Development version 2023-09-07 22:30:09 +02:00
41 changed files with 1512 additions and 572 deletions

View File

@ -1585,10 +1585,14 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
wstring bib; wstring bib;
bool doBibs = false; bool doBibs = false;
bool bibToVacant = true;
if (gdi.hasWidget("Bib")) { if (gdi.hasWidget("Bib")) {
bib = gdi.getText("Bib"); bib = gdi.getText("Bib");
doBibs = gdi.isChecked("HandleBibs"); doBibs = gdi.isChecked("HandleBibs");
if (gdi.hasWidget("VacantBib")) {
bibToVacant = gdi.isChecked("VacantBib");
oe->getDI().setInt("NoVacantBib", bibToVacant ? 0 : 1);
}
} }
wstring time = gdi.getText("FirstStart"); wstring time = gdi.getText("FirstStart");
@ -1680,7 +1684,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
throw std::exception("Not implemented"); throw std::exception("Not implemented");
if (doBibs) if (doBibs)
oe->addBib(cid, leg, bib); oe->addBib(cid, leg, bib, bibToVacant);
// Clear input // Clear input
gdi.restore("", false); gdi.restore("", false);
@ -1703,6 +1707,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
} }
else if (bi.id=="HandleBibs") { else if (bi.id=="HandleBibs") {
gdi.setInputStatus("Bib", gdi.isChecked("HandleBibs")); gdi.setInputStatus("Bib", gdi.isChecked("HandleBibs"));
gdi.setInputStatus("VacantBib", gdi.isChecked("HandleBibs"), true);
} }
else if (bi.id == "DoDeleteStart") { else if (bi.id == "DoDeleteStart") {
pClass pc=oe->getClass(ClassId); pClass pc=oe->getClass(ClassId);
@ -1878,10 +1883,19 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
gdi.fillRight(); gdi.fillRight();
gdi.popX(); gdi.popX();
gdi.addString("", 0, "Antal reserverade nummerlappsnummer mellan klasser:"); gdi.addString("", 0, "Antal reserverade nummerlappsnummer mellan klasser:");
gdi.dropLine(-0.1); gdi.dropLine(-0.2);
gdi.addInput("BibGap", itow(oe->getBibClassGap()), 5); gdi.addInput("BibGap", itow(oe->getBibClassGap()), 5);
gdi.dropLine(3);
gdi.dropLine(2.4);
gdi.popX(); gdi.popX();
if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Vacancy)) {
bool bibToVacant = oe->getDCI().getInt("NoVacantBib") == 0;
gdi.addCheckbox("VacantBib", "Tilldela nummerlapp till vakanter", nullptr, bibToVacant);
gdi.dropLine(2.5);
gdi.popX();
}
gdi.fillRight(); gdi.fillRight();
gdi.addButton("DoBibs", "Tilldela", ClassesCB).setDefault(); gdi.addButton("DoBibs", "Tilldela", ClassesCB).setDefault();
@ -1914,11 +1928,17 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
pc->setBibMode(BibMode(teamBib.first)); pc->setBibMode(BibMode(teamBib.first));
} }
bool bibToVacant = true;
if (gdi.hasWidget("VacantBib")) {
bibToVacant = gdi.isChecked("VacantBib");
oe->getDI().setInt("NoVacantBib", bibToVacant ? 0 : 1);
}
pc->getDI().setString("Bib", getBibCode(bt, gdi.getText("Bib"))); pc->getDI().setString("Bib", getBibCode(bt, gdi.getText("Bib")));
pc->synchronize(); pc->synchronize();
int leg = pc->getParentClass() ? -1 : 0; int leg = pc->getParentClass() ? -1 : 0;
if (bt == AutoBibManual) { if (bt == AutoBibManual) {
oe->addBib(cid, leg, gdi.getText("Bib")); oe->addBib(cid, leg, gdi.getText("Bib"), bibToVacant);
} }
else { else {
oe->setBibClassGap(gdi.getTextNo("BibGap")); oe->setBibClassGap(gdi.getTextNo("BibGap"));
@ -3691,6 +3711,16 @@ void TabClass::saveClassSettingsTable(gdioutput &gdi) {
} }
} }
if (gdi.hasWidget("VacantBib")) {
bool vacantBib = gdi.isChecked("VacantBib");
bool vacantBibStored = oe->getDCI().getInt("NoVacantBib") == 0;
if (vacantBib != vacantBibStored) {
oe->getDI().setInt("NoVacantBib", vacantBib ? 0 : 1);
modifiedBib = true;
}
}
if (!modifiedFee.empty() && oe->getNumRunners() > 0) { if (!modifiedFee.empty() && oe->getNumRunners() > 0) {
bool updateFee = gdi.ask(L"ask:changedclassfee"); bool updateFee = gdi.ask(L"ask:changedclassfee");
@ -3724,10 +3754,18 @@ void TabClass::prepareForDrawing(gdioutput &gdi) {
if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Bib)) { if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Bib)) {
gdi.fillRight(); gdi.fillRight();
gdi.addString("", 0, "Antal reserverade nummerlappsnummer mellan klasser:"); gdi.addString("", 0, "Antal reserverade nummerlappsnummer mellan klasser:");
gdi.dropLine(-0.1); gdi.dropLine(-0.2);
gdi.addInput("BibGap", itow(oe->getBibClassGap()), 5); gdi.addInput("BibGap", itow(oe->getBibClassGap()), 5);
if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Vacancy)) {
bool bibToVacant = oe->getDCI().getInt("NoVacantBib") == 0;
gdi.dropLine(0.2);
gdi.setCX(gdi.getCX() + gdi.scaleLength(15));
gdi.addCheckbox("VacantBib", "Tilldela nummerlapp till vakanter", nullptr, bibToVacant);
}
gdi.popX(); gdi.popX();
gdi.dropLine(1.5); gdi.dropLine(2.4);
gdi.fillDown(); gdi.fillDown();
} }
@ -3868,6 +3906,14 @@ void TabClass::drawDialog(gdioutput &gdi, oEvent::DrawMethod method, const oClas
gdi.addCheckbox("HandleBibs", "Tilldela nummerlappar:", ClassesCB, lastHandleBibs).setSynchData(&lastHandleBibs); gdi.addCheckbox("HandleBibs", "Tilldela nummerlappar:", ClassesCB, lastHandleBibs).setSynchData(&lastHandleBibs);
gdi.dropLine(-0.2); gdi.dropLine(-0.2);
gdi.addInput("Bib", L"", 10, 0, L"", L"Mata in första nummerlappsnummer, eller blankt för att ta bort nummerlappar"); gdi.addInput("Bib", L"", 10, 0, L"", L"Mata in första nummerlappsnummer, eller blankt för att ta bort nummerlappar");
if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Vacancy)) {
bool bibToVacant = oe->getDCI().getInt("NoVacantBib") == 0;
gdi.dropLine(0.2);
gdi.addCheckbox("VacantBib", "Tilldela nummerlapp till vakanter", nullptr, bibToVacant);
gdi.setInputStatus("VacantBib", lastHandleBibs);
}
gdi.setInputStatus("Bib", lastHandleBibs); gdi.setInputStatus("Bib", lastHandleBibs);
gdi.fillDown(); gdi.fillDown();
gdi.dropLine(2.5); gdi.dropLine(2.5);
@ -3968,6 +4014,7 @@ void TabClass::setMultiDayClass(gdioutput &gdi, bool hasMulti, oEvent::DrawMetho
if (hasMulti) { if (hasMulti) {
gdi.check("HandleBibs", false); gdi.check("HandleBibs", false);
gdi.setInputStatus("Bib", false); gdi.setInputStatus("Bib", false);
gdi.setInputStatus("VacantBib", false, true);
} }
} }

View File

@ -636,7 +636,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data)
ListBoxInfo lbi; ListBoxInfo lbi;
bool advancedResults = false; bool advancedResults = false;
if (gdi.getSelectedItem("ListType", lbi)) { if (gdi.getSelectedItem("ListType", lbi)) {
currentListType=EStdListType(lbi.data); currentListType = EStdListType(lbi.data);
} }
else if (gdi.getSelectedItem("ResultType", lbi)) { else if (gdi.getSelectedItem("ResultType", lbi)) {
currentListType = getTypeFromResultIndex(lbi.data); currentListType = getTypeFromResultIndex(lbi.data);
@ -1230,6 +1230,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data)
else if (lbi.id == "ListSelection") { else if (lbi.id == "ListSelection") {
gdi.getSelection(lbi.id, lastClassSelection); gdi.getSelection(lbi.id, lastClassSelection);
if (gdi.hasWidget("ResultType")) { if (gdi.hasWidget("ResultType")) {
lastResultClassSelection = lastClassSelection;
ListBoxInfo entry; ListBoxInfo entry;
gdi.getSelectedItem("ResultType", entry); gdi.getSelectedItem("ResultType", entry);
gdi.setInputStatus("Generate", !lastClassSelection.empty() && int(entry.data) >= 0); gdi.setInputStatus("Generate", !lastClassSelection.empty() && int(entry.data) >= 0);
@ -3011,7 +3012,6 @@ void TabList::setResultOptionsFromType(gdioutput &gdi, int data) {
gdi.setInputStatus("ShowInterResults", builtIn); gdi.setInputStatus("ShowInterResults", builtIn);
gdi.setInputStatus("ShowSplits", builtIn); gdi.setInputStatus("ShowSplits", builtIn);
set<int> clsUnused; set<int> clsUnused;
vector< pair<wstring, size_t> > out; vector< pair<wstring, size_t> > out;
@ -3022,22 +3022,14 @@ void TabList::setResultOptionsFromType(gdioutput &gdi, int data) {
if (!out.empty() && lastLeg >= 0) if (!out.empty() && lastLeg >= 0)
gdi.selectItemByData("LegNumber", lastLeg); gdi.selectItemByData("LegNumber", lastLeg);
//oe->fillLegNumbers(gdi, "LegNumber", li.isTeamList(), true);
gdi.setInputStatus("InputNumber", false); gdi.setInputStatus("InputNumber", false);
} }
else { else {
gdi.setInputStatus("UseLargeSize", li.supportLarge); gdi.setInputStatus("UseLargeSize", li.supportLarge);
gdi.setInputStatus("InputNumber", li.supportParameter); gdi.setInputStatus("InputNumber", li.supportParameter);
//gdi.setInputStatus("SplitAnalysis", li.supportSplitAnalysis);
//gdi.setInputStatus("ShowInterResults", li.supportInterResults);
//gdi.setInputStatus("PageBreak", li.supportPageBreak);
//gdi.setInputStatus("ClassLimit", li.supportClassLimit);
if (li.supportLegs) { if (li.supportLegs) {
gdi.enableInput("LegNumber"); gdi.enableInput("LegNumber");
//oe->fillLegNumbers(gdi, "LegNumber", li.isTeamList(), true);
set<int> clsUnused; set<int> clsUnused;
vector< pair<wstring, size_t> > out; vector< pair<wstring, size_t> > out;
oe->fillLegNumbers(clsUnused, li.isTeamList(), true, out); oe->fillLegNumbers(clsUnused, li.isTeamList(), true, out);
@ -3095,12 +3087,19 @@ void TabList::setResultOptionsFromType(gdioutput &gdi, int data) {
gdi.setSelection("ListSelection", lastResultClassSelection); gdi.setSelection("ListSelection", lastResultClassSelection);
} }
gdi.setInputStatus("Generate", data >= 0 && !lastResultClassSelection.empty()); gdi.setInputStatus("Generate", data >= 0 && hasSelectedClass(gdi));
}
bool TabList::hasSelectedClass(gdioutput& gdi) {
set<int> sel;
gdi.getSelection("ListSelection", sel);
return !sel.empty();
} }
void TabList::clearCompetitionData() { void TabList::clearCompetitionData() {
SelectedList = ""; SelectedList = "";
lastResultClassSelection.clear(); lastResultClassSelection.clear();
lastClassSelection.clear();
ownWindow = false; ownWindow = false;
hideButtons = false; hideButtons = false;

View File

@ -49,6 +49,8 @@ protected:
static void createListButtons(gdioutput &gdi); static void createListButtons(gdioutput &gdi);
static bool hasSelectedClass(gdioutput& gdi);
void generateList(gdioutput &gdi, bool forceUpdate = false); void generateList(gdioutput &gdi, bool forceUpdate = false);
void selectGeneralList(gdioutput &gdi, EStdListType type); void selectGeneralList(gdioutput &gdi, EStdListType type);

View File

@ -207,8 +207,8 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) {
vector<int> delta; vector<int> delta;
vector<int> place; vector<int> place;
vector<int> after; vector<int> after;
vector<int> placeAcc; vector<oRunner::ResultData> placeAcc;
vector<int> afterAcc; vector<oRunner::ResultData> afterAcc;
r->getSplitAnalysis(delta); r->getSplitAnalysis(delta);
r->getLegTimeAfter(after); r->getLegTimeAfter(after);
@ -221,13 +221,13 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) {
for (size_t k = 0; k < delta.size(); k++) { for (size_t k = 0; k < delta.size(); k++) {
out += itow(place[k]); out += itow(place[k]);
if (k < placeAcc.size()) if (k < placeAcc.size())
out += L" (" + itow(placeAcc[k]) + L")"; out += L" (" + itow(placeAcc[k].get(false)) + L")";
if (after[k] > 0) if (after[k] > 0)
out += L" +" + formatTimeMS(after[k], false); out += L" +" + formatTimeMS(after[k], false);
if (k < afterAcc.size() && afterAcc[k]>0) if (k < afterAcc.size() && afterAcc[k].get(false)>0)
out += L" (+" + formatTimeMS(afterAcc[k], false) + L")"; out += L" (+" + formatTimeMS(afterAcc[k].get(false), false) + L")";
if (delta[k] > 0) if (delta[k] > 0)
out += L" B: " + formatTimeMS(delta[k], false); out += L" B: " + formatTimeMS(delta[k], false);
@ -463,7 +463,7 @@ int TabRunner::searchCB(gdioutput &gdi, int type, void *data) {
bool formMode = currentMode == 0; bool formMode = currentMode == 0;
vector< pair<wstring, size_t> > runners; vector<pair<wstring, size_t>> runners;
oe->fillRunners(runners, !formMode, formMode ? 0 : oEvent::RunnerFilterShowAll, filter); oe->fillRunners(runners, !formMode, formMode ? 0 : oEvent::RunnerFilterShowAll, filter);
if (filter.size() == runners.size()){ if (filter.size() == runners.size()){
@ -813,6 +813,15 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data)
else if (bi.id == "ListenReadout") { else if (bi.id == "ListenReadout") {
listenToPunches = gdi.isChecked(bi.id); listenToPunches = gdi.isChecked(bi.id);
} }
else if (bi.id == "HideControls") {
hideReportControls = true;
listenToPunches = true;
PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TRunnerTab, 0);
}
/*else if (bi.id == "ShowReportHeader") {
showReportHeader = gdi.isChecked(bi.id);
PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TRunnerTab, 0);
}*/
else if (bi.id=="Unpair") { else if (bi.id=="Unpair") {
ListBoxInfo lbi; ListBoxInfo lbi;
int cid = bi.getExtraInt(); int cid = bi.getExtraInt();
@ -1225,7 +1234,14 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data)
return 0; return 0;
} }
runnerId = bi.data; runnerId = bi.data;
//loadPage(gdi); PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TRunnerTab, 0);
}
else if (bi.id == "NumCols") {
numReportColumn = bi.data;
PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TRunnerTab, 0);
}
else if (bi.id == "NumRows") {
numReportRow = bi.data;
PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TRunnerTab, 0); PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TRunnerTab, 0);
} }
else if (bi.id=="RClass") { else if (bi.id=="RClass") {
@ -1322,24 +1338,20 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data)
cellAction(gdi, ei.getData(), b); cellAction(gdi, ei.getData(), b);
} }
else if ((ei.id == "DataUpdate") && listenToPunches && currentMode == 5) { else if (currentMode == 5 && ei.id == "PunchCard") {
if (ei.getData() > 0) { if (listenToPunches && ei.getData() > 0) {
runnerId = ei.getData(); addToReport(ei.getData(), true);
}
loadPage(gdi); loadPage(gdi);
} }
else if ((ei.id == "ReadCard") &&
(listenToPunches || oe->isReadOnly()) && currentMode == 5) {
if (ei.getData() > 0) {
vector<pRunner> rs;
oe->getRunnersByCardNo(ei.getData(), true, oEvent::CardLookupProperty::Any, rs);
if (!rs.empty()) {
runnersToReport.resize(rs.size());
for (size_t k = 0; k<rs.size(); k++)
runnersToReport[k] = make_pair(rs[k]->getId(), false);
} }
runnerId = 0; else if (currentMode == 5 && ei.id == "DataUpdate" && currentMode == 5) {
showRunnerReport(gdi);
} }
else if (currentMode == 5 && ei.id == "ReadCard" &&
(listenToPunches || oe->isReadOnly())) {
if (ei.getData() > 0)
addToReport(ei.getData(), oe->isReadOnly());
loadPage(gdi); loadPage(gdi);
} }
} }
@ -1610,85 +1622,312 @@ void TabRunner::setCardNo(gdioutput &gdi, int cardNo) {
} }
} }
string TabRunner::computeKeyForReport() {
string key = itos(runnersToReport.size());
for (auto &id : runnersToReport) {
key += "|" + itos(id.first);
pRunner r = oe->getRunner(id.first, 0);
if (r) {
key += gdioutput::toUTF8(r->getCompleteIdentification() + r->getClass(true));
key += itos(r->getStatus()) + itos(r->getFinishTime()) + itos(r->getStartTime());
vector<pFreePunch> pl;
oe->getPunchesForRunner(id.first, true, pl);
for (auto p : pl)
key += itos(p->getTimeInt());
if (r->getCard()) {
auto ch = r->getCard()->getCardHash();
key += itos(ch.first) + itos(ch.second);
}
}
}
return key;
}
void TabRunner::showRunnerReport(gdioutput &gdi) { void TabRunner::showRunnerReport(gdioutput &gdi) {
string oldHash, newHash = computeKeyForReport();
if (gdi.getData("ReportHash", oldHash) && oldHash == newHash)
return;
gdi.clearPage(true); gdi.clearPage(true);
gdi.setData("ReportHash", newHash);
currentMode = 5; currentMode = 5;
if (!ownWindow && !oe->isReadOnly()) if (!ownWindow && !oe->isReadOnly())
addToolbar(gdi); addToolbar(gdi);
else if (oe->isReadOnly()) else if (oe->isReadOnly() && !listenToPunches)
gdi.addString("", fontLarge, makeDash(L"MeOS - Resultatkiosk")).setColor(colorDarkBlue); gdi.addString("", fontLarge, makeDash(L"MeOS - Resultatkiosk")).setColor(colorDarkBlue);
gdi.dropLine(); if (!hideReportControls) {
bool any = false;
gdi.pushX(); gdi.pushX();
gdi.fillRight(); gdi.fillRight();
if (!oe->isReadOnly() || !listenToPunches) {
gdi.dropLine();
gdi.addSelection("ReportRunner", 300, 300, RunnerCB); gdi.addSelection("ReportRunner", 300, 300, RunnerCB);
oe->fillRunners(gdi, "ReportRunner", true, oEvent::RunnerFilterShowAll | oEvent::RunnerCompactMode); oe->fillRunners(gdi, "ReportRunner", true, oEvent::RunnerFilterShowAll | oEvent::RunnerCompactMode);
gdi.selectItemByData("ReportRunner", runnerId); gdi.selectItemByData("ReportRunner", runnerId);
gdi.autoGrow("ReportRunner");
any = true;
}
if (!oe->isReadOnly()) { if (!oe->isReadOnly() && !hideReportControls) {
if (!ownWindow) { if (!ownWindow) {
gdi.addButton("Kiosk", "Resultatkiosk", RunnerCB); gdi.addButton("Kiosk", "Resultatkiosk", RunnerCB);
gdi.addButton("Window", "Eget fönster", RunnerCB, "Öppna i ett nytt fönster."); gdi.addButton("Window", "Eget fönster", RunnerCB, "Öppna i ett nytt fönster.");
} }
gdi.dropLine(0.2); else {
gdi.addCheckbox("ListenReadout", "Visa senast inlästa deltagare", RunnerCB, listenToPunches); gdi.addButton("HideControls", "Dölj inställningar", RunnerCB);
} }
any = true;
gdi.dropLine(0.2);
gdi.addCheckbox("ListenReadout", "Visa senast inlästa deltagare", RunnerCB, listenToPunches);
gdi.dropLine(2);
gdi.popX();
vector<pair<wstring, size_t>> options;
for (int i = 1; i <= 10; i++)
options.emplace_back(itow(i), i);
gdi.addString("", 0, "Layout");
gdi.addSelection("NumRows", 100, 200, RunnerCB);
gdi.addString("", 0, "rader");
gdi.addItem("NumRows", options);
gdi.selectItemByData("NumRows", numReportRow);
gdi.addSelection("NumCols", 100, 200, RunnerCB);
gdi.addString("", 0, "kolumner");
gdi.addItem("NumCols", options);
gdi.selectItemByData("NumCols", numReportColumn);
//gdi.addCheckbox("ShowReportHeader", "Visa sidhuvud", RunnerCB);
}
if (any)
gdi.dropLine(3); gdi.dropLine(3);
}
gdi.popX(); gdi.popX();
gdi.registerEvent("DataUpdate", RunnerCB); gdi.registerEvent("DataUpdate", RunnerCB);
gdi.registerEvent("ReadCard", RunnerCB); gdi.registerEvent("ReadCard", RunnerCB);
gdi.registerEvent("PunchCard", RunnerCB);
gdi.setData("DataSync", 1);
gdi.setData("PunchSync", 1);
if (runnerId > 0) { if (runnerId > 0) {
runnersToReport.resize(1); bool found = false;
runnersToReport[0] = make_pair(runnerId, false); addToReport(runnerId);
} }
generateRunnerReport(*oe, gdi, runnersToReport); while (runnersToReport.size() > numReportRow * numReportColumn)
runnersToReport.pop_back();
if (runnersToReport.size() == 1) generateRunnerReport(*oe, gdi, numReportColumn, numReportRow, true, runnersToReport);
if (runnersToReport.size() > 0)
runnerId = runnersToReport[0].first; runnerId = runnersToReport[0].first;
} }
void TabRunner::generateRunnerReport(oEvent &oe, gdioutput &gdi, vector<pair<int, bool>> &runnersToReport) { void TabRunner::addToReport(int id) {
bool found = false;
for (auto& rr : runnersToReport) {
if (rr.first == id)
found = true;
}
if (!found) {
runnersToReport.emplace_front(id, false);
}
}
void TabRunner::addToReport(int cardNo, bool punchForShowReport) {
vector<pRunner> rs;
oe->getRunnersByCardNo(cardNo, true, oEvent::CardLookupProperty::Any, rs);
if (!punchForShowReport) {
// Take away runners with no card
vector<pRunner> rsFilter;
for (pRunner r : rs) {
if (r->getCard())
rsFilter.push_back(r);
}
rs.swap(rsFilter);
}
if (!rs.empty()) {
if (rs.size() == 1) {
addToReport(rs[0]->getId());
}
else {
map<int, vector<pRunner>> runnersPerTeam;
for (pRunner r : rs) {
if (r->getTeam())
runnersPerTeam[r->getTeam()->getId()].push_back(r);
else
runnersPerTeam[0].push_back(r);
}
for (auto& rpt : runnersPerTeam) {
if (rpt.second.size() == 1) {
addToReport(rpt.second[0]->getId());
}
else if (rpt.first == 0) {
sort(rpt.second.begin(), rpt.second.end(),
[](const pRunner& a, const pRunner& b) {return unsigned(a->getStartTime() - 1) < unsigned(b->getStartTime() - 1); });
// Take the last competitor using the card
addToReport(rpt.second.back()->getId());
}
else {
sort(rpt.second.begin(), rpt.second.end(),
[](const pRunner& a, const pRunner& b) {return a->getLegNumber() < b->getLegNumber(); });
bool hasResultAny = false;
bool done = false;
for (pRunner r : rpt.second) {
if (r->hasFinished())
hasResultAny = true;
else if (hasResultAny) {
// Prefer to report the next runner to run
addToReport(r->getId());
done = true;
break;
}
}
if (!done) {
if (hasResultAny) {
// Take the last one (final result)
addToReport(rpt.second.back()->getId());
}
else {
// None finished. Take first
addToReport(rpt.second.front()->getId());
}
}
}
}
}
/*runnersToReport.resize(rs.size());
for (size_t k = 0; k<rs.size(); k++)
runnersToReport[k] = make_pair(rs[k]->getId(), false);*/
}
runnerId = 0;
}
void TabRunner::generateRunnerReport(oEvent &oe, gdioutput &gdi,
int numX, int numY,
bool onlySelectedRunner,
const deque<pair<int, bool>> &runnersToReport) {
oe.synchronizeList({ oListId::oLRunnerId, oListId::oLTeamId, oListId::oLPunchId }); oe.synchronizeList({ oListId::oLRunnerId, oListId::oLTeamId, oListId::oLPunchId });
gdi.fillDown(); gdi.fillDown();
int xx, yy;
int margin = gdi.scaleLength(16);
gdi.getTargetDimension(xx, yy);
cTeam t = 0; int maxWidth = max(gdi.scaleLength(130 * 3) + 2*margin, xx / numX - margin * (numX + 1));
if (numX == 1 && numY == 1)
maxWidth = min(maxWidth, gdi.scaleLength(130)*6+margin);
bool frame = true;
vector<const oTeam *> tList;
set<int> clsSet; set<int> clsSet;
for (size_t k = 0; k < runnersToReport.size(); k++) { for (size_t k = 0; k < runnersToReport.size(); k++) {
pRunner r = oe.getRunner(runnersToReport[k].first, 0); pRunner r = oe.getRunner(runnersToReport[k].first, 0);
if (!r) if (!r)
continue; continue;
clsSet.insert(r->getClassId(true)); clsSet.insert(r->getClassId(true));
if (r && r->getTeam()) { pTeam t = r->getTeam();
if (t) {
pClass cls = r->getClassRef(true); pClass cls = r->getClassRef(true);
if (cls && cls->getClassType() != oClassRelay) if (cls && cls->getClassType() != oClassRelay)
continue; continue;
if (t == 0) bool added = count(tList.begin(), tList.end(), t) > 0;
t = r->getTeam(); if (added)
continue;
tList.push_back(t);
} }
} }
oe.calculateResults(clsSet, oEvent::ResultType::PreliminarySplitResults, true); oe.calculateResults(clsSet, oEvent::ResultType::PreliminarySplitResults, true);
oe.calculateResults(clsSet, oEvent::ResultType::ClassResult); oe.calculateResults(clsSet, oEvent::ResultType::ClassResult);
if (t == 0) { RECT rcFrame;
for (size_t k = 0; k < runnersToReport.size(); k++)
runnerReport(oe, gdi, runnersToReport[k].first, runnersToReport[k].second); auto drawBox = [&gdi](RECT &rcFrame) {
RECT rc = rcFrame;
int mg = gdi.scaleLength(5);
rc.left -= mg;
rc.top -= mg;
rc.right += mg;
rc.bottom += mg;
gdi.addRectangle(rc, GDICOLOR::colorLightCyan, true, true);
};
int baseX = gdi.getCX();
int baseY = gdi.getCY();
vector<RECT> rcList;
auto updatePositionDrawBox = [&](bool force) {
if (!force)
rcList.push_back(rcFrame);
if ((force && !rcList.empty()) || rcList.size() == numX) {
int maxYP = 0;
for (RECT& rc : rcList)
maxYP = max<int>(maxYP, rc.bottom);
for (RECT& rc : rcList) {
rc.bottom = maxYP;
drawBox(rc);
} }
else { rcList.clear();
baseY = maxYP + margin;
}
gdi.setCY(baseY);
gdi.setCX(baseX + (maxWidth + margin) * rcList.size());
};
for (size_t k = 0; k < runnersToReport.size(); k++) {
pRunner r = oe.getRunner(runnersToReport[k].first, 0);
if (!r)
continue;
if (count(tList.begin(), tList.end(), r->getTeam()) == 0) {
runnerReport(oe, gdi, runnersToReport[k].first, runnersToReport[k].second, maxWidth, rcFrame);
updatePositionDrawBox(false);
}
}
if (tList.size() > 0) {
oe.calculateTeamResults(clsSet, oEvent::ResultType::ClassResult); oe.calculateTeamResults(clsSet, oEvent::ResultType::ClassResult);
for (const oTeam *t : tList) {
teamReport(oe, gdi, t, onlySelectedRunner, runnersToReport, maxWidth, rcFrame);
updatePositionDrawBox(false);
}
}
updatePositionDrawBox(true);
}
void TabRunner::teamReport(oEvent& oe, gdioutput& gdi,
const oTeam* t,
bool onlySelectedRunner,
const deque<pair<int, bool>>& runnersToReport,
int maxWidth,
RECT& rc) {
rc.top = gdi.getCY();
rc.left = gdi.getCX();
rc.right = rc.left + maxWidth;
set<int> selectedRunners; set<int> selectedRunners;
bool selHasRes = false; bool selHasPartialRes = false;
for (size_t k = 0; k < runnersToReport.size(); k++) { for (size_t k = 0; k < runnersToReport.size(); k++) {
selectedRunners.insert(runnersToReport[k].first);
pRunner r = oe.getRunner(runnersToReport[k].first, 0); pRunner r = oe.getRunner(runnersToReport[k].first, 0);
if (r && r->getTeam() == t) {
selectedRunners.insert(runnersToReport[k].first);
if (r && r->hasOnCourseResult()) if (r && r->hasOnCourseResult())
selHasRes = true; selHasPartialRes = true; //Partial (radio) or complete result
}
} }
wstring tInfo = t->getName(); wstring tInfo = t->getName();
@ -1705,32 +1944,70 @@ void TabRunner::showRunnerReport(gdioutput &gdi) {
gdi.addStringUT(boldLarge, tInfo); gdi.addStringUT(boldLarge, tInfo);
gdi.dropLine(); gdi.dropLine();
pClass cls = t->getClassRef(false);
bool visitedSelected = false; bool visitedSelected = false;
for (int leg = 0; leg < t->getNumRunners(); leg++) { for (int leg = 0; leg < t->getNumRunners(); leg++) {
if (selHasRes && visitedSelected) if ((selHasPartialRes || onlySelectedRunner) && visitedSelected)
break; break;
pRunner r = t->getRunner(leg); pRunner r = t->getRunner(leg);
pRunner nextR = t->getRunner(leg + 1); pRunner nextR = t->getRunner(leg + 1);
bool nextSelected = nextR && selectedRunners.count(nextR->getId()); bool nextSelected = false;
if (cls) {
// Check if leg has the selected runner (parallel legs)
int legNr, legOrd;
cls->splitLegNumberParallel(leg, legNr, legOrd);
int nextLeg = leg;
while (++nextLeg < t->getNumRunners()) {
int legNrN, legOrdN;
cls->splitLegNumberParallel(leg, legNrN, legOrdN);
if (legNrN == legNr + 1) {
nextR = t->getRunner(nextLeg);
nextSelected = nextR && selectedRunners.count(nextR->getId());
if (nextSelected)
break;
}
}
}
else {
nextSelected = nextR && selectedRunners.count(nextR->getId());
}
if (r) { if (r) {
bool selected = selectedRunners.count(r->getId()) > 0; bool selected = selectedRunners.count(r->getId()) > 0;
if (selHasRes) { if (onlySelectedRunner && !selected) {
runnerReport(oe, gdi, r->getId(), !selected); if (!nextSelected)
continue; // Always skip if next is not selected
if (nextR->hasResult())
continue; // Only include previous if next does not have results
}
RECT dmy;
if (selHasPartialRes) {
// The selected runner has some result. Focus on that
runnerReport(oe, gdi, r->getId(), !selected, maxWidth, dmy);
} }
else { else {
runnerReport(oe, gdi, r->getId(), !nextSelected); // The selected runner has not started. Focus on previous result
runnerReport(oe, gdi, r->getId(), !nextSelected, maxWidth, dmy);
} }
visitedSelected |= selected; visitedSelected |= selected;
} }
} }
} rc.bottom = gdi.getCY();
} }
void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) { void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi,
int id, bool compact,
int maxWidth, RECT &rc) {
rc.top = gdi.getCY();
rc.left = gdi.getCX();
rc.right = rc.left + maxWidth;
pRunner r = oe.getRunner(id, 0); pRunner r = oe.getRunner(id, 0);
if (!r || ! r->getClassRef(false)) if (!r || ! r->getClassRef(false))
return; return;
@ -1739,7 +2016,8 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
gdi.fillDown(); gdi.fillDown();
if (r->getTeam() == 0) { if (r->getTeam() == 0) {
gdi.addStringUT(fontMediumPlus, r->getClass(true)); gdi.addStringUT(fontMediumPlus, r->getClass(true));
gdi.addStringUT(boldLarge, r->getCompleteIdentification()); gdi.addStringUT(gdi.getCY(), gdi.getCX(), boldLarge,
r->getCompleteIdentification(), maxWidth - 4);
} }
else { else {
wstring s; wstring s;
@ -1784,20 +2062,31 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
gdi.popX(); gdi.popX();
gdi.fillRight(); gdi.fillRight();
int ww = gdi.scaleLength(120);
auto optionalBreakLine = [&] {
if (gdi.getCX() + ww > rc.left + maxWidth) {
gdi.popX();
gdi.dropLine();
}
};
if (r->getStartTime() > 0) if (r->getStartTime() > 0) {
optionalBreakLine();
gdi.addString("", fontMedium, L"Starttid: X #" + r->getStartTimeCompact()); gdi.addString("", fontMedium, L"Starttid: X #" + r->getStartTimeCompact());
}
if (r->getFinishTime() > 0) if (r->getFinishTime() > 0) {
optionalBreakLine();
gdi.addString("", fontMedium, L"Måltid: X #" + r->getFinishTimeS(false, SubSecond::Auto)); gdi.addString("", fontMedium, L"Måltid: X #" + r->getFinishTimeS(false, SubSecond::Auto));
}
const wstring &after = oe.formatListString(lRunnerTimeAfter, r); const wstring &after = oe.formatListString(lRunnerTimeAfter, r);
if (!after.empty()) { if (!after.empty()) {
optionalBreakLine();
gdi.addString("", fontMedium, L"Tid efter: X #" + after); gdi.addString("", fontMedium, L"Tid efter: X #" + after);
} }
const wstring &lost = oe.formatListString(lRunnerLostTime, r); const wstring &lost = oe.formatListString(lRunnerLostTime, r);
if (!lost.empty()) { if (!lost.empty()) {
optionalBreakLine();
gdi.addString("", fontMedium, L"Bomtid: X #" + lost).setColor(colorDarkRed); gdi.addString("", fontMedium, L"Bomtid: X #" + lost).setColor(colorDarkRed);
} }
@ -1813,17 +2102,28 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
int xp = gdi.getCX(); int xp = gdi.getCX();
int yp = gdi.getCY(); int yp = gdi.getCY();
int xw = gdi.scaleLength(130); int xw = gdi.scaleLength(130);
int cx = xp; int mg4 = gdi.scaleLength(4);
int cx = xp + mg4;
int limit = (9*xw)/10; int limit = (9*xw)/10;
int lh = gdi.getLineHeight(); int lh = gdi.getLineHeight();
int maxTimesPerLine = min(10, max(3, maxWidth / xw));
auto drawBox = [&gdi, xw, mg4, lh](int yp, int cx, GDICOLOR color) {
RECT rc;
rc.top = yp - mg4 / 2;
rc.bottom = yp + lh * 5 - mg4;
rc.left = cx - mg4;
rc.right = cx + xw - mg4 * 2;
gdi.addRectangle(rc, color);
};
if (crs && r->getStatus() != StatusUnknown) { if (crs && r->getStatus() != StatusUnknown) {
int nc = crs->getNumControls(); int nc = crs->getNumControls();
vector<int> delta; vector<int> delta;
vector<int> place; vector<int> place;
vector<int> after; vector<int> after;
vector<int> placeAcc; vector<oRunner::ResultData> placeAcc;
vector<int> afterAcc; vector<oRunner::ResultData> afterAcc;
r->getSplitAnalysis(delta); r->getSplitAnalysis(delta);
r->getLegTimeAfter(after); r->getLegTimeAfter(after);
@ -1844,7 +2144,7 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
else else
gdi.addStringUT(yp, cx, boldText, name, limit); gdi.addStringUT(yp, cx, boldText, name, limit);
wstring split = r->getSplitTimeS(k, false); wstring split = r->getSplitTimeS(k, false, SubSecond::Off);
int bestTime = 0; int bestTime = 0;
if ( k < int(after.size()) && after[k] >= 0) if ( k < int(after.size()) && after[k] >= 0)
@ -1865,14 +2165,14 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
gdi.addStringUT(yp + lh, cx, fontMedium, split, limit); gdi.addStringUT(yp + lh, cx, fontMedium, split, limit);
if (k>0 && k < int(placeAcc.size())) { if (k>0 && k < int(placeAcc.size())) {
split = r->getPunchTimeS(k, false, false, SubSecond::Auto); split = r->getPunchTimeS(k, false, false, false, SubSecond::Auto);
wstring pl = placeAcc[k] > 0 ? itow(placeAcc[k]) : L"-"; wstring pl = placeAcc[k].get(false) > 0 ? itow(placeAcc[k].get(false)) : L"-";
if (k < int(afterAcc.size()) ) { if (k < int(afterAcc.size()) ) {
if (afterAcc[k] > 0) if (afterAcc[k].get(false) > 0)
split += L" (" + pl + L", +" + formatTimeMS(afterAcc[k], false) + L")"; split += L" (" + pl + L", +" + formatTimeMS(afterAcc[k].get(false), false) + L")";
else if (placeAcc[k] == 1) else if (placeAcc[k].get(false) == 1)
split += lang.tl(" (ledare)"); split += lang.tl(" (ledare)");
else if (placeAcc[k] > 0) else if (placeAcc[k].get(false) > 0)
split += L" " + pl; split += L" " + pl;
} }
gdi.addStringUT(yp + 2*lh, cx, fontMedium, split, limit).setColor(colorDarkBlue); gdi.addStringUT(yp + 2*lh, cx, fontMedium, split, limit).setColor(colorDarkBlue);
@ -1885,18 +2185,10 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
colorMediumDarkRed : colorMediumRed; colorMediumDarkRed : colorMediumRed;
} }
RECT rc; drawBox(yp, cx, color);
rc.top = yp - 2;
rc.bottom = yp + lh*5 - 4;
rc.left = cx - 4;
rc.right = cx + xw - 8;
gdi.addRectangle(rc, color);
cx += xw; cx += xw;
if ( (k+1) % maxTimesPerLine == 0) {
if (k % 6 == 5) { cx = xp + mg4;
cx = xp;
yp += lh * 5; yp += lh * 5;
} }
} }
@ -1950,26 +2242,19 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
GDICOLOR color = colorDefault; GDICOLOR color = colorDefault;
RECT rc; drawBox(yp, cx, color);
rc.top = yp - 2;
rc.bottom = yp + lh*5 - 4;
rc.left = cx - 4;
rc.right = cx + xw - 8;
gdi.addRectangle(rc, color);
cx += xw; cx += xw;
if (k % 6 == 5) { if ((k + 1) % maxTimesPerLine == 0) {
cx = xp; cx = xp + mg4;
yp += lh * 5; yp += lh * 5;
} }
} }
} }
gdi.dropLine(3);
gdi.popX(); gdi.popX();
rc.bottom = gdi.getCY();
} }
void TabRunner::showVacancyList(gdioutput &gdi, const string &method, int classId) void TabRunner::showVacancyList(gdioutput &gdi, const string &method, int classId)
{ {
gdi.clearPage(true); gdi.clearPage(true);

View File

@ -23,6 +23,7 @@
#include "tabbase.h" #include "tabbase.h"
#include "Printer.h" #include "Printer.h"
#include "autocompletehandler.h" #include "autocompletehandler.h"
#include <deque>
class Table; class Table;
struct AutoCompleteRecord; struct AutoCompleteRecord;
@ -43,6 +44,15 @@ private:
void selectRunner(gdioutput &gdi, pRunner r); void selectRunner(gdioutput &gdi, pRunner r);
int numReportRow = 1;
int numReportColumn = 1;
bool hideReportControls = false;
bool showReportHeader = true;
void addToReport(int cardNo, bool punchForShowReport);
void addToReport(int id);
string computeKeyForReport();
wstring lastSearchExpr; wstring lastSearchExpr;
unordered_set<int> lastFilter; unordered_set<int> lastFilter;
DWORD timeToFill; DWORD timeToFill;
@ -64,7 +74,7 @@ private:
int runnerId; int runnerId;
bool ownWindow; bool ownWindow;
bool listenToPunches; bool listenToPunches;
vector< pair<int, bool> > runnersToReport; deque<pair<int, bool>> runnersToReport;
vector<pRunner> unknown_dns; vector<pRunner> unknown_dns;
vector<pRunner> known_dns; vector<pRunner> known_dns;
@ -76,7 +86,19 @@ private:
PrinterObject splitPrinter; PrinterObject splitPrinter;
void showRunnerReport(gdioutput &gdi); void showRunnerReport(gdioutput &gdi);
static void runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compactReport);
static void runnerReport(oEvent &oe, gdioutput &gdi,
int id, bool compactReport,
int maxWidth,
RECT& rc);
static void teamReport(oEvent& oe, gdioutput& gdi,
const oTeam *team,
bool onlySelectedRunner,
const deque<pair<int, bool>> &runners,
int maxWidth,
RECT &rc);
void showVacancyList(gdioutput &gdi, const string &method="", int classId=0); void showVacancyList(gdioutput &gdi, const string &method="", int classId=0);
void showCardsList(gdioutput &gdi); void showCardsList(gdioutput &gdi);
@ -126,7 +148,11 @@ public:
bool loadPage(gdioutput &gdi); bool loadPage(gdioutput &gdi);
bool loadPage(gdioutput &gdi, int runnerId); bool loadPage(gdioutput &gdi, int runnerId);
static void generateRunnerReport(oEvent &oe, gdioutput &gdi, vector<pair<int, bool>> &runnersToReport); static void generateRunnerReport(oEvent &oe,
gdioutput &gdi,
int numX, int numY,
bool onlySelectedRunner,
const deque<pair<int, bool>> &runnersToReport);
TabRunner(oEvent *oe); TabRunner(oEvent *oe);
~TabRunner(void); ~TabRunner(void);

View File

@ -84,7 +84,6 @@ TabSI::TabSI(oEvent* poe) :TabBase(poe), activeSIC(ConvertedTimeStatus::Unknown)
minRunnerId = 0; minRunnerId = 0;
inputId = 0; inputId = 0;
printErrorShown = false; printErrorShown = false;
NC = 8;
splitPrinter.onlyChanged = false; splitPrinter.onlyChanged = false;
} }
@ -145,8 +144,7 @@ string TabSI::typeFromSndType(SND s) {
return ""; return "";
} }
int TabSI::siCB(gdioutput& gdi, int type, void* data) int TabSI::siCB(gdioutput& gdi, int type, void* data) {
{
if (type == GUI_BUTTON) { if (type == GUI_BUTTON) {
ButtonInfo bi = *(ButtonInfo*)data; ButtonInfo bi = *(ButtonInfo*)data;
@ -351,6 +349,12 @@ int TabSI::siCB(gdioutput& gdi, int type, void* data)
ListBoxInfo lbi; ListBoxInfo lbi;
if (gdi.getSelectedItem("ComPort", lbi)) { if (gdi.getSelectedItem("ComPort", lbi)) {
if (lbi.data == 9999) {
showTestingPanel = !showTestingPanel;
loadPage(gdi);
return 0;
}
swprintf_s(bf, 64, L"COM%d", lbi.getDataInt()); swprintf_s(bf, 64, L"COM%d", lbi.getDataInt());
wstring port = bf; wstring port = bf;
@ -705,11 +709,20 @@ int TabSI::siCB(gdioutput& gdi, int type, void* data)
sic.CardNumber = gdi.getTextNo("SI"); sic.CardNumber = gdi.getTextNo("SI");
int f = convertAbsoluteTimeHMS(gdi.getText("Finish"), oe->getZeroTimeNum()); int f = convertAbsoluteTimeHMS(gdi.getText("Finish"), oe->getZeroTimeNum());
int s = convertAbsoluteTimeHMS(gdi.getText("Start"), oe->getZeroTimeNum()); int s = convertAbsoluteTimeHMS(gdi.getText("Start"), oe->getZeroTimeNum());
int c = convertAbsoluteTimeHMS(gdi.getText("Check"), oe->getZeroTimeNum());
testStartTime.clear();
testFinishTime.clear();
testCheckTime.clear();
testCardNumber = 0;
if (f < s) { if (f < s) {
f += 24 * timeConstHour; f += 24 * timeConstHour;
} }
sic.FinishPunch.Time = f % (24 * timeConstHour); sic.FinishPunch.Time = f % (24 * timeConstHour);
sic.StartPunch.Time = s % (24 * timeConstHour); sic.StartPunch.Time = s % (24 * timeConstHour);
sic.CheckPunch.Time = c % (24 * timeConstHour);
if (!gdi.isChecked("HasFinish")) { if (!gdi.isChecked("HasFinish")) {
sic.FinishPunch.Code = -1; sic.FinishPunch.Code = -1;
sic.FinishPunch.Time = 0; sic.FinishPunch.Time = 0;
@ -720,9 +733,20 @@ int TabSI::siCB(gdioutput& gdi, int type, void* data)
sic.StartPunch.Time = 0; sic.StartPunch.Time = 0;
} }
if (!gdi.isChecked("HasCheck")) {
sic.CheckPunch.Code = -1;
sic.CheckPunch.Time = 0;
}
if (NC > testControls.size())
testControls.resize(NC);
for (int i = 0; i < NC; i++)
testControls[i] = gdi.getTextNo("C" + itos(i + 1));
double t = 0.1; double t = 0.1;
for (sic.nPunch = 0; sic.nPunch<unsigned(NC); sic.nPunch++) { for (sic.nPunch = 0; sic.nPunch<unsigned(NC); sic.nPunch++) {
int c = gdi.getTextNo("C" + itos(sic.nPunch + 1)); int c = testControls[sic.nPunch];
sic.Punch[sic.nPunch].Time = ((int(f * t + s * (1.0 - t))/ timeUnitsPerSecond) % (24 * timeConstSecPerHour)) * timeUnitsPerSecond; sic.Punch[sic.nPunch].Time = ((int(f * t + s * (1.0 - t))/ timeUnitsPerSecond) % (24 * timeConstSecPerHour)) * timeUnitsPerSecond;
t += ((1.0 - t) * (sic.nPunch + 1) / 10.0) * ((rand() % 100) + 400.0) / 500.0; t += ((1.0 - t) * (sic.nPunch + 1) / 10.0) * ((rand() % 100) + 400.0) / 500.0;
if ((sic.nPunch % 11) == 1 || 5 == (sic.nPunch % 8)) if ((sic.nPunch % 11) == 1 || 5 == (sic.nPunch % 8))
@ -754,31 +778,34 @@ int TabSI::siCB(gdioutput& gdi, int type, void* data)
sic.FinishPunch.Code = -1; sic.FinishPunch.Code = -1;
sic.CheckPunch.Code = -1; sic.CheckPunch.Code = -1;
sic.StartPunch.Code = -1; sic.StartPunch.Code = -1;
sic.CardNumber = gdi.getTextNo("SI"); sic.CardNumber = gdi.getTextNo("SI");
int f = convertAbsoluteTimeHMS(gdi.getText("Finish"), oe->getZeroTimeNum()); int t = convertAbsoluteTimeHMS(gdi.getText("PunchTime"), oe->getZeroTimeNum());
if (f > 0) { testPunchTime.clear();
sic.FinishPunch.Time = f; testCardNumber = 0;
sic.FinishPunch.Code = 1;
sic.punchOnly = true;
gSI->addCard(sic);
return 0;
}
int s = convertAbsoluteTimeHMS(gdi.getText("Start"), oe->getZeroTimeNum()); if (t > 0) {
if (s > 0) { if (testType == oPunch::PunchStart) {
sic.StartPunch.Time = s; sic.StartPunch.Time = t;
sic.StartPunch.Code = 1; sic.StartPunch.Code = 1;
}
else if (testType == oPunch::PunchFinish) {
sic.FinishPunch.Time = t;
sic.FinishPunch.Code = 1;
}
else if (testType == oPunch::PunchCheck) {
sic.CheckPunch.Time = t;
sic.CheckPunch.Code = 1;
}
else {
sic.Punch[sic.nPunch].Code = gdi.getTextNo("ControlNumber");
sic.Punch[sic.nPunch].Time = t;
sic.nPunch = 1;
}
sic.punchOnly = true; sic.punchOnly = true;
sic.convertedTime = ConvertedTimeStatus::Hour24;
gSI->addCard(sic); gSI->addCard(sic);
return 0; return 0;
} }
sic.Punch[sic.nPunch].Code = gdi.getTextNo("C1");
sic.Punch[sic.nPunch].Time = convertAbsoluteTimeHMS(gdi.getText("C2"), oe->getZeroTimeNum());
sic.nPunch = 1;
sic.punchOnly = true;
gSI->addCard(sic);
} }
else if (bi.id == "Cancel") { else if (bi.id == "Cancel") {
int origin = bi.getExtraInt(); int origin = bi.getExtraInt();
@ -1411,14 +1438,20 @@ int TabSI::siCB(gdioutput& gdi, int type, void* data)
updateEntryInfo(gdi); updateEntryInfo(gdi);
} }
else if (bi.id == "ComPort") { else if (bi.id == "ComPort") {
bool active = true;
if (bi.data == 9999)
active = showTestingPanel;
else {
wchar_t bf[64]; wchar_t bf[64];
if (bi.text.substr(0, 3) != L"TCP") if (bi.text.substr(0, 3) != L"TCP")
swprintf_s(bf, 64, L"COM%d", bi.getDataInt()); swprintf_s(bf, 64, L"COM%d", bi.getDataInt());
else else
wcscpy_s(bf, L"TCP"); wcscpy_s(bf, L"TCP");
if (gSI->isPortOpen(bf)) active = gSI->isPortOpen(bf);
}
if (active)
gdi.setText("StartSI", lang.tl("Koppla ifrån")); gdi.setText("StartSI", lang.tl("Koppla ifrån"));
else else
gdi.setText("StartSI", lang.tl("Aktivera")); gdi.setText("StartSI", lang.tl("Aktivera"));
@ -1463,9 +1496,15 @@ int TabSI::siCB(gdioutput& gdi, int type, void* data)
updateEntryInfo(gdi); updateEntryInfo(gdi);
} }
else if (bi.id == "NC") { else if (bi.id == "NC") {
readTestData(gdi);
NC = bi.data; NC = bi.data;
PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TSITab, 0); PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TSITab, 0);
} }
else if (bi.id == "TestType") {
readTestData(gdi);
testType = bi.data;
PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TSITab, 0);
}
} }
else if (type == GUI_LINK) { else if (type == GUI_LINK) {
TextInfo ti = *(TextInfo*)data; TextInfo ti = *(TextInfo*)data;
@ -1483,6 +1522,13 @@ int TabSI::siCB(gdioutput& gdi, int type, void* data)
runnerMatchedId = r->getId(); runnerMatchedId = r->getId();
} }
} }
else if (ti.id == "edit") {
int rId = ti.getExtraInt();
if (oe->getRunner(rId, 0)) {
TabRunner& tr = dynamic_cast<TabRunner&>(*gdi.getTabs().get(TRunnerTab));
tr.loadPage(gdi, rId);
}
}
} }
else if (type == GUI_COMBO) { else if (type == GUI_COMBO) {
ListBoxInfo bi = *(ListBoxInfo*)data; ListBoxInfo bi = *(ListBoxInfo*)data;
@ -1672,16 +1718,25 @@ int TabSI::siCB(gdioutput& gdi, int type, void* data)
} }
else if (ii.id == "SI") { else if (ii.id == "SI") {
pRunner r = oe->getRunnerByCardNo(_wtoi(ii.text.c_str()), 0, oEvent::CardLookupProperty::ForReadout); pRunner r = oe->getRunnerByCardNo(_wtoi(ii.text.c_str()), 0, oEvent::CardLookupProperty::ForReadout);
if (r && r->getStartTime() > 0) { if (testType == 0 && r) {
if (r->getStartTime() > 0) {
gdi.setText("Start", r->getStartTimeS()); gdi.setText("Start", r->getStartTimeS());
gdi.check("HasStart", false); gdi.check("HasStart", false);
int f = r->getStartTime() + (2800 + rand() % 1200) * timeConstSecond; int f = r->getStartTime() + (2800 + rand() % 1200) * timeConstSecond;
gdi.setText("Finish", oe->getAbsTime(f)); gdi.setText("Finish", oe->getAbsTime(f));
int c = r->getStartTime() - (120 + rand() % 30) * timeConstSecond;
gdi.setText("Check", oe->getAbsTime(c));
}
pCourse pc = r->getCourse(false); pCourse pc = r->getCourse(false);
if (pc) { if (pc) {
if (testControls.size() < pc->getNumControls())
testControls.resize(pc->getNumControls());
for (int n = 0; n < pc->getNumControls(); n++) { for (int n = 0; n < pc->getNumControls(); n++) {
if (pc->getControl(n) && n < NC) { if (pc->getControl(n)) {
gdi.setText("C" + itos(n + 1), pc->getControl(n)->getFirstNumber()); testControls[n] = pc->getControl(n)->getFirstNumber();
if (n < NC)
gdi.setText("C" + itos(n + 1), testControls[n]);
} }
} }
} }
@ -1833,7 +1888,11 @@ void TabSI::refillComPorts(gdioutput& gdi)
gdi.addItem("ComPort", L"TCP [OK]", active); gdi.addItem("ComPort", L"TCP [OK]", active);
} }
else else
gdi.addItem("ComPort", L"TCP"); gdi.addItem("ComPort", L"TCP", 0);
gdi.addItem("ComPort", lang.tl("Testning"), 9999);
if (showTestingPanel)
active = 9999;
if (addTestPort) if (addTestPort)
gdi.addItem("ComPort", L"TEST"); gdi.addItem("ComPort", L"TEST");
@ -1909,7 +1968,7 @@ void TabSI::showReadCards(gdioutput& gdi, vector<SICard>& cards)
if (r != 0) if (r != 0)
gdi.addStringUT(yp, xp + off[1], 0, r->getName(), off[2] - off[1] + margin); gdi.addStringUT(yp, xp + off[1], 0, r->getName(), off[2] - off[1] + margin);
gdi.addStringUT(yp, xp + off[2], 0, oe->getAbsTime(cards[k].FinishPunch.Time)); gdi.addStringUT(yp, xp + off[2], 0, formatTimeHMS(cards[k].FinishPunch.Time));
yp += gdi.getLineHeight(); yp += gdi.getLineHeight();
} }
} }
@ -1941,28 +2000,66 @@ bool TabSI::loadPage(gdioutput& gdi) {
firstLoadedAfterNew = false; firstLoadedAfterNew = false;
} }
#ifdef _DEBUG
if (showTestingPanel) {
RECT rc;
rc.left = gdi.getCX();
rc.top = gdi.getCY();
gdi.setCX(gdi.getCX() + gdi.scaleLength(4));
gdi.setCY(gdi.getCY() + gdi.scaleLength(4));
gdi.addString("", fontMediumPlus, "Inmatning Testning");
gdi.fillRight(); gdi.fillRight();
gdi.pushX(); gdi.pushX();
gdi.addInput("SI", L"", 10, SportIdentCB, L"SI");
gdi.addSelection("TestType", 100, 100, SportIdentCB, L"Typ:");
gdi.addItem("TestType", lang.tl("Avläsning"), 0);
gdi.addItem("TestType", lang.tl("Mål"), 2);
gdi.addItem("TestType", lang.tl("Start"), 1);
gdi.addItem("TestType", lang.tl("Check"), 3);
gdi.addItem("TestType", lang.tl("Radio"), 10);
gdi.selectItemByData("TestType", testType);
gdi.addInput("SI", testCardNumber > 0 ? itow(testCardNumber) : L"", 10, SportIdentCB, L"Bricknummer:");
if (testType == 0) {
int s = timeConstHour + (rand() % 60) * timeConstMinute; int s = timeConstHour + (rand() % 60) * timeConstMinute;
int f = s + timeConstHour / 2 + ((rand() % 3600) / 3) * timeConstSecond; int f = s + timeConstHour / 2 + ((rand() % 3600) / 3) * timeConstSecond;
wstring ss = oe->getAbsTime(s);
wstring fs = oe->getAbsTime(f);
wstring cs = oe->getAbsTime(s - (60 + rand() % 60) * timeConstSecond);
if (!testStartTime.empty())
ss = testStartTime;
if (!testFinishTime.empty())
fs = testFinishTime;
if (!testCheckTime.empty())
cs = testCheckTime;
gdi.setCX(gdi.getCX() + gdi.getLineHeight()); gdi.setCX(gdi.getCX() + gdi.getLineHeight());
gdi.dropLine(1.4); gdi.dropLine(1.6);
gdi.addCheckbox("HasStart", ""); gdi.addCheckbox("HasCheck", "", nullptr, useTestCheck);
gdi.dropLine(-1.4); gdi.dropLine(-1.6);
gdi.setCX(gdi.getCX() - gdi.getLineHeight()); gdi.setCX(gdi.getCX() - gdi.getLineHeight());
gdi.addInput("Start", oe->getAbsTime(s), 6, 0, L"Start"); gdi.addInput("Check", cs, 6, 0, L"Check:");
gdi.dropLine(1.4); gdi.dropLine(1.6);
gdi.addCheckbox("HasFinish", ""); gdi.addCheckbox("HasStart", "", nullptr, useTestStart);
gdi.dropLine(-1.4); gdi.dropLine(-1.6);
gdi.setCX(gdi.getCX() - gdi.getLineHeight());
gdi.addInput("Start", ss, 6, 0, L"Start:");
gdi.dropLine(1.6);
gdi.addCheckbox("HasFinish", "", nullptr, useTestFinish);
gdi.dropLine(-1.6);
gdi.setCX(gdi.getCX() - gdi.getLineHeight()); gdi.setCX(gdi.getCX() - gdi.getLineHeight());
gdi.addInput("Finish", oe->getAbsTime(f), 6, 0, L"Mål"); gdi.addInput("Finish", fs, 6, 0, L"Mål:");
gdi.addSelection("NC", 45, 200, SportIdentCB, L"NC"); gdi.addSelection("NC", 45, 200, SportIdentCB, L"Stämplingar:");
const int src[11] = { 33, 34, 45, 50, 36, 38, 59, 61, 62, 67, 100 }; const int src[11] = { 33, 34, 45, 50, 36, 38, 59, 61, 62, 67, 100 };
for (int i = 0; i < 32; i++) for (int i = 0; i < 32; i++)
@ -1984,16 +2081,41 @@ bool TabSI::loadPage(gdioutput& gdi) {
c = src[ix] + level * 10; c = src[ix] + level * 10;
} }
if (i < testControls.size())
c = testControls[i];
gdi.addInput("C" + itos(i + 1), itow(c), 3, 0, L"#C" + itow(i + 1)); gdi.addInput("C" + itos(i + 1), itow(c), 3, 0, L"#C" + itow(i + 1));
} }
gdi.dropLine(); gdi.dropLine();
gdi.addButton("Save", "Bricka", SportIdentCB);
gdi.fillDown(); gdi.fillDown();
gdi.addButton("Save", "Spara", SportIdentCB);
gdi.addButton("SaveP", "Stämpling", SportIdentCB);
gdi.popX(); gdi.popX();
#endif
}
else {
int p = timeConstHour + (rand() % 60) * timeConstMinute;
wstring ps = oe->getAbsTime(p);
if (!testPunchTime.empty())
ps = testPunchTime;
if (testType == 10)
gdi.addInput("ControlNumber", itow(testRadioNumber), 6, 0, L"Kontroll:");
gdi.addInput("PunchTime", ps, 6, 0, L"Tid:");
gdi.dropLine();
gdi.fillDown();
gdi.addButton("SaveP", "Spara", SportIdentCB);
gdi.popX();
}
rc.right = gdi.getWidth();
rc.bottom = gdi.getCY();
gdi.addRectangle(rc, GDICOLOR::colorLightMagenta);
gdi.dropLine();
}
gdi.addString("", boldLarge, "SportIdent"); gdi.addString("", boldLarge, "SportIdent");
gdi.dropLine(); gdi.dropLine();
@ -2944,6 +3066,7 @@ bool TabSI::processCard(gdioutput& gdi, pRunner runner, const SICard& csic, bool
runner->setStatus(StatusOK, true, oBase::ChangeType::Update, false); runner->setStatus(StatusOK, true, oBase::ChangeType::Update, false);
SICard sic(csic); SICard sic(csic);
StoredReadout rout; StoredReadout rout;
rout.runnerId = runner->getId();
if (!csic.isManualInput()) { if (!csic.isManualInput()) {
pCard card = gEvent->allocateCard(runner); pCard card = gEvent->allocateCard(runner);
@ -3125,6 +3248,12 @@ RECT TabSI::StoredReadout::computeRC(gdioutput& gdi) const {
void TabSI::StoredReadout::render(gdioutput& gdi, const RECT& rc) const { void TabSI::StoredReadout::render(gdioutput& gdi, const RECT& rc) const {
gdi.fillDown(); gdi.fillDown();
gdi.addRectangle(rc, color, true); gdi.addRectangle(rc, color, true);
//gdi.addString("edit", rc.right - gdi.scaleLength(30), rc.top+gdi.scaleLength(4), textImage, "S" + itos(gdi.scaleLength(24)));
if (runnerId > 0) {
gdi.addImage("edit", rc.top + gdi.scaleLength(4), rc.right - gdi.scaleLength(30), 0,
itow(IDI_MEOSEDIT), gdi.scaleLength(24), gdi.scaleLength(24), SportIdentCB).setExtra(runnerId);
}
int lh = gdi.getLineHeight(); int lh = gdi.getLineHeight();
int marg = gdi.scaleLength(20); int marg = gdi.scaleLength(20);
int tmarg = gdi.scaleLength(6); int tmarg = gdi.scaleLength(6);
@ -3215,6 +3344,8 @@ wstring TabSI::getTimeAfterString(const oRunner* runner) {
void TabSI::processPunchOnly(gdioutput& gdi, const SICard& csic) void TabSI::processPunchOnly(gdioutput& gdi, const SICard& csic)
{ {
gdi.makeEvent("PunchCard", "sireadout", csic.CardNumber, 0, true);
SICard sic = csic; SICard sic = csic;
DWORD loaded; DWORD loaded;
gEvent->convertTimes(nullptr, sic); gEvent->convertTimes(nullptr, sic);
@ -3707,19 +3838,10 @@ void TabSI::tieCard(gdioutput& gdi) {
} }
void TabSI::showAssignCard(gdioutput& gdi, bool showHelp) { void TabSI::showAssignCard(gdioutput& gdi, bool showHelp) {
//gdi.fillDown();
//gdi.setRestorePoint("AssignCardBase");
if (interactiveReadout) { if (interactiveReadout) {
if (showHelp) { if (showHelp) {
checkBoxToolBar(gdi, { CheckBox::Interactive, CheckBox::AutoTie, CheckBox::AutoTieRent }); checkBoxToolBar(gdi, { CheckBox::Interactive, CheckBox::AutoTie, CheckBox::AutoTieRent });
gdi.addString("", 10, L"Avmarkera 'X' för att hantera alla bricktildelningar samtidigt.#" + lang.tl("Interaktiv inläsning")); gdi.addString("", 10, L"Avmarkera 'X' för att hantera alla bricktildelningar samtidigt.#" + lang.tl("Interaktiv inläsning"));
/*gdi.dropLine(0.5);
gdi.addCheckbox("AutoTie", "Knyt automatiskt efter inläsning", SportIdentCB, oe->getPropertyInt("AutoTie", 1) != 0);
gdi.addCheckbox("AutoTieRent", "Automatisk hyrbrickshantering genom registrerade hyrbrickor", SportIdentCB, oe->getPropertyInt("AutoTieRent", 1) != 0);
if (!oe->hasHiredCardData()) {
gdi.disableInput("AutoTieRent");
gdi.check("AutoTieRent", false);
}*/
gdi.dropLine(0.5); gdi.dropLine(0.5);
gdi.setRestorePoint("ManualTie"); gdi.setRestorePoint("ManualTie");
} }
@ -3730,8 +3852,7 @@ void TabSI::showAssignCard(gdioutput& gdi, bool showHelp) {
gdi.addString("", 10, L"Markera 'X' för att hantera deltagarna en och en.#" + lang.tl("Interaktiv inläsning")); gdi.addString("", 10, L"Markera 'X' för att hantera deltagarna en och en.#" + lang.tl("Interaktiv inläsning"));
} }
gEvent->assignCardInteractive(gdi, SportIdentCB); gEvent->assignCardInteractive(gdi, SportIdentCB, sortAssignCards);
gdi.refresh();
return; return;
} }
@ -4291,6 +4412,8 @@ void TabSI::clearCompetitionData() {
logger.reset(); logger.reset();
numSavedCardsOnCmpOpen = savedCards.size(); numSavedCardsOnCmpOpen = savedCards.size();
sortAssignCards = SortOrder::Custom;
} }
SICard& TabSI::getCard(int id) const { SICard& TabSI::getCard(int id) const {
@ -5088,7 +5211,7 @@ void TabSI::showReadoutStatus(gdioutput& gdi, const oRunner* r,
if (rentalCard) { if (rentalCard) {
gdi.addRectangle(rc, colorYellow); gdi.addRectangle(rc, colorYellow);
gdi.addStringUT(rc.top + (rc.bottom - rc.top) / 3, mrg, boldHuge | textCenter, gdi.addString("", rc.top + (rc.bottom - rc.top) / 3, mrg, boldHuge | textCenter,
"Vänligen återlämna hyrbrickan.", w - 3 * mrg); "Vänligen återlämna hyrbrickan.", w - 3 * mrg);
} }
@ -5168,3 +5291,26 @@ void TabSI::fillMappings(gdioutput& gdi) const {
gdi.addItem("Mappings", itow(mp.first) + L" \u21A6 " + oPunch::getType(mp.second), mp.first); gdi.addItem("Mappings", itow(mp.first) + L" \u21A6 " + oPunch::getType(mp.second), mp.first);
} }
} }
void TabSI::readTestData(gdioutput& gdi) {
testCardNumber = gdi.getTextNo("SI");
if (gdi.hasWidget("PunchTime")) {
testPunchTime = gdi.getText("PunchTime");
}
else {
useTestCheck = gdi.isChecked("HasCheck");
useTestStart = gdi.isChecked("HasStart");
useTestFinish = gdi.isChecked("HasFinish");
testStartTime = gdi.getText("Start");
testFinishTime = gdi.getText("Finish");
testCheckTime = gdi.getText("Check");
if (NC > testControls.size())
testControls.resize(NC);
for (int i = 0; i < NC; i++)
testControls[i] = gdi.getTextNo("C" + itos(i+1));
}
}

View File

@ -94,6 +94,8 @@ private:
shared_ptr<GuiHandler> resetHiredCardHandler; shared_ptr<GuiHandler> resetHiredCardHandler;
GuiHandler *getResetHiredCardHandler(); GuiHandler *getResetHiredCardHandler();
SortOrder sortAssignCards = SortOrder::Custom;
int runnerMatchedId; int runnerMatchedId;
bool printErrorShown; bool printErrorShown;
void printProtected(gdioutput &gdi, gdioutput &gdiprint); void printProtected(gdioutput &gdi, gdioutput &gdiprint);
@ -185,17 +187,32 @@ private:
static int analyzePunch(SIPunch &p, int &start, int &accTime, int &days); static int analyzePunch(SIPunch &p, int &start, int &accTime, int &days);
void createCompetitionFromCards(gdioutput &gdi); void createCompetitionFromCards(gdioutput &gdi);
int NC; int NC = 8;
int testType = 0;
bool showTestingPanel = false;
wstring testStartTime;
bool useTestStart = true;
wstring testFinishTime;
bool useTestFinish = true;
wstring testCheckTime;
bool useTestCheck = false;
int testRadioNumber = 50;
wstring testPunchTime;
vector<int> testControls;
int testCardNumber = 0;
void readTestData(gdioutput& gdi);
class EditCardData : public GuiHandler { class EditCardData : public GuiHandler {
TabSI *tabSI; TabSI *tabSI;
EditCardData(const EditCardData&);
EditCardData &operator=(const EditCardData&);
public: public:
EditCardData() : tabSI(0) {} EditCardData() : tabSI(0) {}
EditCardData(const EditCardData&) = delete;
EditCardData& operator=(const EditCardData&) = delete;
void handle(gdioutput &gdi, BaseInfo &info, GuiEventType type); void handle(gdioutput &gdi, BaseInfo &info, GuiEventType type);
friend class TabSI; friend class TabSI;
}; };
@ -252,6 +269,7 @@ private:
vector<int> MP; vector<int> MP;
GDICOLOR color; GDICOLOR color;
bool rentCard = false; bool rentCard = false;
int runnerId = 0;
RECT computeRC(gdioutput &gdi) const; RECT computeRC(gdioutput &gdi) const;
void render(gdioutput &gdi, const RECT &rc) const; void render(gdioutput &gdi, const RECT &rc) const;

View File

@ -241,11 +241,11 @@ int TabSpeaker::processButton(gdioutput &gdi, const ButtonInfo &bu)
gdi.dropLine(3); gdi.dropLine(3);
gdi.popX(); gdi.popX();
gdi.registerEvent("DataUpdate", tabSpeakerCB); gdi.registerEvent("DataUpdate", tabSpeakerCB);
vector<pair<int, bool>> runnersToReport; deque<pair<int, bool>> runnersToReport;
if (runnerId > 0) { if (runnerId > 0) {
runnersToReport.emplace_back(runnerId, false); runnersToReport.emplace_back(runnerId, false);
} }
TabRunner::generateRunnerReport(*oe, gdi, runnersToReport); TabRunner::generateRunnerReport(*oe, gdi, 1, 1, false, runnersToReport);
gdi.refresh(); gdi.refresh();
} }
else if (bu.id == "Priority") { else if (bu.id == "Priority") {

View File

@ -274,7 +274,7 @@ void Table::filter(int col, const wstring &filt, bool forceFilter)
wchar_t filt_lc[1024]; wchar_t filt_lc[1024];
wcscpy_s(filt_lc, filt.c_str()); wcscpy_s(filt_lc, filt.c_str());
CharLowerBuff(filt_lc, filt.length()); prepareMatchString(filt_lc, filt.length());
sortIndex.resize(2); sortIndex.resize(2);
for (size_t k=2;k<baseIndex.size();k++) { for (size_t k=2;k<baseIndex.size();k++) {

BIN
code/edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

View File

@ -256,7 +256,7 @@ Checkenhet = Check unit
Choose result module = Choose result module Choose result module = Choose result module
ClassAvailableMaps = Available maps for class ClassAvailableMaps = Available maps for class
ClassCourseResult = Class, course, result ClassCourseResult = Class, course, result
ClassDefaultResult = Class, Default result ClassDefaultResult = Class, default result
ClassFinishTime = Class, finish time ClassFinishTime = Class, finish time
ClassKnockoutTotalResult = Class, knock-out total result ClassKnockoutTotalResult = Class, knock-out total result
ClassLength = Course length for class ClassLength = Course length for class
@ -2674,3 +2674,8 @@ Bevara höjd/bredd-relationen = Preserve aspect ratio
RunnerLegTeamLeaderName = First competitor in team to finish leg RunnerLegTeamLeaderName = First competitor in team to finish leg
info:offsetclassid = If you import entries and classes from different sources to the same competition, it might happen that there are clashes in the Id numbers of the classes. To separate the classes, you may enter an offset for the Class Id when working with files from a particular source; this will be added to the Id numbers.\n\nYou must specify the same offset each time you import from that source.\n\n A suitble number could be1000 (which will work as long as every Id is less that 1000). info:offsetclassid = If you import entries and classes from different sources to the same competition, it might happen that there are clashes in the Id numbers of the classes. To separate the classes, you may enter an offset for the Class Id when working with files from a particular source; this will be added to the Id numbers.\n\nYou must specify the same offset each time you import from that source.\n\n A suitble number could be1000 (which will work as long as every Id is less that 1000).
Förskjutning av klassers Id = Offset Class ID Förskjutning av klassers Id = Offset Class ID
Tilldela nummerlapp till vakanter = Assign bibs to vacancies
Avläsning = Readout
Inmatning Testning = Input Testing
Testning = Testing
CourseNumControls = Number of controls

View File

@ -214,7 +214,9 @@ Bevakar händelser i X = Moniteur d'évènement X
Bevakningsprioritering = Sélectionner le coureur à surveiller Bevakningsprioritering = Sélectionner le coureur à surveiller
Bevara höjd/bredd-relationen = Conserver les proportions Bevara höjd/bredd-relationen = Conserver les proportions
Bibs = Dossards Bibs = Dossards
Bild = Image
Bild under text = Mettre l'image sous le texte Bild under text = Mettre l'image sous le texte
Bilder = Images
Block = Bloc Block = Bloc
Blockbredd = Largeur de bloc Blockbredd = Largeur de bloc
Bläddra = Parcourir Bläddra = Parcourir
@ -265,7 +267,7 @@ ClassKnockoutTotalResult = Class, knock-out total result
ClassLength = Longueur du circuit pour la catégorie ClassLength = Longueur du circuit pour la catégorie
ClassLiveResult = Live results (temps radios), par catégorie ClassLiveResult = Live results (temps radios), par catégorie
ClassName = Catégorie ClassName = Catégorie
ClassNumEntries = Nombre d'inscrits ClassNumEntries = Nombre d'inscrits dans la catégorie
ClassPoints = Catégorie, points ClassPoints = Catégorie, points
ClassResult = Catégorie, résultat ClassResult = Catégorie, résultat
ClassResultFraction = Pourcentage de la catégorie ayant terminé ClassResultFraction = Pourcentage de la catégorie ayant terminé
@ -422,6 +424,7 @@ Elektronisk godkänd = Accepté électroniquement
Elit = Élite Elit = Élite
Elitavgift = Frais élites Elitavgift = Frais élites
Elitklasser = Catégorie élite Elitklasser = Catégorie élite
Empty Results Split Print = Temps intermédiaires sans résultats
En bana med slingor tillåter deltagaren att ta slingorna i valfri ordning = Un circuit avec des boucles autorise le coureur à effectuer les boucles dans n'importe quel ordre. En bana med slingor tillåter deltagaren att ta slingorna i valfri ordning = Un circuit avec des boucles autorise le coureur à effectuer les boucles dans n'importe quel ordre.
En gafflad sträcka = Circuit avec variations En gafflad sträcka = Circuit avec variations
En klass kan inte slås ihop med sig själv = Vous ne pouvez pas fusionner une catégorie avec elle-même En klass kan inte slås ihop med sig själv = Vous ne pouvez pas fusionner une catégorie avec elle-même
@ -621,6 +624,7 @@ Förhindra att laget deltar i någon omstart = Permet à l'équipe de ne pas pre
Förhindra omstart = Refuser le départ des attardés Förhindra omstart = Refuser le départ des attardés
Förhöjd avgift = Frais supplémentaires Förhöjd avgift = Frais supplémentaires
Förskjutning = Déplacement Förskjutning = Déplacement
Förskjutning av klassers Id = Décalage de l'identifiant de catégorie
Först = En premier Först = En premier
Först-i-mål, gemensam = Du premier au dernier, commun Först-i-mål, gemensam = Du premier au dernier, commun
Först-i-mål, klassvis = Du premier au dernier, par catégorie Först-i-mål, klassvis = Du premier au dernier, par catégorie
@ -685,6 +689,7 @@ Hittar inte hjälpfilen, X = Documentation introuvable, X
Hittar inte klass X = Impossible de trouver la catégorie X Hittar inte klass X = Impossible de trouver la catégorie X
Hjälp = Aide Hjälp = Aide
Hoppar över stafettklass: X = Sauter la catégorie de relais: X Hoppar över stafettklass: X = Sauter la catégorie de relais: X
Horisontell = Horizontal
Horizontell = Horizontalement Horizontell = Horizontalement
Huvudlista = Liste principale Huvudlista = Liste principale
Hyravgift = Tarif de location Hyravgift = Tarif de location
@ -763,6 +768,8 @@ Inconsistent qualification rule, X = Règle de qualification incohérente, X
Index = Index Index = Index
Index in X[index] = Index en X[index] Index in X[index] = Index en X[index]
Individual Example = Exemple de course individuelle Individual Example = Exemple de course individuelle
Individual Results Split Print = Avec résultats individuels
Individual Rogaining Split Print = Avec résultats de la course au score
Individual result by finish time = Résultat individuel sur l'heure d'arrivée Individual result by finish time = Résultat individuel sur l'heure d'arrivée
Individual results in a club = Résultats individuel au sein du club Individual results in a club = Résultats individuel au sein du club
Individuell = par catégorie Individuell = par catégorie
@ -1009,6 +1016,7 @@ Liveresultat, radiotider = Résultats en direct avec radios
Ljud = Son Ljud = Son
Ljudfiler, baskatalog = Répertoire de base des fichiers sonores Ljudfiler, baskatalog = Répertoire de base des fichiers sonores
Ljudval = Sélection des sons Ljudval = Sélection des sons
Lokal kopia från X = Copie locale de X
Lokalt = Localement Lokalt = Localement
Long = Longue Long = Longue
Longitud = Longitude Longitud = Longitude
@ -1402,6 +1410,8 @@ Relation till föregående = Lien avec le précédent
Relativ skalfaktor för typsnittets storlek i procent = Facteur d'échelle pour la police (pourcentage) Relativ skalfaktor för typsnittets storlek i procent = Facteur d'échelle pour la police (pourcentage)
Relay Example = Exemple de relais Relay Example = Exemple de relais
Relay Example (Lokal kopia från: localhost) = Exemple de relais (Copie locale depuis : localhost) Relay Example (Lokal kopia från: localhost) = Exemple de relais (Copie locale depuis : localhost)
Relay Results Split Print = Avec résultats du relais
Relay/Team Results Split Print = Avec résultats du relais/de l'équipe
Relays = Relais Relays = Relais
Rep = Arrêt attardés Rep = Arrêt attardés
Reparera vald tävling = Réparer la compétition sélectionnée Reparera vald tävling = Réparer la compétition sélectionnée
@ -1526,6 +1536,7 @@ RunnerId = Licence/ID
RunnerLeg = Concurrent (variation spécifique) RunnerLeg = Concurrent (variation spécifique)
RunnerLegNumber = Numéro de relais groupé du coureur RunnerLegNumber = Numéro de relais groupé du coureur
RunnerLegNumberAlpha = Numéro de relais exact du coureur RunnerLegNumberAlpha = Numéro de relais exact du coureur
RunnerLegTeamLeaderName = Premier membre de l'équipe à terminer son circuit
RunnerName = Nom du coureur RunnerName = Nom du coureur
RunnerNationality = Nationalité du coureur RunnerNationality = Nationalité du coureur
RunnerPaid = Payé RunnerPaid = Payé
@ -1888,6 +1899,7 @@ Tabelläge = Mode table
Tar bort X = Retrait de X Tar bort X = Retrait de X
Team = Équipe Team = Équipe
Team Rogaining = Course au score en équipe Team Rogaining = Course au score en équipe
Team Rogaining Split Print = Avec résultats de la course au score en équipe
TeamBib = Dossard de l'équipe TeamBib = Dossard de l'équipe
TeamClub = Club de l'équipe TeamClub = Club de l'équipe
TeamCourseName = Nom du circuit pour l'équipe/la branche TeamCourseName = Nom du circuit pour l'équipe/la branche
@ -2217,6 +2229,7 @@ Välj alla klasser = Sélectionner toutes les catégories
Välj allt = Sélectionner tout Välj allt = Sélectionner tout
Välj automatiskt = Sélection automatique Välj automatiskt = Sélection automatique
Välj bild = Choisir une image Välj bild = Choisir une image
Välj bland befintliga bilder = Sélectionner une image existante
Välj deltagare för förhandsgranskning = Sélectionner un coureur pour prévisualisation Välj deltagare för förhandsgranskning = Sélectionner un coureur pour prévisualisation
Välj den etapp som föregår denna tävling = Sélectionner l'étape précédente Välj den etapp som föregår denna tävling = Sélectionner l'étape précédente
Välj den etapp som kommer efter denna tävling = Sélectionner l'étape suivante Välj den etapp som kommer efter denna tävling = Sélectionner l'étape suivante
@ -2428,11 +2441,12 @@ info:mapcontrol = MeOS ne sait pas déterminer la fonction d'un boîtier s'il n'
info:multieventnetwork = Pour prendre en charge plus d'une étape vous devez travailler localement. Faite une copie de sauvegarde de la compétition, ouvrez-la en local et transférez les résultats à l'étape suivante. Enfin, remontez l'étape suivante sur le serveur. info:multieventnetwork = Pour prendre en charge plus d'une étape vous devez travailler localement. Faite une copie de sauvegarde de la compétition, ouvrez-la en local et transférez les résultats à l'étape suivante. Enfin, remontez l'étape suivante sur le serveur.
info:multiple_start = Un coureur peut faire plusieurs fois le même circuit. Une nouvelle inscription est créée à chaque lecture de la puce info:multiple_start = Un coureur peut faire plusieurs fois le même circuit. Une nouvelle inscription est créée à chaque lecture de la puce
info:nosplitprint = Chargement de la liste personnalisée impossible\n\nCelle par défaut la remplace info:nosplitprint = Chargement de la liste personnalisée impossible\n\nCelle par défaut la remplace
info:offsetclassid = Si vous importez des inscrits et des catégories depuis des sources différentes dans la même compétition, il peut y avoir des conflits entre les numéros d'identifiant des catégories. Pour différencier les catégories, vous pouvez entrer un décalage de la valeur de l'identifiant des catégorires pour chacune des sources. Celui-ci sera ajouté à l'identifiant existant.\n\nVous pouvez par exemple entrer 1000 (si aucune catégorie n'a déjà un identifiant supérieur à 1000).
info:pageswithcolumns = Montrer la liste page par page, avec un nombre spécifié de colonnes. Les infos sont mises à jour à chaque bouclage. info:pageswithcolumns = Montrer la liste page par page, avec un nombre spécifié de colonnes. Les infos sont mises à jour à chaque bouclage.
info:readout_action = X: Puce no. Y lue.\nDes actions manuelles sont requises. info:readout_action = X: Puce no. Y lue.\nDes actions manuelles sont requises.
info:readout_queue = X: Puce no. Y lue.\nLa puce a été mise en file d'attente. info:readout_queue = X: Puce no. Y lue.\nLa puce a été mise en file d'attente.
info:readoutbase = Activez le boîtier SI en sélectionnant son port COM ou en recherchant les boîtiers SI installés. Appuyez sur Information pour obtenir le statut du port sélectionné.\n\nLecture Interactive vous permet de gérer directement les problèmes tels qu'un numéro de puce erroné. N'utilisez pas cette possibilité quand les coureurs ayant des problèmes sont pris en charge séparément.\n\nLa base de données des coureurs est utilisée si vous voulez ajouter automatiquement de nouveaux coureurs. Les poinçons sont utilisés pour trouver (détecter) la bonne catégorie. info:readoutbase = Activez le boîtier SI en sélectionnant son port COM ou en recherchant les boîtiers SI installés. Appuyez sur Information pour obtenir le statut du port sélectionné.\n\nLecture Interactive vous permet de gérer directement les problèmes tels qu'un numéro de puce erroné. N'utilisez pas cette possibilité quand les coureurs ayant des problèmes sont pris en charge séparément.\n\nLa base de données des coureurs est utilisée si vous voulez ajouter automatiquement de nouveaux coureurs. Les poinçons sont utilisés pour trouver (détecter) la bonne catégorie.
info:readoutmore = Verrouillez la fonction pour éviter toute modification accidentelle.\n\nSélection des sons vous permet de lancer différents sons lors de la lecture des puces pour être plus facilement informé des problèmes.\n\n"Fenêtre de prévisualisation coureur" affiche une nouvelle fenêtre destinée au coureur qui lit sa puce, regroupant les informations principales de sa course.\n\nPlusieurs courses pour un coureur est utile si un coureur est autorisé a faire plusieurs fois son circuit. Une nouvelle inscriprion est créée à chaque lecture de la puce. info:readoutmore = Verrouillez la fonction pour éviter toute modification accidentelle.\n\nSélection des sons vous permet de lancer différents sons lors de la lecture des puces pour être plus facilement informé des problèmes.\n\n"Fenêtre de prévisualisation coureur" affiche une nouvelle fenêtre destinée au coureur qui lit sa puce, regroupant les informations principales de sa course.\n\nPlusieurs courses pour un coureur est utile si un coureur est autorisé a faire plusieurs fois son circuit. Une nouvelle inscription est créée à chaque lecture de la puce.
info:readoutwindow = Cette fenêtre affiche les informations principales de chaque lecture de puce info:readoutwindow = Cette fenêtre affiche les informations principales de chaque lecture de puce
info:runnerdbonline = Comme vous êtes connecté à un serveur, il n'est pas possible d'éditer les bases de données club et coureurs manuellement. Effectuez les changements avant d'uploader la compétition sur un serveur. Il est également possible de remplacer la base de données existante sur le serveur en important une nouvelle base (à partir de IOF XML). info:runnerdbonline = Comme vous êtes connecté à un serveur, il n'est pas possible d'éditer les bases de données club et coureurs manuellement. Effectuez les changements avant d'uploader la compétition sur un serveur. Il est également possible de remplacer la base de données existante sur le serveur en important une nouvelle base (à partir de IOF XML).
info:teamcourseassignment = Le fichier des circuits IOF XML 3.0 contient les variations des équipes. Pour importer ces données, vous devez préparer la compétition en fonction des données du fichier :\n\n1.Assurez-vous que les catégories de relais ont le bon nombre de branches.\n2. Affectez les dossards à chaque catégorie. Passez par la Configuration Rapide dans l'onglet Catégories et renseignez le premier dossard de chaque catégorie (avec la répartition automatique des dossards). Vous pouvez aussi importer les équipes et leur attribuer des dossards comme d'habitude.\n\n\n3. Importez les circuits. Vous pouvez importer plusieurs fois ce fichier si nécessaire pour mettre à jour les variations. info:teamcourseassignment = Le fichier des circuits IOF XML 3.0 contient les variations des équipes. Pour importer ces données, vous devez préparer la compétition en fonction des données du fichier :\n\n1.Assurez-vous que les catégories de relais ont le bon nombre de branches.\n2. Affectez les dossards à chaque catégorie. Passez par la Configuration Rapide dans l'onglet Catégories et renseignez le premier dossard de chaque catégorie (avec la répartition automatique des dossards). Vous pouvez aussi importer les équipes et leur attribuer des dossards comme d'habitude.\n\n\n3. Importez les circuits. Vous pouvez importer plusieurs fois ce fichier si nécessaire pour mettre à jour les variations.

View File

@ -888,6 +888,7 @@ TextInfo& gdioutput::addImage(const string& id, int yp, int xp, int format,
imageReferences.push_back(&TI); imageReferences.push_back(&TI);
TI.id = id;
TI.format = format | textImage; TI.format = format | textImage;
TI.xp = xp; TI.xp = xp;
TI.yp = yp; TI.yp = yp;
@ -1256,7 +1257,7 @@ ButtonInfo &gdioutput::addButton(int x, int y, int w, const string &id,
updatePos(x, y, w+scaleLength(GDI_BUTTON_SPACING), height+5); updatePos(x, y, w+scaleLength(GDI_BUTTON_SPACING), height+5);
bi.xp=x; bi.xp=x;
bi.yp=y; bi.yp = y - 1;
bi.width = w; bi.width = w;
bi.text=ttext; bi.text=ttext;
bi.id=id; bi.id=id;
@ -1321,23 +1322,23 @@ ButtonInfo &gdioutput::addCheckbox(int x, int y, const string &id, const string
return addCheckbox(x,y,id, widen(text), cb, Checked, widen(tooltip), AbsPos); return addCheckbox(x,y,id, widen(text), cb, Checked, widen(tooltip), AbsPos);
} }
ButtonInfo &gdioutput::addCheckbox(int x, int y, const string &id, const wstring &text, ButtonInfo& gdioutput::addCheckbox(int x, int y, const string& id, const wstring& text,
GUICALLBACK cb, bool Checked, const wstring &tooltip, bool AbsPos) GUICALLBACK cb, bool Checked, const wstring& tooltip, bool AbsPos)
{ {
ButtonInfo bi; ButtonInfo bi;
SIZE size; SIZE size;
wstring ttext = lang.tl(text); wstring ttext = lang.tl(text);
HDC hDC=GetDC(hWndTarget); HDC hDC = GetDC(hWndTarget);
SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT)); SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
GetTextExtentPoint32(hDC, L"M", 1, &size); GetTextExtentPoint32(hDC, L"M", 1, &size);
int ox=OffsetX; int ox = OffsetX;
int oy=OffsetY; int oy = OffsetY;
if (AbsPos) { if (AbsPos) {
ox=0; ox = 0;
oy=0; oy = 0;
} }
int h = size.cy; int h = size.cy;
@ -1345,15 +1346,16 @@ ButtonInfo &gdioutput::addCheckbox(int x, int y, const string &id, const wstring
GetTextExtentPoint32(hDC, ttext.c_str(), ttext.length(), &size); GetTextExtentPoint32(hDC, ttext.c_str(), ttext.length(), &size);
ReleaseDC(hWndTarget, hDC); ReleaseDC(hWndTarget, hDC);
bi.hWnd=CreateWindowEx(0,L"BUTTON", L"", WS_TABSTOP|WS_VISIBLE| int cbY = y + (size.cy - h) / 2;
WS_CHILD | WS_CLIPSIBLINGS |BS_AUTOCHECKBOX|BS_NOTIFY, bi.hWnd = CreateWindowEx(0, L"BUTTON", L"", WS_TABSTOP | WS_VISIBLE |
x-ox, y-oy + (size.cy-h)/2, h, h, hWndTarget, NULL, WS_CHILD | WS_CLIPSIBLINGS | BS_AUTOCHECKBOX | BS_NOTIFY,
x - ox, cbY - oy, h, h, hWndTarget, NULL,
(HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE), NULL); (HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE), NULL);
TextInfo &desc = addStringUT(y , x + (3*h)/2, 0, ttext, 0, checkBoxCallback); TextInfo& desc = addStringUT(y, x + (3 * h) / 2, 0, ttext, 0, checkBoxCallback);
desc.id = "T" + id; desc.id = "T" + id;
SendMessage(bi.hWnd, WM_SETFONT, (WPARAM) getGUIFont(), 0); SendMessage(bi.hWnd, WM_SETFONT, (WPARAM)getGUIFont(), 0);
if (Checked) if (Checked)
SendMessage(bi.hWnd, BM_SETCHECK, BST_CHECKED, 0); SendMessage(bi.hWnd, BM_SETCHECK, BST_CHECKED, 0);
@ -1366,18 +1368,18 @@ ButtonInfo &gdioutput::addCheckbox(int x, int y, const string &id, const wstring
else else
updatePos(x, y, size.cx + int(30 * scale), desc.textRect.bottom - desc.textRect.top + scaleLength(4)); updatePos(x, y, size.cx + int(30 * scale), desc.textRect.bottom - desc.textRect.top + scaleLength(4));
} }
if (tooltip.length()>0) { if (tooltip.length() > 0) {
addToolTip(id, tooltip, bi.hWnd); addToolTip(id, tooltip, bi.hWnd);
addToolTip(desc.id, tooltip, 0, &desc.textRect); addToolTip(desc.id, tooltip, 0, &desc.textRect);
} }
bi.isCheckbox = true; bi.isCheckbox = true;
bi.xp=x; bi.xp = x;
bi.yp=y; bi.yp = cbY;
bi.width = desc.textRect.right - (x-ox); bi.width = desc.textRect.right - (x - ox);
bi.text=ttext; bi.text = ttext;
bi.id=id; bi.id = id;
bi.callBack=cb; bi.callBack = cb;
bi.AbsPos=AbsPos; bi.AbsPos = AbsPos;
bi.originalState = Checked; bi.originalState = Checked;
bi.isEdit(true); bi.isEdit(true);
BI.push_back(bi); BI.push_back(bi);
@ -2240,7 +2242,7 @@ void gdioutput::processButtonMessage(ButtonInfo &bi, WPARAM wParam)
if (bi.isCheckbox) if (bi.isCheckbox)
bi.checked = SendMessage(bi.hWnd, BM_GETCHECK, 0, 0)==BST_CHECKED; bi.checked = SendMessage(bi.hWnd, BM_GETCHECK, 0, 0)==BST_CHECKED;
bi.synchData(); bi.synchData();
if (bi.callBack || bi.handler) { if (bi.callBack || bi.hasEventHandler()) {
setWaitCursor(true); setWaitCursor(true);
if (!bi.handleEvent(*this, GUI_BUTTON) && bi.callBack) if (!bi.handleEvent(*this, GUI_BUTTON) && bi.callBack)
bi.callBack(this, GUI_BUTTON, &bi); //it may be destroyed here... bi.callBack(this, GUI_BUTTON, &bi); //it may be destroyed here...
@ -3027,10 +3029,8 @@ void gdioutput::doEnter() {
HWND hWnd=GetFocus(); HWND hWnd=GetFocus();
for (list<ButtonInfo>::iterator it=BI.begin(); it!=BI.end(); ++it) for (list<ButtonInfo>::iterator it=BI.begin(); it!=BI.end(); ++it)
if (it->isDefaultButton() && (it->callBack || it->handler)) { if (it->isDefaultButton()) {
if (it->handler) if (!it->handleEvent(*this, GUI_BUTTON) && it->callBack)
it->handleEvent(*this, GUI_BUTTON);
else
it->callBack(this, GUI_BUTTON, &*it); it->callBack(this, GUI_BUTTON, &*it);
return; return;
} }
@ -3038,13 +3038,11 @@ void gdioutput::doEnter() {
list<InputInfo>::iterator it; list<InputInfo>::iterator it;
for(it=II.begin(); it != II.end(); ++it) for(it=II.begin(); it != II.end(); ++it)
if (it->hWnd==hWnd && (it->callBack || it->handler)){ if (it->hWnd==hWnd && (it->hasEventHandler() || it->callBack)){
TCHAR bf[1024]; TCHAR bf[1024];
GetWindowText(hWnd, bf, 1024); GetWindowText(hWnd, bf, 1024);
it->text=bf; it->text = bf;
if (it->handler) if (!it->handleEvent(*this, GUI_INPUT))
it->handleEvent(*this, GUI_INPUT);
else
it->callBack(this, GUI_INPUT, &*it); it->callBack(this, GUI_INPUT, &*it);
return; return;
} }
@ -3139,10 +3137,8 @@ void gdioutput::doEscape()
tit->table->escape(*this); tit->table->escape(*this);
for (list<ButtonInfo>::iterator it=BI.begin(); it!=BI.end(); ++it) { for (list<ButtonInfo>::iterator it=BI.begin(); it!=BI.end(); ++it) {
if (it->isCancelButton() && (it->callBack || it->handler) ) { if (it->isCancelButton() && (it->callBack || it->hasEventHandler()) ) {
if (it->handler) if (!it->handleEvent(*this, GUI_BUTTON))
it->handleEvent(*this, GUI_BUTTON);
else
it->callBack(this, GUI_BUTTON, &*it); it->callBack(this, GUI_BUTTON, &*it);
return; return;
} }
@ -3517,13 +3513,13 @@ BaseInfo *gdioutput::setText(const char *id, const wstring &text, bool Update, i
bool gdioutput::insertText(const string &id, const wstring &text) bool gdioutput::insertText(const string &id, const wstring &text)
{ {
for (list<InputInfo>::iterator it=II.begin(); for (list<InputInfo>::iterator it = II.begin();
it != II.end(); ++it) { it != II.end(); ++it) {
if (it->id==id) { if (it->id == id) {
SetWindowText(it->hWnd, text.c_str()); SetWindowText(it->hWnd, text.c_str());
it->text = text; it->text = text;
if (it->handler) if (it->hasEventHandler())
it->handleEvent(*this, GUI_INPUT); it->handleEvent(*this, GUI_INPUT);
else if (it->callBack) else if (it->callBack)
it->callBack(this, GUI_INPUT, &*it); it->callBack(this, GUI_INPUT, &*it);
@ -4385,9 +4381,6 @@ void gdioutput::RenderString(TextInfo &ti, HDC hDC) {
h = ti.textRect.bottom - ti.textRect.top; h = ti.textRect.bottom - ti.textRect.top;
image.drawImage(imgId, Image::ImageMethod::Default, hDC, rc.left, rc.top, w, h); image.drawImage(imgId, Image::ImageMethod::Default, hDC, rc.left, rc.top, w, h);
//width = image.getWidth(imgId);
//height = image.getHeight(imgId);
} }
} }
if (!fixedRect) { if (!fixedRect) {
@ -5503,7 +5496,7 @@ int gdioutput::sendCtrlMessage(const string &id)
{ {
for (list<ButtonInfo>::iterator it=BI.begin(); it != BI.end(); ++it) { for (list<ButtonInfo>::iterator it=BI.begin(); it != BI.end(); ++it) {
if (id==it->id) { if (id==it->id) {
if (it->handler) if (it->hasEventHandler())
return it->handleEvent(*this, GUI_BUTTON); return it->handleEvent(*this, GUI_BUTTON);
else if (it->callBack) else if (it->callBack)
return it->callBack(this, GUI_BUTTON, &*it); //it may be destroyed here... return it->callBack(this, GUI_BUTTON, &*it); //it may be destroyed here...
@ -7201,11 +7194,11 @@ string gdioutput::dbPress(const string &id, int extra) {
if (it->isCheckbox) { if (it->isCheckbox) {
check(id, !isChecked(id)); check(id, !isChecked(id));
} }
else if(!it->callBack && !it->handler) else if(!it->callBack && !it->hasEventHandler())
throw meosException("Button " + id + " is not active."); throw meosException("Button " + id + " is not active.");
wstring val = it->text; wstring val = it->text;
if (it->handler) if (it->hasEventHandler())
it->handleEvent(*this, GUI_BUTTON); it->handleEvent(*this, GUI_BUTTON);
else if (it->callBack) else if (it->callBack)
it->callBack(this, GUI_BUTTON, &*it); //it may be destroyed here... it->callBack(this, GUI_BUTTON, &*it); //it may be destroyed here...
@ -7229,11 +7222,11 @@ string gdioutput::dbPress(const string &id, const char *extra) {
if (it->isCheckbox) { if (it->isCheckbox) {
check(id, !isChecked(id)); check(id, !isChecked(id));
} }
else if(!it->callBack && !it->handler) else if(!it->callBack && !it->hasEventHandler())
throw meosException("Button " + id + " is not active."); throw meosException("Button " + id + " is not active.");
wstring val = it->text; wstring val = it->text;
if (it->handler) if (it->hasEventHandler())
it->handleEvent(*this, GUI_BUTTON); it->handleEvent(*this, GUI_BUTTON);
else if (it->callBack) else if (it->callBack)
it->callBack(this, GUI_BUTTON, &*it); //it may be destroyed here... it->callBack(this, GUI_BUTTON, &*it); //it may be destroyed here...
@ -7272,12 +7265,12 @@ string gdioutput::dbSelect(const string &id, int data) {
void gdioutput::internalSelect(ListBoxInfo &bi) { void gdioutput::internalSelect(ListBoxInfo &bi) {
bi.syncData(); bi.syncData();
if (bi.callBack || bi.handler) { if (bi.callBack || bi.handler || bi.managedHandler) {
setWaitCursor(true); setWaitCursor(true);
hasCleared = false; hasCleared = false;
try { try {
bi.writeLock = true; bi.writeLock = true;
if (bi.handler) if (bi.hasEventHandler())
bi.handleEvent(*this, GUI_LISTBOX); bi.handleEvent(*this, GUI_LISTBOX);
else else
bi.callBack(this, GUI_LISTBOX, &bi); //it may be destroyed here... Then hasCleared is set. bi.callBack(this, GUI_LISTBOX, &bi); //it may be destroyed here... Then hasCleared is set.
@ -7304,7 +7297,7 @@ void gdioutput::dbInput(const string &id, const string &text) {
SetWindowText(it->hWnd, widen(text).c_str()); SetWindowText(it->hWnd, widen(text).c_str());
it->text = widen(text); it->text = widen(text);
it->data = -1; it->data = -1;
if (it->handler) if (it->hasEventHandler())
it->handleEvent(*this, GUI_COMBO); it->handleEvent(*this, GUI_COMBO);
else if (it->callBack) else if (it->callBack)
it->callBack(this, GUI_COMBO, &*it); //it may be destroyed here... it->callBack(this, GUI_COMBO, &*it); //it may be destroyed here...
@ -7319,7 +7312,7 @@ void gdioutput::dbInput(const string &id, const string &text) {
it->text = widen(text); it->text = widen(text);
SetWindowText(it->hWnd, widen(text).c_str()); SetWindowText(it->hWnd, widen(text).c_str());
if (it->handler) if (it->hasEventHandler())
it->handleEvent(*this, GUI_INPUT); it->handleEvent(*this, GUI_INPUT);
else if (it->callBack) else if (it->callBack)
it->callBack(this, GUI_INPUT, &*it); it->callBack(this, GUI_INPUT, &*it);
@ -7357,7 +7350,7 @@ void gdioutput::dbDblClick(const string &id, int data) {
if (!IsWindowEnabled(it->hWnd)) if (!IsWindowEnabled(it->hWnd))
throw meosException("Selection " + id + " is not active."); throw meosException("Selection " + id + " is not active.");
selectItemByData(id, data); selectItemByData(id, data);
if (it->handler) if (it->hasEventHandler())
it->handleEvent(*this, GUI_LISTBOXSELECT); it->handleEvent(*this, GUI_LISTBOXSELECT);
else if (it->callBack) else if (it->callBack)
it->callBack(this, GUI_LISTBOXSELECT, &*it); //it may be destroyed here... it->callBack(this, GUI_LISTBOXSELECT, &*it); //it may be destroyed here...

View File

@ -205,7 +205,7 @@ class InfoCompetitor : public InfoBaseCompetitor {
class InfoTeam : public InfoBaseCompetitor { class InfoTeam : public InfoBaseCompetitor {
protected: protected:
// The outer level holds legs, the inner level holds (parallel/patrol) runners on each leg. // The outer level holds legs, the inner level holds (parallel/patrol) runners on each leg.
vector< vector<int> > competitors; vector<vector<int>> competitors;
public: public:
bool synchronize(oTeam &t); bool synchronize(oTeam &t);
void serialize(xmlbuffer &xml, bool diffOnly) const; void serialize(xmlbuffer &xml, bool diffOnly) const;

View File

@ -2417,7 +2417,10 @@ pRunner IOF30Interface::readPersonResult(gdioutput &gdi, pClass pc, xmlobject &x
wstring s; wstring s;
for (auto &split : splits) { for (auto &split : splits) {
int code = split.getObjectInt("ControlCode"); int code = split.getObjectInt("ControlCode");
int time = split.getObjectInt("Time"); wstring out;
split.getObjectString("Time", out);
double t = _wtof(out.c_str());
int time = int(t * timeConstSecond);
split.getObjectString("status", s); split.getObjectString("status", s);
if (s != L"missing") if (s != L"missing")
card->addPunch(code, st + time, 0, 0); card->addPunch(code, st + time, 0, 0);

View File

@ -463,6 +463,8 @@ int APIENTRY WinMain(HINSTANCE hInstance,
gdi_main->init(hWndWorkspace, hWndMain, hMainTab); gdi_main->init(hWndWorkspace, hWndMain, hMainTab);
gdi_main->getTabs().get(TCmpTab)->loadPage(*gdi_main); gdi_main->getTabs().get(TCmpTab)->loadPage(*gdi_main);
image.loadImage(IDI_MEOSEDIT, Image::ImageMethod::Default);
autoTask = new AutoTask(hWndMain, *gEvent, *gdi_main); autoTask = new AutoTask(hWndMain, *gEvent, *gdi_main);
autoTask->setTimers(); autoTask->setTimers();

View File

@ -53,6 +53,7 @@ IDB_ECO BITMAP "bmp00001.bmp"
IDI_SPLASHIMAGE PNG "meos.png" IDI_SPLASHIMAGE PNG "meos.png"
IDI_MEOSIMAGE PNG "title.png" IDI_MEOSIMAGE PNG "title.png"
IDI_MEOSINFO PNG "info24.png" IDI_MEOSINFO PNG "info24.png"
IDI_MEOSEDIT PNG "edit.png"
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// English (United States) resources // English (United States) resources

View File

@ -919,16 +919,10 @@ string itos(uint64_t i)
return bf; return bf;
} }
void prepareMatchString(wchar_t* data_c, int size) {
bool filterMatchString(const string &c, const char *filt_lc) CharLowerBuff(data_c, size);
{ for (int j = 0; j < size; j++)
if (filt_lc[0] == 0) data_c[j] = toLowerStripped(data_c[j]);
return true;
char key[2048];
strcpy_s(key, c.c_str());
CharLowerBuffA(key, c.length());
return strstr(key, filt_lc)!=0;
} }
bool filterMatchString(const wstring &c, const wchar_t *filt_lc, int &score) { bool filterMatchString(const wstring &c, const wchar_t *filt_lc, int &score) {
@ -937,7 +931,11 @@ bool filterMatchString(const wstring &c, const wchar_t *filt_lc, int &score) {
return true; return true;
wchar_t key[2048]; wchar_t key[2048];
wcscpy_s(key, c.c_str()); wcscpy_s(key, c.c_str());
CharLowerBuff(key, c.length()); int cl = c.length();
CharLowerBuff(key, cl);
for (int j = 0; j < cl; j++)
key[j] = toLowerStripped(key[j]);
bool match = wcsstr(key, filt_lc) != 0; bool match = wcsstr(key, filt_lc) != 0;
if (match) { if (match) {
while (filt_lc[score] && key[score] && filt_lc[score] == key[score]) while (filt_lc[score] && key[score] && filt_lc[score] == key[score])
@ -1259,14 +1257,14 @@ int toLowerStripped(wchar_t c) {
return c; return c;
static wchar_t *map = 0; static wchar_t *map = 0;
if (map == 0) { if (map == nullptr) {
map = new wchar_t[65536]; map = new wchar_t[65536];
for (int i = 0; i < 65536; i++) for (int i = 0; i < 65536; i++)
map[i] = i; map[i] = i;
setChar(map, L'Å', L'å'); setChar(map, L'Å', L'a');
setChar(map, L'Ä', L'ä'); setChar(map, L'Ä', L'a');
setChar(map, L'Ö', L'ö'); setChar(map, L'Ö', L'o');
setChar(map, L'É', L'e'); setChar(map, L'É', L'e');
setChar(map, L'é', L'e'); setChar(map, L'é', L'e');
@ -1289,6 +1287,8 @@ int toLowerStripped(wchar_t c) {
setChar(map, L'ñ', L'n'); setChar(map, L'ñ', L'n');
setChar(map, L'Ñ', L'n'); setChar(map, L'Ñ', L'n');
setChar(map, L'ä', L'a');
setChar(map, L'å', L'a');
setChar(map, L'á', L'a'); setChar(map, L'á', L'a');
setChar(map, L'Á', L'a'); setChar(map, L'Á', L'a');
setChar(map, L'à', L'a'); setChar(map, L'à', L'a');
@ -1315,19 +1315,29 @@ int toLowerStripped(wchar_t c) {
setChar(map, L'Õ', L'o'); setChar(map, L'Õ', L'o');
setChar(map, L'ô', L'o'); setChar(map, L'ô', L'o');
setChar(map, L'Ô', L'o'); setChar(map, L'Ô', L'o');
setChar(map, L'ö', L'o');
setChar(map, L'ý', L'y'); setChar(map, L'ý', L'y');
setChar(map, L'Ý', L'Y'); setChar(map, L'Ý', L'Y');
setChar(map, L'ÿ', L'y'); setChar(map, L'ÿ', L'y');
setChar(map, L'Æ', L'ä'); setChar(map, L'Æ', L'a');
setChar(map, L'æ', L'ä'); setChar(map, L'æ', L'a');
setChar(map, L'Ø', L'ö'); setChar(map, L'Ø', L'o');
setChar(map, L'ø', L'ö'); setChar(map, L'ø', L'o');
setChar(map, L'Ç', L'c'); setChar(map, L'Ç', L'c');
setChar(map, L'ç', L'c'); setChar(map, L'ç', L'c');
wstring srcEx = L"Ă㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻĽľĿŀŁł"
L"ŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽž";
wstring dstEx = L"aaaaccccccccddddeeeeeeeeeegggggggghhhhiiiiiiiiiijjjjkkklllllllll"
L"nnnnnnnnnooooooaarrrrrrssssssssttttttuuuuuuuuuuuuwwyyyzzzzzz";
assert(srcEx.size() == dstEx.size());
for (int j = 0; j < srcEx.size(); j++)
setChar(map, srcEx[j], dstEx[j]);
} }
int a = map[c]; int a = map[c];
return a; return a;

View File

@ -152,10 +152,13 @@ wstring itow(int64_t i);
wstring itow(uint64_t i); wstring itow(uint64_t i);
///Lower case match (filt_lc must be lc) ///Lower case match (filt_lc must be lc and stripped of accents)
bool filterMatchString(const string &c, const char *filt_lc);
bool filterMatchString(const wstring &c, const wchar_t *filt_lc, int &score); bool filterMatchString(const wstring &c, const wchar_t *filt_lc, int &score);
/** To lower case and strip accants */
void prepareMatchString(wchar_t* data_c, int size);
bool matchNumber(int number, const wchar_t *key); bool matchNumber(int number, const wchar_t *key);
int getMeosBuild(); int getMeosBuild();

View File

@ -25,12 +25,12 @@
//ABCDEFGHIJKLMNOP //ABCDEFGHIJKLMNOP
int getMeosBuild() { int getMeosBuild() {
string revision("$Rev: 1263 $"); string revision("$Rev: 1266 $");
return 174 + atoi(revision.substr(5, string::npos).c_str()); return 174 + atoi(revision.substr(5, string::npos).c_str());
} }
wstring getMeosDate() { wstring getMeosDate() {
wstring date(L"$Date: 2023-05-12 16:11:34 +0200 (fre, 12 maj 2023) $"); wstring date(L"$Date: 2023-06-01 22:17:24 +0200 (tor, 01 jun 2023) $");
return date.substr(7,10); return date.substr(7,10);
} }
@ -39,7 +39,7 @@ wstring getBuildType() {
} }
wstring getMajorVersion() { wstring getMajorVersion() {
return L"3.9"; return L"4.0";
} }
wstring getMeosFullVersion() { wstring getMeosFullVersion() {

View File

@ -2198,6 +2198,7 @@ void MetaList::initSymbols() {
typeToSymbol[lCourseUsage] = L"CourseUsage"; typeToSymbol[lCourseUsage] = L"CourseUsage";
typeToSymbol[lCourseUsageNoVacant] = L"CourseUsageNoVacant"; typeToSymbol[lCourseUsageNoVacant] = L"CourseUsageNoVacant";
typeToSymbol[lCourseClasses] = L"CourseClasses"; typeToSymbol[lCourseClasses] = L"CourseClasses";
typeToSymbol[lCourseNumControls] = L"CourseNumControls";
typeToSymbol[lCourseShortening] = L"CourseShortening"; typeToSymbol[lCourseShortening] = L"CourseShortening";
typeToSymbol[lRunnerName] = L"RunnerName"; typeToSymbol[lRunnerName] = L"RunnerName";
typeToSymbol[lRunnerGivenName] = L"RunnerGivenName"; typeToSymbol[lRunnerGivenName] = L"RunnerGivenName";
@ -2301,18 +2302,26 @@ void MetaList::initSymbols() {
typeToSymbol[lPunchNamedTime] = L"PunchNamedTime"; typeToSymbol[lPunchNamedTime] = L"PunchNamedTime";
typeToSymbol[lPunchName] = L"PunchName"; typeToSymbol[lPunchName] = L"PunchName";
typeToSymbol[lPunchNamedSplit] = L"PunchNamedSplit"; typeToSymbol[lPunchNamedSplit] = L"PunchNamedSplit";
typeToSymbol[lPunchTeamTotalNamedTime] = L"PunchTeamTotalNamedTime";
typeToSymbol[lPunchTime] = L"PunchTime"; typeToSymbol[lPunchTime] = L"PunchTime";
typeToSymbol[lPunchTeamTime] = L"PunchTeamTime";
typeToSymbol[lPunchControlNumber] = L"PunchControlNumber"; typeToSymbol[lPunchControlNumber] = L"PunchControlNumber";
typeToSymbol[lPunchControlCode] = L"PunchControlCode"; typeToSymbol[lPunchControlCode] = L"PunchControlCode";
typeToSymbol[lPunchLostTime] = L"PunchLostTime"; typeToSymbol[lPunchLostTime] = L"PunchLostTime";
typeToSymbol[lPunchControlPlace] = L"PunchControlPlace"; typeToSymbol[lPunchControlPlace] = L"PunchControlPlace";
typeToSymbol[lPunchControlPlaceAcc] = L"PunchControlPlaceAcc"; typeToSymbol[lPunchControlPlaceAcc] = L"PunchControlPlaceAcc";
typeToSymbol[lPunchControlPlaceTeamAcc] = L"PunchControlPlaceTeamAcc";
typeToSymbol[lPunchSplitTime] = L"PunchSplitTime"; typeToSymbol[lPunchSplitTime] = L"PunchSplitTime";
typeToSymbol[lPunchTotalTime] = L"PunchTotalTime"; typeToSymbol[lPunchTotalTime] = L"PunchTotalTime";
typeToSymbol[lPunchTeamTotalTime] = L"PunchTeamTotalTime";
typeToSymbol[lPunchAbsTime] = L"PunchAbsTime"; typeToSymbol[lPunchAbsTime] = L"PunchAbsTime";
typeToSymbol[lPunchTotalTimeAfter] = L"PunchTotalTimeAfter"; typeToSymbol[lPunchTotalTimeAfter] = L"PunchTotalTimeAfter";
typeToSymbol[lPunchTeamTotalTimeAfter] = L"PunchTeamTotalTimeAfter";
typeToSymbol[lPunchTimeSinceLast] = L"PunchTimeSinceLast"; typeToSymbol[lPunchTimeSinceLast] = L"PunchTimeSinceLast";
typeToSymbol[lRogainingPunch] = L"RogainingPunch"; typeToSymbol[lRogainingPunch] = L"RogainingPunch";
@ -3461,7 +3470,7 @@ void MetaList::getAutoComplete(const wstring& w, vector<AutoCompleteRecord>& rec
s_lc[j].resize(ws[j].size() + 1); s_lc[j].resize(ws[j].size() + 1);
ws[j] = trim(ws[j]); ws[j] = trim(ws[j]);
wcscpy_s(s_lc[j].data(), s_lc[j].size(), ws[j].c_str()); wcscpy_s(s_lc[j].data(), s_lc[j].size(), ws[j].c_str());
CharLowerBuff(s_lc[j].data(), ws[j].length()); prepareMatchString(s_lc[j].data(), ws[j].length());
} }
wstring tl; wstring tl;

View File

@ -434,6 +434,10 @@ public:
static void fillSymbols(vector < pair<wstring, size_t>> &symb); static void fillSymbols(vector < pair<wstring, size_t>> &symb);
static const map<SortOrder, string>& getOrderToSymbol() {
return orderToSymbol;
}
friend class MetaListPost; friend class MetaListPost;
}; };

View File

@ -815,7 +815,7 @@ wstring oCard::getCardVoltage() const {
} }
wstring oCard::getCardVoltage(int miliVolt) { wstring oCard::getCardVoltage(int miliVolt) {
if (miliVolt == 0) if (miliVolt <= 10)
return L""; return L"";
int vi = miliVolt / 1000; int vi = miliVolt / 1000;
int vd = (miliVolt % 1000) / 10; int vd = (miliVolt % 1000) / 10;
@ -830,9 +830,9 @@ oCard::BatteryStatus oCard::isCriticalCardVoltage() const {
} }
oCard::BatteryStatus oCard::isCriticalCardVoltage(int miliVolt) { oCard::BatteryStatus oCard::isCriticalCardVoltage(int miliVolt) {
if (miliVolt > 0 && miliVolt < 2445) if (miliVolt > 10 && miliVolt < 2445)
return BatteryStatus::Bad; return BatteryStatus::Bad;
else if (miliVolt > 0 && miliVolt <= 2710) else if (miliVolt > 10 && miliVolt <= 2710)
return BatteryStatus::Warning; return BatteryStatus::Warning;
return BatteryStatus::OK; return BatteryStatus::OK;

View File

@ -404,7 +404,7 @@ int oClass::getNumRunners(bool checkFirstLeg, bool noCountVacant, bool noCountNo
} }
} }
string key = getCountTypeKey(checkFirstLeg ? 0 : -1, string key = getCountTypeKey(checkFirstLeg ? 0 : -1,
noCountNotCompeting ? CountKeyType::All : CountKeyType::IncludeNotCompeting, noCountNotCompeting ? CountKeyType::AllCompeting : CountKeyType::IncludeNotCompeting,
!noCountVacant); !noCountVacant);
auto res = tTypeKeyToRunnerCount.second.find(key); auto res = tTypeKeyToRunnerCount.second.find(key);
@ -419,7 +419,7 @@ int oClass::getNumRunners(bool checkFirstLeg, bool noCountVacant, bool noCountNo
continue; continue;
if (noCountVacant && r.isVacant()) if (noCountVacant && r.isVacant())
continue; continue;
if (noCountNotCompeting && r.getStatus() == StatusNotCompetiting) if (noCountNotCompeting && (r.getStatus() == StatusNotCompetiting || r.getStatus() == StatusCANCEL))
continue; continue;
int id = r.getClassId(true); int id = r.getClassId(true);
@ -3371,6 +3371,16 @@ int oClass::getLegPlace(int ifrom, int ito, int time) const
return 0; return 0;
} }
int oClass::getAccLegControlLeader(int teamLeg, int courseControlId) const {
assert(false);
return 0;
}
int oClass::getAccLegControlPlace(int teamLeg, int courseControlId, int time) const {
assert(false);
return 0;
}
void oClass::insertAccLegPlace(int courseId, int controlNo, int time, int place) void oClass::insertAccLegPlace(int courseId, int controlNo, int time, int place)
{ /* { /*
char bf[256]; char bf[256];
@ -3441,7 +3451,6 @@ int oClass::getAccLegPlace(int courseId, int controlNo, int time) const
return 0; return 0;
} }
void oClass::calculateSplits() { void oClass::calculateSplits() {
clearSplitAnalysis(); clearSplitAnalysis();
set<pCourse> cSet; set<pCourse> cSet;
@ -3460,33 +3469,50 @@ void oClass::calculateSplits() {
LegResult legBestTime; LegResult legBestTime;
vector<pRunner> rCls; vector<pRunner> rCls;
oe->getRunners(Id, -1, rCls, false); oe->getRunners(Id, -1, rCls, false);
/*
if (isQualificationFinalBaseClass() || isQualificationFinalBaseClass()) {
for (auto &r : oe->Runners) { for (pRunner it : rCls) {
if (!r.isRemoved() && r.getClassRef(true) == this) pCourse tpc = it->getCourse(false);
rCls.push_back(&r); if (tpc == nullptr)
continue;
cSet.insert(tpc);
} }
map<int, vector<pRunner>> rClsCrs;
if (cSet.size() > 1) {
for (pRunner it : rCls) {
pCourse tpc = it->getCourse(false);
if (tpc)
rClsCrs[tpc->getId()].push_back(it);
}
}
bool multiLeg = getNumStages() > 1; // Perhaps ignore parallell legs...
if (multiLeg) {
teamLegCourseControlToLeaderPlace.resize(getNumStages());
for (auto& lp : teamLegCourseControlToLeaderPlace)
lp.clear();
} }
else { else {
for (auto &r : oe->Runners) { teamLegCourseControlToLeaderPlace.clear();
if (!r.isRemoved() && r.Class == this)
rCls.push_back(&r);
} }
}*/
for (set<pCourse>::iterator cit = cSet.begin(); cit!= cSet.end(); ++cit) { for (pCourse pc : cSet) {
pCourse pc = *cit;
// Store all split times in a matrix // Store all split times in a matrix
const unsigned nc = pc->getNumControls(); const unsigned nc = pc->getNumControls();
if (nc == 0) if (nc == 0)
return; return;
vector< vector<int> > splits(nc+1); vector<vector<int>> splits(nc+1);
vector< vector<int> > splitsAcc(nc+1); vector<vector<int>> splitsAcc(nc+1);
vector<bool> acceptMissingPunch(nc+1, true); vector<int8_t> acceptMissingPunch(nc+1, true);
vector<pRunner>* rList;
if (rClsCrs.empty())
rList = &rCls;
else
rList = &rClsCrs[pc->getId()];
for (pRunner it : rCls) { for (pRunner it : *rList) {
pCourse tpc = it->getCourse(false); pCourse tpc = it->getCourse(false);
if (tpc != pc || tpc == 0) if (tpc != pc || tpc == 0)
continue; continue;
@ -3504,7 +3530,7 @@ void oClass::calculateSplits() {
} }
} }
for (pRunner it : rCls) { for (pRunner it : *rList) {
pCourse tpc = it->getCourse(false); pCourse tpc = it->getCourse(false);
if (tpc != pc) if (tpc != pc)
@ -3513,17 +3539,46 @@ void oClass::calculateSplits() {
const vector<SplitData> &sp = it->getSplitTimes(true); const vector<SplitData> &sp = it->getSplitTimes(true);
const int s = min<int>(nc, sp.size()); const int s = min<int>(nc, sp.size());
int off = 0;
unordered_map<int, PlaceTime>* teamAccTimes = nullptr;
if (multiLeg && it->tInTeam)
off = it->tInTeam->getTotalRunningTimeAtLegStart(it->tLeg, false);
if (off > 0 && it->tLeg < teamLegCourseControlToLeaderPlace.size())
teamAccTimes = &teamLegCourseControlToLeaderPlace[it->tLeg];
vector<int> &tLegTimes = it->tLegTimes; vector<int> &tLegTimes = it->tLegTimes;
tLegTimes.resize(nc + 1); tLegTimes.resize(nc + 1);
bool ok = true; bool ok = true;
// Acc team finish time
if (teamAccTimes && it->FinishTime > 0 && (it->tStatus == StatusOK || it->tStatus == StatusUnknown)) {
int ccId = oPunch::PunchFinish;
int t = it->getRunningTime(false);
auto& res = (*teamAccTimes)[ccId];
if (res.leader <= 0 || res.leader > t + off)
res.leader = t + off;
// Count times
++res.timeToPlace[t + off];
}
for (int k = 0; k < s; k++) { for (int k = 0; k < s; k++) {
if (sp[k].getTime(true) > 0) { if (sp[k].getTime(true) > 0) {
if (ok) { if (ok) {
// Store accumulated times // Store accumulated times
int t = sp[k].getTime(true) - it->tStartTime; int t = sp[k].getTime(true) - it->tStartTime;
if (it->tStartTime>0 && t>0) if (it->tStartTime > 0 && t > 0) {
splitsAcc[k].push_back(t); splitsAcc[k].push_back(t);
if (teamAccTimes) {
int ccId = pc->getCourseControlId(k);
auto& res = (*teamAccTimes)[ccId];
if (res.leader <= 0 || res.leader > t + off)
res.leader = t + off;
// Count times
++res.timeToPlace[t + off];
}
}
} }
if (k == 0) { // start -> first if (k == 0) { // start -> first
@ -3636,8 +3691,7 @@ void oClass::calculateSplits() {
} }
} }
for (set<pCourse>::iterator cit = cSet.begin(); cit != cSet.end(); ++cit) { for (pCourse pc : cSet) {
pCourse pc = *cit;
const unsigned nc = pc->getNumControls(); const unsigned nc = pc->getNumControls();
vector<int> normRes(nc+1); vector<int> normRes(nc+1);
vector<int> bestRes(nc+1); vector<int> bestRes(nc+1);
@ -3655,6 +3709,18 @@ void oClass::calculateSplits() {
swap(tSplitAnalysisData[pc->getId()], normRes); swap(tSplitAnalysisData[pc->getId()], normRes);
swap(tCourseLegLeaderTime[pc->getId()], bestRes); swap(tCourseLegLeaderTime[pc->getId()], bestRes);
} }
// Convert number of competitors with time to place
for (auto& courseControlLeaderPlace : teamLegCourseControlToLeaderPlace) {
for (auto& leaderPlace : courseControlLeaderPlace) {
int place = 1;
for (auto& numTimes : leaderPlace.second.timeToPlace) {
int num = numTimes.second;
numTimes.second = place;
place += num;
}
}
}
} }
bool oClass::isRogaining() const { bool oClass::isRogaining() const {

View File

@ -25,6 +25,7 @@
#include <vector> #include <vector>
#include <set> #include <set>
#include <map> #include <map>
#include <unordered_map>
#include "inthashmap.h" #include "inthashmap.h"
class oClass; class oClass;
typedef oClass* pClass; typedef oClass* pClass;
@ -260,9 +261,20 @@ protected:
inthashmap *tLegTimeToPlace; inthashmap *tLegTimeToPlace;
inthashmap *tLegAccTimeToPlace; inthashmap *tLegAccTimeToPlace;
struct PlaceTime {
int leader = -1;
map<int, int> timeToPlace;
};
vector<unordered_map<int, PlaceTime>> teamLegCourseControlToLeaderPlace;
void insertLegPlace(int from, int to, int time, int place); void insertLegPlace(int from, int to, int time, int place);
void insertAccLegPlace(int courseId, int controlNo, int time, int place); void insertAccLegPlace(int courseId, int controlNo, int time, int place);
/** Get relay/team accumulated leader time/place at control. */
int getAccLegControlLeader(int teamLeg, int courseControlId) const;
int getAccLegControlPlace(int teamLeg, int courseControlId, int time) const;
// For sub split times // For sub split times
int tLegLeaderTime; int tLegLeaderTime;
mutable int tNoTiming; mutable int tNoTiming;
@ -357,7 +369,7 @@ protected:
mutable pair<int, map<string, int>> tTypeKeyToRunnerCount; mutable pair<int, map<string, int>> tTypeKeyToRunnerCount;
enum CountKeyType { enum CountKeyType {
All, AllCompeting,
Finished, Finished,
ExpectedStarting, ExpectedStarting,
DNS, DNS,
@ -620,7 +632,7 @@ public:
bool hasTrueMultiCourse() const; bool hasTrueMultiCourse() const;
unsigned getNumStages() const {return MultiCourse.size();} unsigned getNumStages() const {return MultiCourse.size();}
/** Get the set of true legs, identifying parallell legs etc. Returns indecs into /** Get the set of true legs, identifying parallell legs etc. Returns indices into
legInfo of the last leg of the true leg (first), and true leg (second).*/ legInfo of the last leg of the true leg (first), and true leg (second).*/
struct TrueLegInfo { struct TrueLegInfo {
protected: protected:

View File

@ -653,7 +653,8 @@ const vector< pair<wstring, size_t> > &oEvent::getCourses(vector<pair<wstring, s
vector<wchar_t> filt_lc(filter.length() + 1); vector<wchar_t> filt_lc(filter.length() + 1);
wcscpy_s(filt_lc.data(), filt_lc.size(), filter.c_str()); wcscpy_s(filt_lc.data(), filt_lc.size(), filter.c_str());
CharLowerBuff(filt_lc.data(), filter.length()); prepareMatchString(filt_lc.data(), filter.length());
int score; int score;
wstring b; wstring b;
for (size_t k = 0; k < ac.size(); k++) { for (size_t k = 0; k < ac.size(); k++) {

View File

@ -70,7 +70,7 @@
extern Image image; extern Image image;
//Version of database //Version of database
int oEvent::dbVersion = 90; int oEvent::dbVersion = 91;
bool oEvent::useSubSecond() const { bool oEvent::useSubSecond() const {
if (useSubsecondsVersion == dataRevision) if (useSubsecondsVersion == dataRevision)
@ -540,6 +540,8 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi)
oEventData->addVariableString("MergeTag", 12, "Tag"); oEventData->addVariableString("MergeTag", 12, "Tag");
oEventData->addVariableString("MergeInfo", "MergeInfo"); oEventData->addVariableString("MergeInfo", "MergeInfo");
oEventData->addVariableString("SplitPrint", 40, "Sträcktidslista"); // Id from MetaListContainer::getUniqueId oEventData->addVariableString("SplitPrint", 40, "Sträcktidslista"); // Id from MetaListContainer::getUniqueId
oEventData->addVariableInt("NoVacantBib", oDataContainer::oIS8U, "Inga vakanta nummerlappar");
oEventData->initData(this, dataSize); oEventData->initData(this, dataSize);
oClubData=new oDataContainer(oClub::dataSize); oClubData=new oDataContainer(oClub::dataSize);
@ -4781,7 +4783,7 @@ bool oEvent::hasTeam() const
return Teams.size() > 0; return Teams.size() > 0;
} }
void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) { void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber, bool assignToVacant) {
if ( !classHasTeams(ClassId) ) { if ( !classHasTeams(ClassId) ) {
sortRunners(ClassStartTimeClub); sortRunners(ClassStartTimeClub);
oRunnerList::iterator it; oRunnerList::iterator it;
@ -4804,6 +4806,8 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) {
if (it->isRemoved()) if (it->isRemoved())
continue; continue;
if ( (ClassId==0 || it->getClassId(true)==ClassId) && (it->legToRun()==leg || leg == -1)) { if ( (ClassId==0 || it->getClassId(true)==ClassId) && (it->legToRun()==leg || leg == -1)) {
if (!assignToVacant && it->isVacant())
continue;
wchar_t bib[32]; wchar_t bib[32];
swprintf_s(bib, pattern, num); swprintf_s(bib, pattern, num);
pClass pc = it->getClassRef(true); pClass pc = it->getClassRef(true);
@ -4832,6 +4836,8 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) {
for (auto it = Teams.begin(); it != Teams.end(); ++it) { for (auto it = Teams.begin(); it != Teams.end(); ++it) {
if (it->isRemoved()) if (it->isRemoved())
continue; continue;
if (!assignToVacant && it->isVacant())
continue;
if (ClassId == 0 || it->getClassId(false) == ClassId) { if (ClassId == 0 || it->getClassId(false) == ClassId) {
if (it->getClassRef(false) && it->getClassRef(false)->getBibMode() != BibFree) { if (it->getClassRef(false) && it->getClassRef(false)->getBibMode() != BibFree) {
for (size_t i = 0; i < it->Runners.size(); i++) { for (size_t i = 0; i < it->Runners.size(); i++) {
@ -4847,7 +4853,7 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) {
} }
} }
sortTeams(ClassStartTime, 0, true); // Sort on first leg starttime and sortindex sortTeams(ClassStartTimeClub, 0, true); // Sort on first leg starttime and sortindex
if (!firstNumber.empty()) { if (!firstNumber.empty()) {
wchar_t pattern[32]; wchar_t pattern[32];
@ -4856,6 +4862,8 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) {
for (auto it=Teams.begin(); it != Teams.end(); ++it) { for (auto it=Teams.begin(); it != Teams.end(); ++it) {
if (it->isRemoved()) if (it->isRemoved())
continue; continue;
if (!assignToVacant && it->isVacant())
continue;
if (ClassId == 0 || it->getClassId(false) == ClassId) { if (ClassId == 0 || it->getClassId(false) == ClassId) {
wchar_t bib[32]; wchar_t bib[32];
@ -4887,6 +4895,8 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) {
} }
void oEvent::addAutoBib() { void oEvent::addAutoBib() {
bool noBibToVacant = oe->getDCI().getInt("NoVacantBib") != 0;
sortRunners(ClassStartTimeClub); sortRunners(ClassStartTimeClub);
oRunnerList::iterator it; oRunnerList::iterator it;
int clsId = -1; int clsId = -1;
@ -4907,6 +4917,7 @@ void oEvent::addAutoBib() {
pClass cls = tit->getClassRef(false); pClass cls = tit->getClassRef(false);
if (cls == 0) if (cls == 0)
continue; continue;
teamStartNo[tit->getId()] = tit->getStartNo(); teamStartNo[tit->getId()] = tit->getStartNo();
wstring bibInfo = cls->getDCI().getString("Bib"); wstring bibInfo = cls->getDCI().getString("Bib");
@ -4935,7 +4946,7 @@ void oEvent::addAutoBib() {
} }
} }
sortTeams(ClassStartTime, 0, true); // Sort on first leg starttime and sortindex sortTeams(ClassStartTimeClub, 0, true); // Sort on first leg starttime and sortindex
map<int, vector<pTeam> > cls2TeamList; map<int, vector<pTeam> > cls2TeamList;
for (oTeamList::iterator tit = Teams.begin(); tit != Teams.end(); ++tit) { for (oTeamList::iterator tit = Teams.begin(); tit != Teams.end(); ++tit) {
@ -4946,7 +4957,7 @@ void oEvent::addAutoBib() {
} }
map<int, vector<pRunner> > cls2RunnerList; map<int, vector<pRunner> > cls2RunnerList;
for (it=Runners.begin(); it != Runners.end(); ++it) { for (it = Runners.begin(); it != Runners.end(); ++it) {
if (it->isRemoved() || !it->getClassId(false)) if (it->isRemoved() || !it->getClassId(false))
continue; continue;
int clsId = it->getClassId(true); int clsId = it->getClassId(true);
@ -5015,6 +5026,11 @@ void oEvent::addAutoBib() {
else { else {
bool lockedForking = cls->lockedForking(); bool lockedForking = cls->lockedForking();
for (size_t k = 0; k < tl.size(); k++) { for (size_t k = 0; k < tl.size(); k++) {
if (noBibToVacant && tl[k]->isVacant()) {
tl[k]->getDI().setString("Bib", L""); //Remove only bib
}
else {
wchar_t buff[32]; wchar_t buff[32];
swprintf_s(buff, pattern, number); swprintf_s(buff, pattern, number);
@ -5026,6 +5042,7 @@ void oEvent::addAutoBib() {
tl[k]->setBib(buff, number, true); tl[k]->setBib(buff, number, true);
} }
number += interval; number += interval;
}
tl[k]->applyBibs(); tl[k]->applyBibs();
tl[k]->evaluate(ChangeType::Update); tl[k]->evaluate(ChangeType::Update);
} }
@ -5046,7 +5063,7 @@ void oEvent::addAutoBib() {
cls->synchronize(true); cls->synchronize(true);
} }
for (size_t k = 0; k < rl.size(); k++) { for (size_t k = 0; k < rl.size(); k++) {
if (pattern[0]) { if (pattern[0] && (!noBibToVacant || !rl[k]->isVacant())) {
wchar_t buff[32]; wchar_t buff[32];
swprintf_s(buff, pattern, number); swprintf_s(buff, pattern, number);
rl[k]->setBib(buff, number, !locked); rl[k]->setBib(buff, number, !locked);
@ -5267,19 +5284,73 @@ bool compareClubClassTeamName(const oRunner &a, const oRunner &b)
return a.getClub()<b.getClub(); return a.getClub()<b.getClub();
} }
void oEvent::assignCardInteractive(gdioutput &gdi, GUICALLBACK cb) void oEvent::assignCardInteractive(gdioutput& gdi, GUICALLBACK cb, SortOrder& orderRunners)
{ {
gdi.fillDown(); gdi.fillDown();
gdi.dropLine(1); gdi.dropLine(1);
gdi.addString("", 2, "Tilldelning av hyrbrickor"); gdi.addString("", 2, "Tilldelning av hyrbrickor");
class SortUpdate : public GuiHandler {
SortOrder& orderRunners;
oEvent* oe;
GUICALLBACK cb;
public:
SortUpdate(oEvent *oe, GUICALLBACK cb, SortOrder& orderRunners) :
orderRunners(orderRunners), cb(cb), oe(oe) { }
void handle(gdioutput& gdi, BaseInfo& info, GuiEventType type) final {
ListBoxInfo& lb = dynamic_cast<ListBoxInfo&>(info);
orderRunners = SortOrder(lb.data);
oe->assignCardInteractive(gdi, cb, orderRunners);
}
~SortUpdate() {
}
};
if (gdi.hasData("AssignCardMark")) {
gdi.restore("AssignCardRP", false);
}
else {
auto h = make_shared<SortUpdate>(this, cb, orderRunners);
gdi.dropLine(0.5);
gdi.addSelection("Sorting", 200, 300, nullptr, L"Sortering:").setHandler(h);
vector<pair<wstring, size_t> > orders;
for (auto ord : MetaList::getOrderToSymbol()) {
if (ord.first != SortOrder::Custom && ord.first != SortOrder::ClassDefaultResult)
orders.push_back(make_pair(lang.tl(ord.second), ord.first));
}
sort(orders.begin(), orders.end());
orders.insert(orders.begin(), make_pair(lang.tl("Standard"), SortOrder::Custom));
gdi.addItem("Sorting", orders);
gdi.selectItemByData("Sorting", orderRunners);
gdi.dropLine();
gdi.setData("AssignCardMark", 1);
gdi.setRestorePoint("AssignCardRP");
}
if (orderRunners == SortOrder::Custom) {
Runners.sort(compareClubClassTeamName); Runners.sort(compareClubClassTeamName);
}
else {
CurrentSortOrder = orderRunners;
Runners.sort();
}
oRunnerList::iterator it; oRunnerList::iterator it;
pClub lastClub=0; pClub lastClub = nullptr;
pClass lastClass = nullptr;
int k=0; const int px4 = gdi.scaleLength(4);
for (it=Runners.begin(); it != Runners.end(); ++it) { const int px450 = gdi.scaleLength(450);
int k = 0;
bool groupByClub = orderRunners == SortOrder::Custom || orderRunners == ClubClassStartTime;
bool groupByClass = orderByClass(orderRunners);
for (it = Runners.begin(); it != Runners.end(); ++it) {
if (it->skip() || it->getCardNo() || it->isVacant() || it->needNoCard()) if (it->skip() || it->getCardNo() || it->isVacant() || it->needNoCard())
continue; continue;
@ -5287,28 +5358,43 @@ void oEvent::assignCardInteractive(gdioutput &gdi, GUICALLBACK cb)
if (it->getStatus() == StatusDNS || it->getStatus() == StatusCANCEL || it->getStatus() == StatusNotCompetiting) if (it->getStatus() == StatusDNS || it->getStatus() == StatusCANCEL || it->getStatus() == StatusNotCompetiting)
continue; continue;
if (it->Club!=lastClub) { if (groupByClub && it->Club != lastClub) {
lastClub=it->Club; lastClub = it->Club;
gdi.dropLine(0.5); gdi.dropLine(0.5);
gdi.addString("", 1, it->getClub()); gdi.addStringUT(1, it->getClub());
}
else if (groupByClass && it->Class != lastClass) {
lastClass = it->getClassRef(true);
gdi.dropLine(0.5);
gdi.addStringUT(1, it->getClass(true));
} }
wstring r; wstring r;
if (it->Class) if (!groupByClass && it->Class)
r+=it->getClass(false)+L", "; r += it->getClass(false) + L", ";
if (!groupByClub && it->Club)
r += it->getClub() + L", ";
if (it->tInTeam) { if (it->tInTeam) {
r+=itow(it->tInTeam->getStartNo()) + L" " + it->tInTeam->getName() + L", "; if (!it->tInTeam->getBib().empty())
} r += it->tInTeam->getBib() + L" ";
r += it->tInTeam->getName() + L", ";
}
else {
if (!it->getBib().empty())
r += it->getBib() + L" ";
}
r += it->getName() + L":"; r += it->getName() + L":";
gdi.fillRight(); gdi.fillRight();
gdi.pushX(); gdi.pushX();
gdi.addStringUT(0, r); gdi.addStringUT(0, r);
char id[24]; char id[24];
sprintf_s(id, "*%d", k++); sprintf_s(id, "*%d", k++);
gdi.addInput(max(gdi.getCX(), 450), gdi.getCY()-4, gdi.addInput(max(gdi.getCX(), px450), gdi.getCY() - px4,
id, L"", 10, cb).setExtra(it->getId()); id, L"", 10, cb).setExtra(it->getId());
gdi.popX(); gdi.popX();
@ -5316,8 +5402,10 @@ void oEvent::assignCardInteractive(gdioutput &gdi, GUICALLBACK cb)
gdi.fillDown(); gdi.fillDown();
} }
if (k==0) if (k == 0)
gdi.addString("", 0, "Ingen löpare saknar bricka"); gdi.addString("", 0, "Ingen löpare saknar bricka");
gdi.refresh();
} }
void oEvent::calcUseStartSeconds() void oEvent::calcUseStartSeconds()

View File

@ -726,7 +726,7 @@ public:
inline bool useStartSeconds() const {return tUseStartSeconds;} inline bool useStartSeconds() const {return tUseStartSeconds;}
void calcUseStartSeconds(); void calcUseStartSeconds();
void assignCardInteractive(gdioutput &gdi, GUICALLBACK cb); void assignCardInteractive(gdioutput &gdi, GUICALLBACK cb, SortOrder& orderRunners);
int getPropertyInt(const char *name, int def); int getPropertyInt(const char *name, int def);
const string &getPropertyString(const char *name, const string &def); const string &getPropertyString(const char *name, const string &def);
@ -820,7 +820,7 @@ public:
void checkOrderIdMultipleCourses(int ClassId); void checkOrderIdMultipleCourses(int ClassId);
void addBib(int ClassId, int leg, const wstring &firstNumber); void addBib(int ClassId, int leg, const wstring &firstNumber, bool assignVacant);
void addAutoBib(); void addAutoBib();
//Speaker functions. //Speaker functions.

View File

@ -510,10 +510,13 @@ private:
else { else {
vector<pRunner> cr; vector<pRunner> cr;
oe->getRunners(c_it->getId(), 0, cr, false); oe->getRunners(c_it->getId(), 0, cr, false);
for (pRunner r : cr) for (pRunner r : cr) {
if (r->getStatus() == StatusNotCompetiting || r->getStatus() == StatusCANCEL)
continue;
if (r->getStartGroup(true) == ci.startGroupId) if (r->getStartGroup(true) == ci.startGroupId)
nr++; nr++;
} }
}
if (ci.nVacant == -1 || !ci.nVacantSpecified || di.changedVacancyInfo) { if (ci.nVacant == -1 || !ci.nVacantSpecified || di.changedVacancyInfo) {
// Auto initialize // Auto initialize
@ -982,7 +985,7 @@ void oEvent::loadDrawSettings(const set<int> &classes, DrawInfo &drawInfo, vecto
cInfo[i].nRunners = pc->getNumRunners(true, true, true) + cInfo[i].nVacant; cInfo[i].nRunners = pc->getNumRunners(true, true, true) + cInfo[i].nVacant;
if (cInfo[i].nRunners>0) { if (cInfo[i].nRunners > 0) {
runnerPerGroup[cInfo[i].unique] += cInfo[i].nRunners; runnerPerGroup[cInfo[i].unique] += cInfo[i].nRunners;
runnerPerCourse[cInfo[i].courseId] += cInfo[i].nRunners; runnerPerCourse[cInfo[i].courseId] += cInfo[i].nRunners;
} }
@ -996,6 +999,7 @@ void oEvent::loadDrawSettings(const set<int> &classes, DrawInfo &drawInfo, vecto
cInfo[k].nRunnersCourse = runnerPerCourse[cInfo[k].courseId]; cInfo[k].nRunnersCourse = runnerPerCourse[cInfo[k].courseId];
} }
} }
void oEvent::drawRemaining(DrawMethod method, bool placeAfter) void oEvent::drawRemaining(DrawMethod method, bool placeAfter)
{ {
DrawType drawType = placeAfter ? DrawType::RemainingAfter : DrawType::RemainingBefore; DrawType drawType = placeAfter ? DrawType::RemainingAfter : DrawType::RemainingBefore;
@ -1770,7 +1774,7 @@ void oEvent::drawList(const vector<ClassDrawSpecification> &spec,
for (it=Runners.begin(); it != Runners.end(); ++it) { for (it=Runners.begin(); it != Runners.end(); ++it) {
int cid = it->getClassId(true); int cid = it->getClassId(true);
if (!it->isRemoved() && clsId2Ix.count(cid)) { if (!it->isRemoved() && clsId2Ix.count(cid)) {
if (it->getStatus() == StatusNotCompetiting) if (it->getStatus() == StatusNotCompetiting || it->getStatus() == StatusCANCEL)
continue; continue;
int ix = clsId2Ix[cid]; int ix = clsId2Ix[cid];
if (spec[ix].startGroup != 0 && it->getStartGroup(true) != spec[ix].startGroup) if (spec[ix].startGroup != 0 && it->getStartGroup(true) != spec[ix].startGroup)
@ -1792,7 +1796,7 @@ void oEvent::drawList(const vector<ClassDrawSpecification> &spec,
for (it=Runners.begin(); it != Runners.end(); ++it) { for (it=Runners.begin(); it != Runners.end(); ++it) {
if (!it->isRemoved() && clsId2Ix.count(it->getClassId(true))) { if (!it->isRemoved() && clsId2Ix.count(it->getClassId(true))) {
if (it->getStatus() == StatusNotCompetiting) if (it->getStatus() == StatusNotCompetiting || it->getStatus() == StatusCANCEL)
continue; continue;
int st = it->getStartTime(); int st = it->getStartTime();

View File

@ -1051,8 +1051,9 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) const {
if (ccId <= 0) if (ccId <= 0)
continue; continue;
int crs = r->getCourse(false)->getId(); int crs = r->getCourse(false)->getId();
int time = p.getTimeInt() - r->getStartTime(); //XXX Team time int time = p.getTimeInt() - r->getStartTime();
r->tOnCourseResults.emplace_back(ccId, courseCCid2CourseIx[make_pair(crs, ccId)], time); int teamTotalTime = time + (r->tInTeam ? r->tInTeam->getTotalRunningTimeAtLegStart(r->tLeg, false) : 0);
r->tOnCourseResults.emplace_back(ccId, courseCCid2CourseIx[make_pair(crs, ccId)], time, teamTotalTime);
int clsId = r->getClassId(true); int clsId = r->getClassId(true);
int leg = r->getLegNumber(); int leg = r->getLegNumber();
if (cls->getQualificationFinal()) if (cls->getQualificationFinal())
@ -1072,6 +1073,8 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) const {
const set<int> &expectedCCid = classLeg2ExistingCCId[make_pair(clsId, leg)]; const set<int> &expectedCCid = classLeg2ExistingCCId[make_pair(clsId, leg)];
size_t nRT = 0; size_t nRT = 0;
int teamTotalOff = r.tInTeam ? r.tInTeam->getTotalRunningTimeAtLegStart(r.tLeg, false) : 0;
for (auto &radioTimes : r.tOnCourseResults.res) { for (auto &radioTimes : r.tOnCourseResults.res) {
if (expectedCCid.count(radioTimes.courseControlId)) if (expectedCCid.count(radioTimes.courseControlId))
nRT++; nRT++;
@ -1091,8 +1094,9 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) const {
} }
} }
if (!added) { if (!added) {
int time = p.getTimeInt() - r.getStartTime(); //XXX Team time int time = p.getTimeInt() - r.getStartTime();
r.tOnCourseResults.emplace_back(ccId, p.tIndex, time); int teamTotalTime = time + teamTotalOff;
r.tOnCourseResults.emplace_back(ccId, p.tIndex, time, teamTotalTime);
} }
} }
} }
@ -1101,11 +1105,13 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) const {
} }
vector<tuple<int, int, int>> timeRunnerIx; vector<tuple<int, int, int>> timeRunnerIx;
vector<tuple<int, int, int>> totalTimeRunnerIx;
for (auto rList : runnerByClassLeg) { for (auto rList : runnerByClassLeg) {
auto &rr = rList.second; auto &rr = rList.second;
pClass cls = getClass(rList.first.first); pClass cls = getClass(rList.first.first);
assert(cls); assert(cls);
bool totRes = cls->getNumStages() > 1; //bool totRes = cls->getNumStages() > 1;
set<int> &legCCId = classLeg2ExistingCCId[rList.first]; set<int> &legCCId = classLeg2ExistingCCId[rList.first];
legCCId.insert(oPunch::PunchFinish); legCCId.insert(oPunch::PunchFinish);
@ -1113,18 +1119,17 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) const {
// Leg with negative sign // Leg with negative sign
int negLeg = 0; int negLeg = 0;
timeRunnerIx.clear(); timeRunnerIx.clear();
totalTimeRunnerIx.clear();
int nRun = rr.size(); int nRun = rr.size();
if (ccId == oPunch::PunchFinish) { if (ccId == oPunch::PunchFinish) {
negLeg = -1000; //Finish, smallest number negLeg = -1000; //Finish, smallest number
for (int j = 0; j < nRun; j++) { for (int j = 0; j < nRun; j++) {
auto r = rr[j]; auto r = rr[j];
if (r->prelStatusOK(true, false, false)) { if (r->prelStatusOK(true, false, false)) {
int time; int time = r->getRunningTime(true);
if (!r->tInTeam || !totRes) int teamTotalTime = r->tInTeam ? r->tInTeam->getLegRunningTime(r->tLeg, true, false) : time;
time = r->getRunningTime(true);
else {
time = r->tInTeam->getLegRunningTime(r->tLeg, true, false);
}
int ix = -1; int ix = -1;
int nr = r->tOnCourseResults.res.size(); int nr = r->tOnCourseResults.res.size();
for (int i = 0; i < nr; i++) { for (int i = 0; i < nr; i++) {
@ -1139,9 +1144,10 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) const {
pCourse crs = r->getCourse(false); pCourse crs = r->getCourse(false);
if (crs) if (crs)
nc = crs->getNumControls(); nc = crs->getNumControls();
r->tOnCourseResults.emplace_back(ccId, nc, time); r->tOnCourseResults.emplace_back(ccId, nc, time, teamTotalTime);
} }
timeRunnerIx.emplace_back(time, j, ix); timeRunnerIx.emplace_back(time, j, ix);
totalTimeRunnerIx.emplace_back(teamTotalTime, j, ix);
} }
} }
} }
@ -1152,12 +1158,16 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) const {
for (int i = 0; i < nr; i++) { for (int i = 0; i < nr; i++) {
if (r->tOnCourseResults.res[i].courseControlId == ccId) { if (r->tOnCourseResults.res[i].courseControlId == ccId) {
timeRunnerIx.emplace_back(r->tOnCourseResults.res[i].time, j, i); timeRunnerIx.emplace_back(r->tOnCourseResults.res[i].time, j, i);
totalTimeRunnerIx.emplace_back(r->tOnCourseResults.res[i].teamTotalTime, j, i);
negLeg = min(negLeg, -r->tOnCourseResults.res[i].controlIx); negLeg = min(negLeg, -r->tOnCourseResults.res[i].controlIx);
break; break;
} }
} }
} }
} }
auto computeResult = [&rr, &negLeg](vector<tuple<int, int, int>>& timeRunnerIx, bool total) {
sort(timeRunnerIx.begin(), timeRunnerIx.end()); sort(timeRunnerIx.begin(), timeRunnerIx.end());
int place = 0; int place = 0;
@ -1174,15 +1184,25 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) const {
} }
auto r = rr[get<1>(timeRunnerIx[i])]; auto r = rr[get<1>(timeRunnerIx[i])];
int locIx = get<2>(timeRunnerIx[i]); int locIx = get<2>(timeRunnerIx[i]);
if (total) {
r->tOnCourseResults.res[locIx].teamTotalPlace = place;
r->tOnCourseResults.res[locIx].teamTotalAfter = time - leadTime;
}
else {
r->tOnCourseResults.res[locIx].place = place; r->tOnCourseResults.res[locIx].place = place;
r->tOnCourseResults.res[locIx].after = time - leadTime; r->tOnCourseResults.res[locIx].after = time - leadTime;
int &legWithTimeIndexNeg = r->currentControlTime.first; int& legWithTimeIndexNeg = r->currentControlTime.first;
if (negLeg < legWithTimeIndexNeg) { if (negLeg < legWithTimeIndexNeg) {
legWithTimeIndexNeg = negLeg; legWithTimeIndexNeg = negLeg;
r->currentControlTime.second = ct; r->currentControlTime.second = ct;
} }
} }
} }
};
computeResult(timeRunnerIx, false);
computeResult(totalTimeRunnerIx, true);
}
} }
} }

View File

@ -331,7 +331,8 @@ int oListInfo::getMaxCharWidth(const oEvent *oe,
break; break;
case lPunchName: case lPunchName:
case lControlName: case lControlName:
case lPunchNamedTime: { case lPunchNamedTime:
case lPunchTeamTotalNamedTime: {
wstring maxcn = lang.tl("Mål"); wstring maxcn = lang.tl("Mål");
vector<pControl> ctrl; vector<pControl> ctrl;
oe->getControls(ctrl, false); oe->getControls(ctrl, false);
@ -342,6 +343,8 @@ int oListInfo::getMaxCharWidth(const oEvent *oe,
} }
if (pps[k].type == lPunchNamedTime) if (pps[k].type == lPunchNamedTime)
extra = maxcn + L": 50:50 (50:50)"; extra = maxcn + L": 50:50 (50:50)";
if (pps[k].type == lPunchTeamTotalNamedTime)
extra = maxcn + L": 2:50:50 (50:50)";
else else
maxcn.swap(extra); maxcn.swap(extra);
} }
@ -366,6 +369,7 @@ int oListInfo::getMaxCharWidth(const oEvent *oe,
case lRunnerTimeAdjustment: case lRunnerTimeAdjustment:
case lRunnerGeneralTimeAfter: case lRunnerGeneralTimeAfter:
case lPunchTotalTimeAfter: case lPunchTotalTimeAfter:
case lPunchTeamTotalTimeAfter:
extra = L"+10:00"; extra = L"+10:00";
break; break;
case lTeamRogainingPointOvertime: case lTeamRogainingPointOvertime:
@ -390,12 +394,15 @@ int oListInfo::getMaxCharWidth(const oEvent *oe,
case lRunnerStageStatus: case lRunnerStageStatus:
case lRunnerTimePlaceFixed: case lRunnerTimePlaceFixed:
case lPunchLostTime: case lPunchLostTime:
case lPunchTotalTime:
case lPunchTimeSinceLast: case lPunchTimeSinceLast:
case lPunchSplitTime: case lPunchSplitTime:
case lPunchNamedSplit: case lPunchNamedSplit:
extra = L"50:50"; extra = L"50:50";
break; break;
case lPunchTotalTime:
case lPunchTeamTotalTime:
extra = L"1:50:50";
break;
case lRunnerGeneralTimeStatus: case lRunnerGeneralTimeStatus:
case lClassStartTimeRange: case lClassStartTimeRange:
extra = L"50:50 (50:50)"; extra = L"50:50 (50:50)";
@ -415,6 +422,7 @@ int oListInfo::getMaxCharWidth(const oEvent *oe,
case lTeamTotalPlace: case lTeamTotalPlace:
case lPunchControlPlace: case lPunchControlPlace:
case lPunchControlPlaceAcc: case lPunchControlPlaceAcc:
case lPunchControlPlaceTeamAcc:
case lRunnerStagePlace: case lRunnerStagePlace:
extra = L"99."; extra = L"99.";
break; break;
@ -517,7 +525,7 @@ int oListInfo::getMaxCharWidth(const oEvent *oe,
pp.linearLegIndex = true; pp.linearLegIndex = true;
int numIter = 1; int numIter = 1;
if (pp.type == lPunchNamedTime || pp.type == lPunchTime) { if (pp.type == lPunchNamedTime || pp.type == lPunchTime || pp.type == lPunchTeamTime) {
row[k] = max(row[k], 10); row[k] = max(row[k], 10);
pRunner r = pRunner(&*it); pRunner r = pRunner(&*it);
numIter = (r && r->getCard()) ? r->getCard()->getNumPunches() + 1 : 1; numIter = (r && r->getCard()) ? r->getCard()->getNumPunches() + 1 : 1;
@ -693,14 +701,16 @@ const wstring &oEvent::formatPunchStringAux(const oPrintPost &pp, const oListPar
} }
break; break;
case lPunchTime: case lPunchTime:
case lPunchTeamTime:
case lPunchControlNumber: case lPunchControlNumber:
case lPunchControlCode: case lPunchControlCode:
case lPunchLostTime: case lPunchLostTime:
case lPunchControlPlace: case lPunchControlPlace:
case lPunchControlPlaceAcc: case lPunchControlPlaceAcc:
case lPunchControlPlaceTeamAcc:
case lPunchSplitTime: case lPunchSplitTime:
case lPunchTotalTime: case lPunchTotalTime:
case lPunchTeamTotalTime:
case lPunchAbsTime: case lPunchAbsTime:
if (punch && r && !invalidClass) { if (punch && r && !invalidClass) {
if (punch->tIndex >= 0) { if (punch->tIndex >= 0) {
@ -710,9 +720,14 @@ const wstring &oEvent::formatPunchStringAux(const oPrintPost &pp, const oListPar
break; break;
} }
switch (pp.type) { switch (pp.type) {
case lPunchTime: { case lPunchTime:
case lPunchTeamTime: {
if (punch->hasTime()) { if (punch->hasTime()) {
swprintf_s(bfw, L"\u2013 (%s)", formatTime(punch->getTimeInt() - r->getStartTime()).c_str()); int off = 0;
if (pp.type == lPunchTeamTime && r->getTeam())
off = r->getTeam()->getTotalRunningTimeAtLegStart(r->getLegNumber(), false);
swprintf_s(bfw, L"\u2013 (%s)", formatTime(off + punch->getTimeInt() - r->getStartTime(), SubSecond::Off).c_str());
} }
else { else {
wsptr = &makeDash(L"- (-)"); wsptr = &makeDash(L"- (-)");
@ -733,12 +748,24 @@ const wstring &oEvent::formatPunchStringAux(const oPrintPost &pp, const oListPar
} }
case lPunchAbsTime: { case lPunchAbsTime: {
if (punch->hasTime()) if (punch->hasTime())
wsptr = &getAbsTime(punch->getTimeInt()); wsptr = &getAbsTime(punch->getTimeInt(), SubSecond::Off);
break; break;
} }
case lPunchTotalTime: { case lPunchTotalTime: {
if (punch->hasTime()) if (punch->hasTime())
wsptr = &formatTime(punch->getTimeInt() - r->getStartTime()); wsptr = &formatTime(punch->getTimeInt() - r->getStartTime(), SubSecond::Off);
break;
}
case lPunchTeamTotalTime: {
if (punch->hasTime()) {
pTeam t = r->getTeam();
if (!t || r->getLegNumber() == 0)
wsptr = &formatTime(punch->getTimeInt() - r->getStartTime(), SubSecond::Off);
else {
int input = t->getTotalRunningTimeAtLegStart(r->getLegNumber(), false);
wsptr = &formatTime(input + punch->getTimeInt() - r->getStartTime(), SubSecond::Off);
}
}
break; break;
} }
} }
@ -865,6 +892,11 @@ const wstring &oEvent::formatSpecialStringAux(const oPrintPost &pp, const oListP
} }
break; break;
case lCourseNumControls:
if (pc)
wsptr = &itow(pc->getNumControls());
break;
case lCourseClasses: case lCourseClasses:
if (pc) { if (pc) {
vector<pClass> cls; vector<pClass> cls;
@ -1175,11 +1207,21 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara
break; break;
case lCourseClimb: case lCourseClimb:
case lCourseUsageNoVacant:
case lCourseUsage:
if (r) { if (r) {
pCourse crs = r->getCourse(false); pCourse crs = r->getCourse(false);
return formatSpecialStringAux(pp, par, t, 0, crs, 0, counter); return formatSpecialStringAux(pp, par, t, 0, crs, 0, counter);
} }
break; break;
case lCourseNumControls:
if (r) {
pCourse crs = r->getCourse(true);
return formatSpecialStringAux(pp, par, t, 0, crs, 0, counter);
}
break;
case lCourseShortening: case lCourseShortening:
if (r) { if (r) {
int sh = r->getNumShortening(); int sh = r->getNumShortening();
@ -1783,7 +1825,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara
case lRunnerTimePlaceFixed: case lRunnerTimePlaceFixed:
if (r && !invalidClass) { if (r && !invalidClass) {
int t = r->getTimeWhenPlaceFixed(); int t = r->getTimeWhenPlaceFixed();
if (t == 0 || (t>0 && t < getComputerTime())) { if (t == 0 || (t > 0 && t < getComputerTime())) {
wcscpy_s(wbf, lang.tl("klar").c_str()); wcscpy_s(wbf, lang.tl("klar").c_str());
} }
else if (t == -1) else if (t == -1)
@ -2259,17 +2301,22 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara
case lControlName: case lControlName:
case lPunchName: case lPunchName:
case lPunchNamedTime: case lPunchNamedTime:
case lPunchTeamTotalNamedTime:
case lPunchNamedSplit: case lPunchNamedSplit:
case lPunchTime: case lPunchTime:
case lPunchTeamTime:
case lPunchSplitTime: case lPunchSplitTime:
case lPunchTotalTime: case lPunchTotalTime:
case lPunchTeamTotalTime:
case lPunchControlNumber: case lPunchControlNumber:
case lPunchControlCode: case lPunchControlCode:
case lPunchLostTime: case lPunchLostTime:
case lPunchControlPlace: case lPunchControlPlace:
case lPunchControlPlaceAcc: case lPunchControlPlaceAcc:
case lPunchControlPlaceTeamAcc:
case lPunchAbsTime: case lPunchAbsTime:
case lPunchTotalTimeAfter: case lPunchTotalTimeAfter:
case lPunchTeamTotalTimeAfter:
if (r && r->getCourse(false) && !invalidClass) { if (r && r->getCourse(false) && !invalidClass) {
const pCourse crs=r->getCourse(true); const pCourse crs=r->getCourse(true);
const oControl *ctrl = nullptr; const oControl *ctrl = nullptr;
@ -2281,22 +2328,23 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara
} }
switch (pp.type) { switch (pp.type) {
case lPunchNamedSplit: case lPunchNamedSplit:
if (ctrl && ctrl->hasName() && r->getPunchTime(counter.level3, false, true) > 0) { if (ctrl && ctrl->hasName() && r->getPunchTime(counter.level3, false, true, false) > 0) {
swprintf_s(wbf, L"%s", r->getNamedSplitS(counter.level3).c_str()); swprintf_s(wbf, L"%s", r->getNamedSplitS(counter.level3, SubSecond::Off).c_str());
} }
break; break;
case lPunchNamedTime: case lPunchNamedTime:
if (ctrl && ctrl->hasName() && (!par.lineBreakControlList || r->getPunchTime(counter.level3, false, true) > 0)) { case lPunchTeamTotalNamedTime:
if (ctrl && ctrl->hasName() && (!par.lineBreakControlList || r->getPunchTime(counter.level3, false, true, false) > 0)) {
swprintf_s(wbf, L"%s: %s (%s)", ctrl->getName().c_str(), swprintf_s(wbf, L"%s: %s (%s)", ctrl->getName().c_str(),
r->getNamedSplitS(counter.level3).c_str(), r->getNamedSplitS(counter.level3, SubSecond::Off).c_str(),
r->getPunchTimeS(counter.level3, false, true, SubSecond::Off).c_str()); r->getPunchTimeS(counter.level3, false, true, pp.type == lPunchTeamTotalNamedTime, SubSecond::Off).c_str());
} }
break; break;
case lControlName: case lControlName:
case lPunchName: case lPunchName:
if (ctrl && ctrl->hasName() && (!par.lineBreakControlList || r->getPunchTime(counter.level3, false, true) > 0)) { if (ctrl && ctrl->hasName() && (!par.lineBreakControlList || r->getPunchTime(counter.level3, false, true, false) > 0)) {
swprintf_s(wbf, L"%s", ctrl->getName().c_str()); swprintf_s(wbf, L"%s", ctrl->getName().c_str());
} }
else if (counter.level3 == nCtrl) { else if (counter.level3 == nCtrl) {
@ -2304,27 +2352,30 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara
} }
break; break;
case lPunchTime: { case lPunchTime:
case lPunchTeamTime: {
swprintf_s(wbf, L"%s (%s)", swprintf_s(wbf, L"%s (%s)",
r->getSplitTimeS(counter.level3, false).c_str(), r->getSplitTimeS(counter.level3, false, SubSecond::Off).c_str(),
r->getPunchTimeS(counter.level3, false, true, SubSecond::Off).c_str()); r->getPunchTimeS(counter.level3, false, true, pp.type == lPunchTeamTime, SubSecond::Off).c_str());
break; break;
} }
case lPunchSplitTime: { case lPunchSplitTime: {
wcscpy_s(wbf, r->getSplitTimeS(counter.level3, false).c_str()); wcscpy_s(wbf, r->getSplitTimeS(counter.level3, false, SubSecond::Off).c_str());
break; break;
} }
case lPunchTotalTime: { case lPunchTotalTime:
if (r->getPunchTime(counter.level3, false, true) > 0) { case lPunchTeamTotalTime: {
wcscpy_s(wbf, r->getPunchTimeS(counter.level3, false, true, SubSecond::Off).c_str()); int pt = r->getPunchTime(counter.level3, false, true, pp.type == lPunchTeamTotalTime);
} if (pt > 0)
wsptr = &formatTime(pt, SubSecond::Off);
break; break;
} }
case lPunchTotalTimeAfter: { case lPunchTotalTimeAfter:
if (r->getPunchTime(counter.level3, false, true) > 0) { case lPunchTeamTotalTimeAfter: {
int rt = r->getLegTimeAfterAcc(counter.level3); if (r->getPunchTime(counter.level3, false, true, false) > 0) {
int rt = r->getLegTimeAfterAcc(counter.level3, pp.type == lPunchTeamTotalTimeAfter);
if (rt > 0) if (rt > 0)
wcscpy_s(wbf, (L"+" + formatTime(rt)).c_str()); wcscpy_s(wbf, (L"+" + formatTime(rt, SubSecond::Off)).c_str());
} }
break; break;
} }
@ -2350,8 +2401,9 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara
swprintf_s(wbf, L"%d", p); swprintf_s(wbf, L"%d", p);
break; break;
} }
case lPunchControlPlaceAcc: { case lPunchControlPlaceAcc:
int p = r->getLegPlaceAcc(counter.level3); case lPunchControlPlaceTeamAcc: {
int p = r->getLegPlaceAcc(counter.level3, pp.type == lPunchControlPlaceTeamAcc);
if (p > 0) if (p > 0)
swprintf_s(wbf, L"%d", p); swprintf_s(wbf, L"%d", p);
break; break;
@ -2361,7 +2413,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara
break; break;
} }
case lPunchAbsTime: { case lPunchAbsTime: {
int t = r->getPunchTime(counter.level3, false, true); int t = r->getPunchTime(counter.level3, false, true, false);
if (t > 0) if (t > 0)
wsptr = &getAbsTime(r->tStartTime + t); wsptr = &getAbsTime(r->tStartTime + t);
break; break;
@ -3360,7 +3412,7 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form
} }
else if (li.listSubType == li.EBaseTypeCoursePunches || else if (li.listSubType == li.EBaseTypeCoursePunches ||
li.listSubType == li.EBaseTypeAllPunches) { li.listSubType == li.EBaseTypeAllPunches) {
pRunner r = it->Runners.empty() ? 0 : it->Runners[0]; pRunner r = it->getRunner(linearLegSpec);
if (!r) if (!r)
return true; return true;

View File

@ -61,6 +61,7 @@ enum EPostType {
lCourseUsage, lCourseUsage,
lCourseUsageNoVacant, lCourseUsageNoVacant,
lCourseClasses, lCourseClasses,
lCourseNumControls,
lRunnerName, lRunnerName,
lRunnerGivenName, lRunnerGivenName,
lRunnerFamilyName, lRunnerFamilyName,
@ -172,19 +173,28 @@ enum EPostType {
lTeamPlaceDiff, lTeamPlaceDiff,
lPunchNamedTime, lPunchNamedTime,
lPunchTeamTotalNamedTime,
lPunchNamedSplit, lPunchNamedSplit,
lPunchName, lPunchName,
lPunchTime, lPunchTime,
lPunchTeamTime,
lPunchControlNumber, lPunchControlNumber,
lPunchControlCode, lPunchControlCode,
lPunchLostTime, lPunchLostTime,
lPunchControlPlace, lPunchControlPlace,
lPunchControlPlaceAcc, lPunchControlPlaceAcc,
lPunchControlPlaceTeamAcc,
lPunchSplitTime, lPunchSplitTime,
lPunchTotalTime, lPunchTotalTime,
lPunchTotalTimeAfter, lPunchTotalTimeAfter,
lPunchTeamTotalTime,
lPunchTeamTotalTimeAfter,
lPunchAbsTime, lPunchAbsTime,
lPunchTimeSinceLast, lPunchTimeSinceLast,

View File

@ -2027,19 +2027,9 @@ bool oRunner::operator<(const oRunner &c) const {
pClub cl = getClubRef(); pClub cl = getClubRef();
pClub ocl = c.getClubRef(); pClub ocl = c.getClubRef();
if (cl != ocl) { if (cl != ocl) {
if (cl == nullptr && ocl) int cres = compareClubs(cl, ocl);
return true; if (cres != 2)
else if (ocl == nullptr) return cres != 0;
return false;
const wstring a = cl->getName();
const wstring b = ocl->getName();
int res = CompareString(LOCALE_USER_DEFAULT, 0,
a.c_str(), a.length(),
b.c_str(), b.length());
if (res != CSTR_EQUAL)
return res == CSTR_LESS_THAN;
} }
} }
@ -2401,7 +2391,9 @@ bool oRunner::operator<(const oRunner &c) const {
else return tStartTime < c.tStartTime; else return tStartTime < c.tStartTime;
} }
else if (Club != c.Club) { else if (Club != c.Club) {
return getClub() < c.getClub(); int cres = compareClubs(Club, c.Club);
if (cres != 2)
return cres != 0;
} }
} }
else if (oe->CurrentSortOrder == ClassTeamLeg) { else if (oe->CurrentSortOrder == ClassTeamLeg) {
@ -4173,11 +4165,11 @@ int oRunner::getSplitTime(int controlNumber, bool normalized) const
{ {
if (!Card) { if (!Card) {
if (controlNumber == 0) if (controlNumber == 0)
return getPunchTime(0, false, true); return getPunchTime(0, false, true, false);
else { else {
int ct = getPunchTime(controlNumber, false, true); int ct = getPunchTime(controlNumber, false, true, false);
if (ct > 0) { if (ct > 0) {
int dt = getPunchTime(controlNumber - 1, false, true); int dt = getPunchTime(controlNumber - 1, false, true, false);
if (dt > 0 && ct > dt) if (dt > 0 && ct > dt)
return ct - dt; return ct - dt;
} }
@ -4219,7 +4211,7 @@ int oRunner::getNamedSplit(int controlNumber) const {
return -1; return -1;
int k=controlNumber-1; int k=controlNumber-1;
int ct = getPunchTime(controlNumber, false, true); int ct = getPunchTime(controlNumber, false, true, false);
if (ct <= 0) if (ct <= 0)
return -1; return -1;
@ -4228,7 +4220,7 @@ int oRunner::getNamedSplit(int controlNumber) const {
pControl c = crs->Controls[k]; pControl c = crs->Controls[k];
if (c && c->hasName()) { if (c && c->hasName()) {
int dt = getPunchTime(k, false, true); int dt = getPunchTime(k, false, true, false);
if (dt > 0 && ct > dt) if (dt > 0 && ct > dt)
return max(ct - dt, -1); return max(ct - dt, -1);
else return -1; else return -1;
@ -4240,52 +4232,53 @@ int oRunner::getNamedSplit(int controlNumber) const {
return ct; return ct;
} }
wstring oRunner::getSplitTimeS(int controlNumber, bool normalized) const const wstring &oRunner::getSplitTimeS(int controlNumber, bool normalized, SubSecond mode) const
{ {
return formatTime(getSplitTime(controlNumber, normalized)); return formatTime(getSplitTime(controlNumber, normalized), mode);
} }
wstring oRunner::getNamedSplitS(int controlNumber) const const wstring &oRunner::getNamedSplitS(int controlNumber, SubSecond mode) const
{ {
return formatTime(getNamedSplit(controlNumber)); return formatTime(getNamedSplit(controlNumber), mode);
} }
int oRunner::getPunchTime(int controlNumber, bool normalized, bool adjusted) const int oRunner::getPunchTime(int controlIndex, bool normalized, bool adjusted, bool teamTotal) const
{ {
int off = teamTotal && tInTeam ? tInTeam->getTotalRunningTimeAtLegStart(getLegNumber(), false) : 0;
if (!Card) { if (!Card) {
pCourse pc = getCourse(false); pCourse pc = getCourse(false);
if (!pc || controlNumber > pc->getNumControls()) if (!pc || controlIndex > pc->getNumControls())
return -1; return -1;
if (controlNumber == pc->getNumControls()) if (controlIndex == pc->getNumControls())
return getFinishTime() - tStartTime; return getFinishTime() - tStartTime + off;
int ccId = pc->getCourseControlId(controlNumber); int ccId = pc->getCourseControlId(controlIndex);
pFreePunch fp = oe->getPunch(Id, ccId, getCardNo()); pFreePunch fp = oe->getPunch(Id, ccId, getCardNo());
if (fp) if (fp)
return fp->getTimeInt() - tStartTime; return fp->getTimeInt() - tStartTime + off;
return -1; return -1;
} }
const vector<SplitData> &st = getSplitTimes(normalized); const vector<SplitData> &st = getSplitTimes(normalized);
if (unsigned(controlNumber) < st.size()) { if (unsigned(controlIndex) < st.size()) {
if (st[controlNumber].hasTime()) if (st[controlIndex].hasTime())
return st[controlNumber].getTime(adjusted) - tStartTime; return st[controlIndex].getTime(adjusted) - tStartTime + off;
else return -1; else return -1;
} }
else if (unsigned(controlNumber) == st.size()) else if (unsigned(controlIndex) == st.size())
return FinishTime - tStartTime; return FinishTime - tStartTime + off;
return -1; return -1;
} }
wstring oRunner::getPunchTimeS(int controlNumber, bool normalized, bool adjusted, SubSecond mode) const const wstring &oRunner::getPunchTimeS(int controlIndex, bool normalized, bool adjusted,
{ bool teamTotal, SubSecond mode) const {
return formatTime(getPunchTime(controlNumber, normalized, adjusted), mode); return formatTime(getPunchTime(controlIndex, normalized, adjusted, teamTotal), mode);
} }
bool oAbstractRunner::isVacant() const bool oAbstractRunner::isVacant() const {
{
int vacClub = oe->getVacantClubIfExist(false); int vacClub = oe->getVacantClubIfExist(false);
return vacClub > 0 && getClubId()==vacClub; return vacClub > 0 && getClubId()==vacClub;
} }
@ -4409,7 +4402,8 @@ void oRunner::fillSpeakerObject(int leg, int courseControlId, int previousContro
} }
} }
pRunner oEvent::findRunner(const wstring &s, int lastId, const unordered_set<int> &inputFilter, pRunner oEvent::findRunner(const wstring &s, int lastId,
const unordered_set<int> &inputFilter,
unordered_set<int> &matchFilter) const unordered_set<int> &matchFilter) const
{ {
matchFilter.clear(); matchFilter.clear();
@ -4418,7 +4412,7 @@ pRunner oEvent::findRunner(const wstring &s, int lastId, const unordered_set<int
int sn = _wtoi(trm.c_str()); int sn = _wtoi(trm.c_str());
wchar_t s_lc[1024]; wchar_t s_lc[1024];
wcscpy_s(s_lc, s.c_str()); wcscpy_s(s_lc, s.c_str());
CharLowerBuff(s_lc, len); prepareMatchString(s_lc, len);
int score; int score;
pRunner res = 0; pRunner res = 0;
@ -4929,7 +4923,7 @@ void oRunner::printSplits(gdioutput& gdi, const oListInfo* li) const {
if (pc) { if (pc) {
for (int n = 0; n < pc->nControls; n++) { for (int n = 0; n < pc->nControls; n++) {
spMax = max(spMax, getSplitTime(n, false)); spMax = max(spMax, getSplitTime(n, false));
totMax = max(totMax, getPunchTime(n, false, false)); totMax = max(totMax, getPunchTime(n, false, false, false));
} }
} }
bool moreThanHour = max(totMax, getRunningTime(true)) >= timeConstHour; bool moreThanHour = max(totMax, getRunningTime(true)) >= timeConstHour;
@ -5082,7 +5076,7 @@ void oRunner::printSplits(gdioutput& gdi, const oListInfo* li) const {
adjust = getTimeAdjust(controlLegIndex); adjust = getTimeAdjust(controlLegIndex);
sp = getSplitTime(controlLegIndex, false); sp = getSplitTime(controlLegIndex, false);
if (sp > 0) { if (sp > 0) {
punchTime = getPunchTimeS(controlLegIndex, false, false, SubSecond::Off); punchTime = getPunchTimeS(controlLegIndex, false, false, false, SubSecond::Off);
gdi.addStringUT(cy, cx + c2, fontSmall | textRight, formatTime(sp, SubSecond::Off)); gdi.addStringUT(cy, cx + c2, fontSmall | textRight, formatTime(sp, SubSecond::Off));
} }
} }
@ -5807,7 +5801,7 @@ void oRunner::getLegTimeAfter(vector<int> &times) const
} }
} }
void oRunner::getLegTimeAfterAcc(vector<int> &times) const void oRunner::getLegTimeAfterAcc(vector<ResultData> &times) const
{ {
times.clear(); times.clear();
if (splitTimes.empty() || !Class || tStartTime<=0) if (splitTimes.empty() || !Class || tStartTime<=0)
@ -5835,6 +5829,9 @@ void oRunner::getLegTimeAfterAcc(vector<int> &times) const
//xxx reorder output //xxx reorder output
times.resize(nc+1); times.resize(nc+1);
bool isRelayTeam = tInTeam != nullptr;
int off = tInTeam ? tInTeam->getTotalRunningTimeAtLegStart(tLeg, false) : 0;
for (unsigned k = 0; k<=nc; k++) { for (unsigned k = 0; k<=nc; k++) {
int s = 0; int s = 0;
if (k < sp.size()) if (k < sp.size())
@ -5843,18 +5840,27 @@ void oRunner::getLegTimeAfterAcc(vector<int> &times) const
s = FinishTime; s = FinishTime;
if (s>0) { if (s>0) {
times[k] = s - tStartTime - leaders[k]; times[k].data = s - tStartTime - leaders[k];
if (times[k]<0) if (times[k].data < 0)
times[k] = -1; times[k].data = -1;
} }
else else
times[k] = -1; times[k].data = -1;
if (!isRelayTeam || times[k].data < 0)
times[k].teamTotalData = times[k].data;
else {
if (k < nc)
times[k].teamTotalData = s - tStartTime + off - cls->getAccLegControlLeader(tLeg, pc->getCourseControlId(k));
else
times[k].teamTotalData = s - tStartTime + off - cls->getAccLegControlLeader(tLeg, oPunch::PunchFinish);
}
} }
// Normalized order // Normalized order
const vector<int> &reorder = getCourse(true)->getMapToOriginalOrder(); const vector<int> &reorder = getCourse(true)->getMapToOriginalOrder();
if (!reorder.empty()) { if (!reorder.empty()) {
vector<int> orderedTimes(times.size()); vector<ResultData> orderedTimes(times.size());
for (size_t k = 0; k < min(reorder.size(), times.size()); k++) { for (size_t k = 0; k < min(reorder.size(), times.size()); k++) {
orderedTimes[k] = times[reorder[k]]; orderedTimes[k] = times[reorder[k]];
} }
@ -5862,7 +5868,7 @@ void oRunner::getLegTimeAfterAcc(vector<int> &times) const
} }
} }
void oRunner::getLegPlacesAcc(vector<int> &places) const void oRunner::getLegPlacesAcc(vector<ResultData> &places) const
{ {
places.clear(); places.clear();
pCourse pc = getCourse(false); pCourse pc = getCourse(false);
@ -5880,6 +5886,10 @@ void oRunner::getLegPlacesAcc(vector<int> &places) const
const unsigned nc = pc->getNumControls(); const unsigned nc = pc->getNumControls();
const vector<SplitData> &sp = getSplitTimes(true); const vector<SplitData> &sp = getSplitTimes(true);
places.resize(nc+1); places.resize(nc+1);
bool isRelayTeam = tInTeam != nullptr;
int off = tInTeam ? tInTeam->getTotalRunningTimeAtLegStart(tLeg, false) : 0;
for (unsigned k = 0; k<=nc; k++) { for (unsigned k = 0; k<=nc; k++) {
int s = 0; int s = 0;
if (k < sp.size()) if (k < sp.size())
@ -5890,17 +5900,24 @@ void oRunner::getLegPlacesAcc(vector<int> &places) const
if (s>0) { if (s>0) {
int time = s - tStartTime; int time = s - tStartTime;
if (time>0) if (time > 0) {
places[k] = cls->getAccLegPlace(id, k, time); places[k].data = cls->getAccLegPlace(id, k, time);
if (k < nc)
places[k].teamTotalData = cls->getAccLegControlPlace(tLeg, pc->getCourseControlId(k), time + off);
else else
places[k] = 0; places[k].teamTotalData = cls->getAccLegControlPlace(tLeg, oPunch::PunchFinish, time + off);
}
else {
places[k].data = 0;
places[k].teamTotalData = 0;
}
} }
} }
// Normalized order // Normalized order
const vector<int> &reorder = getCourse(true)->getMapToOriginalOrder(); const vector<int> &reorder = getCourse(true)->getMapToOriginalOrder();
if (!reorder.empty()) { if (!reorder.empty()) {
vector<int> orderedPlaces(reorder.size()); vector<ResultData> orderedPlaces(reorder.size());
for (size_t k = 0; k < reorder.size(); k++) { for (size_t k = 0; k < reorder.size(); k++) {
orderedPlaces[k] = places[reorder[k]]; orderedPlaces[k] = places[reorder[k]];
} }
@ -5974,31 +5991,31 @@ int oRunner::getLegTimeAfter(int ctrlNo) const {
return -1; return -1;
} }
int oRunner::getLegPlaceAcc(int ctrlNo) const { int oRunner::getLegPlaceAcc(int ctrlNo, bool teamTotal) const {
for (auto &res : tOnCourseResults.res) { for (auto &res : tOnCourseResults.res) {
if (res.controlIx == ctrlNo) if (res.controlIx == ctrlNo)
return res.place; return teamTotal ? res.place : res.teamTotalPlace;
} }
if (!Card) { if (!Card) {
return 0; return 0;
} }
setupRunnerStatistics(); setupRunnerStatistics();
if (unsigned(ctrlNo) < tPlaceLegAcc.size()) if (unsigned(ctrlNo) < tPlaceLegAcc.size())
return tPlaceLegAcc[ctrlNo]; return tPlaceLegAcc[ctrlNo].get(teamTotal);
else else
return 0; return 0;
} }
int oRunner::getLegTimeAfterAcc(int ctrlNo) const { int oRunner::getLegTimeAfterAcc(int ctrlNo, bool teamTotal) const {
for (auto &res : tOnCourseResults.res) { for (auto &res : tOnCourseResults.res) {
if (res.controlIx == ctrlNo) if (res.controlIx == ctrlNo)
return res.after; return teamTotal ? res.teamTotalAfter : res.after;
} }
if (!Card) if (!Card)
return -1; return -1;
setupRunnerStatistics(); setupRunnerStatistics();
if (unsigned(ctrlNo) < tAfterLegAcc.size()) if (unsigned(ctrlNo) < tAfterLegAcc.size())
return tAfterLegAcc[ctrlNo]; return tAfterLegAcc[ctrlNo].get(teamTotal);
else else
return -1; return -1;
} }

View File

@ -87,6 +87,25 @@ enum SortOrder {
SortEnumLastItem SortEnumLastItem
}; };
static bool orderByClass(SortOrder so) {
switch (so) {
case ClassStartTime:
case ClassTeamLeg:
case ClassResult:
case ClassDefaultResult:
case ClassCourseResult:
case ClassTotalResult:
case ClassTeamLegResult:
case ClassFinishTime:
case ClassStartTimeClub:
case ClassPoints:
case ClassLiveResult:
case ClassKnockoutTotalResult:
return true;
}
return false;
}
class oRunner; class oRunner;
typedef oRunner* pRunner; typedef oRunner* pRunner;
typedef const oRunner* cRunner; typedef const oRunner* cRunner;
@ -178,6 +197,9 @@ public:
protected: protected:
TempResult tmpResult; TempResult tmpResult;
/** Return 1 if a<b, 0 if b<a, otherwise 2.*/
static int compareClubs(const oClub* a, const oClub* b);
mutable int tTimeAdjustment; mutable int tTimeAdjustment;
mutable int tPointAdjustment; mutable int tPointAdjustment;
mutable int tAdjustDataRevision; mutable int tAdjustDataRevision;
@ -551,6 +573,23 @@ public:
}; };
class oRunner final: public oAbstractRunner { class oRunner final: public oAbstractRunner {
public:
struct ResultData {
private:
int data = 0;
int teamTotalData = 0;
public:
ResultData(int data, int teamTotalData) : data(data), teamTotalData(teamTotalData) { }
ResultData() = default;
int get(bool total) const {
return total ? teamTotalData : data;
}
friend class oRunner;
};
protected: protected:
pCourse Course; pCourse Course;
@ -633,24 +672,32 @@ protected:
void clearOnChangedRunningTime(); void clearOnChangedRunningTime();
// Cached runner statistics // Cached runner statistics
mutable vector<int> tMissedTime; mutable vector<int> tMissedTime;
mutable vector<int> tPlaceLeg; mutable vector<int> tPlaceLeg;
mutable vector<int> tAfterLeg; mutable vector<int> tAfterLeg;
mutable vector<int> tPlaceLegAcc; mutable vector<ResultData> tPlaceLegAcc;
mutable vector<int> tAfterLegAcc; mutable vector<ResultData> tAfterLegAcc;
// Used to calculate temporary split time results // Used to calculate temporary split time results
struct OnCourseResult { struct OnCourseResult {
OnCourseResult(int courseControlId, OnCourseResult(int courseControlId,
int controlIx, int controlIx,
int time) : courseControlId(courseControlId), int time,
controlIx(controlIx), time(time) {} int teamTotalTime) : courseControlId(courseControlId),
controlIx(controlIx), time(time),
teamTotalTime(teamTotalTime) {}
int courseControlId; int courseControlId;
int controlIx; int controlIx;
int time; int time;
int place; int teamTotalTime;
int after;
int place = -1;
int after = -1;
int teamTotalPlace = -1;
int teamTotalAfter = -1;
}; };
mutable pair<int, int> currentControlTime; mutable pair<int, int> currentControlTime;
@ -660,8 +707,9 @@ protected:
void clear() { hasAnyRes = false; res.clear(); } void clear() { hasAnyRes = false; res.clear(); }
void emplace_back(int courseControlId, void emplace_back(int courseControlId,
int controlIx, int controlIx,
int time) { int time,
res.emplace_back(courseControlId, controlIx, time); int teamTotalTime) {
res.emplace_back(courseControlId, controlIx, time, teamTotalTime);
hasAnyRes = true; hasAnyRes = true;
} }
bool empty() const { return hasAnyRes == false; } bool empty() const { return hasAnyRes == false; }
@ -867,8 +915,8 @@ public:
int getMissedTime(int ctrlNo) const; int getMissedTime(int ctrlNo) const;
int getLegPlace(int ctrlNo) const; int getLegPlace(int ctrlNo) const;
int getLegTimeAfter(int ctrlNo) const; int getLegTimeAfter(int ctrlNo) const;
int getLegPlaceAcc(int ctrlNo) const; int getLegPlaceAcc(int ctrlNo, bool teamTotal) const;
int getLegTimeAfterAcc(int ctrlNo) const; int getLegTimeAfterAcc(int ctrlNo, bool teamTotal) const;
/** Calculate the time when the runners place is fixed, i.e, /** Calculate the time when the runners place is fixed, i.e,
when no other runner can threaten the place. when no other runner can threaten the place.
@ -957,22 +1005,25 @@ public:
void getLegPlaces(vector<int> &places) const; void getLegPlaces(vector<int> &places) const;
void getLegTimeAfter(vector<int> &deltaTimes) const; void getLegTimeAfter(vector<int> &deltaTimes) const;
void getLegPlacesAcc(vector<int> &places) const; void getLegPlacesAcc(vector<ResultData> &places) const;
void getLegTimeAfterAcc(vector<int> &deltaTimes) const; void getLegTimeAfterAcc(vector<ResultData> &deltaTimes) const;
// Normalized = true means permuted to the unlooped version of the course // Normalized = true means permuted to the unlooped version of the course
int getSplitTime(int controlNumber, bool normalized) const; int getSplitTime(int controlNumber, bool normalized) const;
int getTimeAdjust(int controlNumber) const; int getTimeAdjust(int controlNumber) const;
int getNamedSplit(int controlNumber) const; int getNamedSplit(int controlNumber) const;
wstring getNamedSplitS(int controlNumber) const; const wstring &getNamedSplitS(int controlNumber, SubSecond mode) const;
/** Get running time from start to a control (specified by its index on the course)
normalized: true means permuted to the unlooped version of the course
teamTotalTime: true means time is measured from the team's starting time
*/
int getPunchTime(int controlIndex, bool normalized, bool adjusted, bool teamTotalTime) const;
const wstring &getPunchTimeS(int controlIndex, bool normalized, bool adjusted, bool teamTotalTime, SubSecond mode) const;
// Normalized = true means permuted to the unlooped version of the course // Normalized = true means permuted to the unlooped version of the course
int getPunchTime(int controlNumber, bool normalized, bool adjusted) const; const wstring &getSplitTimeS(int controlNumber, bool normalized, SubSecond mode) const;
wstring getPunchTimeS(int controlNumber, bool normalized, bool adjusted, SubSecond mode) const;
// Normalized = true means permuted to the unlooped version of the course
wstring getSplitTimeS(int controlNumber, bool normalized) const;
void addTableRow(Table &table) const; void addTableRow(Table &table) const;
pair<int, bool> inputData(int id, const wstring &input, pair<int, bool> inputData(int id, const wstring &input,

View File

@ -436,6 +436,17 @@ int oTeam::getLegFinishTime(int leg) const
else return 0; else return 0;
} }
int oTeam::getTotalRunningTimeAtLegStart(int leg, bool multidayTotal) const {
int off = multidayTotal ? max(0, getInputTime()) : 0;
if (!Class || leg == 0)
return off;
int pleg = Class->getPreceedingLeg(leg);
if (pleg < 0)
return off;
return getLegRunningTime(pleg, false, multidayTotal);
}
int oTeam::getRunningTime(bool computedTime) const { int oTeam::getRunningTime(bool computedTime) const {
return getLegRunningTime(-1, computedTime, false); return getLegRunningTime(-1, computedTime, false);
} }
@ -812,9 +823,7 @@ wstring oTeam::getLegPrintPlaceS(int leg, bool multidayTotal, bool withDot) cons
return _EmptyWString; return _EmptyWString;
} }
bool oTeam::compareResultClub(const oTeam& a, const oTeam& b) { int oAbstractRunner::compareClubs(const oClub* ca, const oClub* cb) {
pClub ca = a.getClubRef();
pClub cb = b.getClubRef();
if (ca != cb) { if (ca != cb) {
if (ca == nullptr && cb) if (ca == nullptr && cb)
return true; return true;
@ -830,6 +839,17 @@ bool oTeam::compareResultClub(const oTeam& a, const oTeam& b) {
if (res != CSTR_EQUAL) if (res != CSTR_EQUAL)
return res == CSTR_LESS_THAN; return res == CSTR_LESS_THAN;
} }
return 2;
}
bool oTeam::compareResultClub(const oTeam& a, const oTeam& b) {
pClub ca = a.getClubRef();
pClub cb = b.getClubRef();
if (ca != cb) {
int cres = compareClubs(ca, cb);
if (cres != 2)
return cres != 0;
}
return compareResult(a, b); return compareResult(a, b);
} }
@ -855,8 +875,13 @@ bool oTeam::compareResult(const oTeam &a, const oTeam &b)
int aix = a.getDCI().getInt("SortIndex"); int aix = a.getDCI().getInt("SortIndex");
int bix = b.getDCI().getInt("SortIndex"); int bix = b.getDCI().getInt("SortIndex");
if (aix != bix) if (aix != bix) {
if (aix == 0)
aix = numeric_limits<int>::max();
if (bix == 0)
bix = numeric_limits<int>::max();
return aix < bix; return aix < bix;
}
return CompareString(LOCALE_USER_DEFAULT, 0, return CompareString(LOCALE_USER_DEFAULT, 0,
a.sName.c_str(), a.sName.length(), a.sName.c_str(), a.sName.length(),
@ -877,6 +902,24 @@ bool oTeam::compareResultNoSno(const oTeam &a, const oTeam &b)
else if (a.tmpSortTime != b.tmpSortTime) else if (a.tmpSortTime != b.tmpSortTime)
return a.tmpSortTime<b.tmpSortTime; return a.tmpSortTime<b.tmpSortTime;
int aix = a.getDCI().getInt("SortIndex");
int bix = b.getDCI().getInt("SortIndex");
if (aix != bix) {
if (aix == 0)
aix = numeric_limits<int>::max();
if (bix == 0)
bix = numeric_limits<int>::max();
return aix < bix;
}
pClub ca = a.getClubRef();
pClub cb = b.getClubRef();
if (ca != cb) {
int cres = compareClubs(ca, cb);
if (cres != 2)
return cres != 0;
}
return CompareString(LOCALE_USER_DEFAULT, 0, return CompareString(LOCALE_USER_DEFAULT, 0,
a.sName.c_str(), a.sName.length(), a.sName.c_str(), a.sName.length(),
b.sName.c_str(), b.sName.length()) == CSTR_LESS_THAN; b.sName.c_str(), b.sName.length()) == CSTR_LESS_THAN;
@ -1751,7 +1794,7 @@ pRunner oTeam::getRunner(unsigned leg) const {
if (leg==-1) if (leg==-1)
leg=Runners.size()-1; leg=Runners.size()-1;
return leg<Runners.size() ? Runners[leg] : 0; return leg<Runners.size() ? Runners[leg] : nullptr;
} }
int oTeam::getRogainingPoints(bool computed, bool multidayTotal) const { int oTeam::getRogainingPoints(bool computed, bool multidayTotal) const {

View File

@ -269,6 +269,9 @@ public:
int getLegRunningTime(int leg, bool computed, bool multidayTotal) const; int getLegRunningTime(int leg, bool computed, bool multidayTotal) const;
// Get the team's total running time when starting specified leg
int getTotalRunningTimeAtLegStart(int leg, bool multidayTotal) const;
RunnerStatus getLegStatus(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; const wstring &getLegStatusS(int leg, bool computed, bool multidayTotal) const;

View File

@ -240,7 +240,7 @@ pTeam oEvent::findTeam(const wstring &s, int lastId, unordered_set<int> &filter)
int len = trm.length(); int len = trm.length();
wchar_t s_lc[1024]; wchar_t s_lc[1024];
wcscpy_s(s_lc, trm.c_str()); wcscpy_s(s_lc, trm.c_str());
CharLowerBuff(s_lc, len); prepareMatchString(s_lc, len);
int sn = _wtoi(s.c_str()); int sn = _wtoi(s.c_str());
oTeamList::const_iterator it; oTeamList::const_iterator it;

View File

@ -478,7 +478,7 @@ void OnlineInput::processEntries(oEvent &oe, const xmlList &entries) {
bool paid = entry.getObjectBool("paid"); bool paid = entry.getObjectBool("paid");
xmlobject xname = entry.getObject("name"); xmlobject xname = entry.getObject("name");
wstring birthyear = 0; wstring birthyear;
if (xname) { if (xname) {
xname.getObjectString("birthyear", birthyear); xname.getObjectString("birthyear", birthyear);
} }

View File

@ -17,6 +17,7 @@
#define IDI_SPLASHIMAGE 512 #define IDI_SPLASHIMAGE 512
#define IDI_MEOSIMAGE 513 #define IDI_MEOSIMAGE 513
#define IDI_MEOSINFO 514 #define IDI_MEOSINFO 514
#define IDI_MEOSEDIT 515
// Next default values for new objects // Next default values for new objects
// //

View File

@ -1150,7 +1150,7 @@ void RestServer::lookup(oEvent &oe, const string &what, const multimap<string, s
auto &sd = r->getSplitTimes(false); auto &sd = r->getSplitTimes(false);
vector<int> after; vector<int> after;
r->getLegTimeAfter(after); r->getLegTimeAfter(after);
vector<int> afterAcc; vector<oRunner::ResultData> afterAcc;
r->getLegTimeAfterAcc(afterAcc); r->getLegTimeAfterAcc(afterAcc);
vector<int> delta; vector<int> delta;
r->getSplitAnalysis(delta); r->getSplitAnalysis(delta);
@ -1177,8 +1177,8 @@ void RestServer::lookup(oEvent &oe, const string &what, const multimap<string, s
else else
analysis[0].second = L""; analysis[0].second = L"";
if (afterAcc[ix] > 0) if (afterAcc[ix].get(0) > 0)
analysis[1].second = formatTime(afterAcc[ix]); analysis[1].second = formatTime(afterAcc[ix].get(false));
else else
analysis[1].second = L""; analysis[1].second = L"";
@ -1190,7 +1190,7 @@ void RestServer::lookup(oEvent &oe, const string &what, const multimap<string, s
int place = r->getLegPlace(ix); int place = r->getLegPlace(ix);
analysis[3].second = place > 0 ? itow(place) : L""; analysis[3].second = place > 0 ? itow(place) : L"";
int placeAcc = r->getLegPlaceAcc(ix); int placeAcc = r->getLegPlaceAcc(ix, false);
analysis[4].second = placeAcc > 0 ? itow(placeAcc) : L""; analysis[4].second = placeAcc > 0 ? itow(placeAcc) : L"";
xml.write("Analysis", analysis, L""); xml.write("Analysis", analysis, L"");

View File

@ -2683,3 +2683,8 @@ Bevara höjd/bredd-relationen = Bevara höjd/bredd-relationen
RunnerLegTeamLeaderName = Först i mål på sträckan RunnerLegTeamLeaderName = Först i mål på sträckan
info:offsetclassid = Om du importerar anmälningar och klasser från olika källor till samma tävling kan det hända att klassernas Id-nummer krockar. För att hålla isär klasserna kan du då ange en förskjutning av Id-nummer när du arbetar med datafiler från en viss källa; denna kommer att adderas till klassernas Id-nummer.\n\nDu måste ange samma förskjutning varje gång du importerar från en viss källa. Ett lämpligt värde kan vara 1000 (som fungerar om all Id-nummer är mindre än 1000). info:offsetclassid = Om du importerar anmälningar och klasser från olika källor till samma tävling kan det hända att klassernas Id-nummer krockar. För att hålla isär klasserna kan du då ange en förskjutning av Id-nummer när du arbetar med datafiler från en viss källa; denna kommer att adderas till klassernas Id-nummer.\n\nDu måste ange samma förskjutning varje gång du importerar från en viss källa. Ett lämpligt värde kan vara 1000 (som fungerar om all Id-nummer är mindre än 1000).
Förskjutning av klassers Id = Förskjutning av klassers Id Förskjutning av klassers Id = Förskjutning av klassers Id
Tilldela nummerlapp till vakanter = Tilldela nummerlapp till vakanter
Avläsning = Avläsning
Inmatning Testning = Inmatning Testning
Testning = Testning
CourseNumControls = Antal kontroller