diff --git a/code/RestService.cpp b/code/RestService.cpp index 80dd277..ec7baa0 100644 --- a/code/RestService.cpp +++ b/code/RestService.cpp @@ -96,7 +96,7 @@ void RestService::settings(gdioutput &gdi, oEvent &oe, bool created) { bool disablePermisson = true; gdi.popX(); - gdi.addCheckbox("MapRoot", "Mappa rootadresssen (http:///localhost:port/) till funktion:", nullptr, !rootMap.empty()).setHandler(this); + gdi.addCheckbox("MapRoot", "Mappa rootadressen (http:///localhost:port/) till funktion:", nullptr, !rootMap.empty()).setHandler(this); gdi.addInput("RootMap", gdi.recodeToWide(rootMap)); gdi.setInputStatus("RootMap", !rootMap.empty()); diff --git a/code/RunnerDB.cpp b/code/RunnerDB.cpp index 224318f..b5259de 100644 --- a/code/RunnerDB.cpp +++ b/code/RunnerDB.cpp @@ -48,8 +48,24 @@ RunnerDB::RunnerDB(oEvent *oe_): oe(oe_) loadedFromServer = false; dataDate = 20100201; dataTime = 222222; - runnerTable = 0; - clubTable = 0; +} + +RunnerDB::RunnerDB(const RunnerDB &in) : oe(in.oe) { + loadedFromServer = false; + dataDate = in.dataDate; + dataTime = in.dataTime; + + rdb = in.rdb; + rwdb = in.rwdb; + for (size_t k = 0; k < rwdb.size(); k++) + rwdb[k].init(this, k); + + rhash = in.rhash; + + cdb = in.cdb; + oRDB = in.oRDB; + chash = in.chash; + freeCIx = in.freeCIx; } RunnerDB::~RunnerDB(void) @@ -57,6 +73,15 @@ RunnerDB::~RunnerDB(void) releaseTables(); } +bool RunnerDB::operator!=(const RunnerDB &in) const { +return dataDate != in.dataDate || + dataTime != in.dataTime || + freeCIx != in.freeCIx || + rdb.size() != in.rdb.size() || + cdb.size() != in.cdb.size() || + (!rdb.empty() && !(rdb.back() == in.rdb.back())); +} + RunnerDBEntry::RunnerDBEntry() { memset(this, 0, sizeof(RunnerDBEntry)); @@ -268,8 +293,8 @@ RunnerWDBEntry *RunnerDB::addRunner(const wchar_t *name, int club, int card) { assert(rdb.size() == rwdb.size()); - rdb.push_back(RunnerDBEntry()); - rwdb.push_back(RunnerWDBEntry()); + rdb.emplace_back(); + rwdb.emplace_back(); rwdb.back().init(this, rdb.size()-1); RunnerWDBEntry &e=rwdb.back(); @@ -290,7 +315,7 @@ RunnerWDBEntry *RunnerDB::addRunner(const wchar_t *name, if (!idhash.empty()) idhash[extId] = rdb.size()-1; if (!nhash.empty()) - nhash.insert(pair(canonizeName(e.name), rdb.size()-1)); + nhash.emplace(canonizeName(e.name), rdb.size()-1); } return &e; } @@ -301,8 +326,8 @@ RunnerWDBEntry *RunnerDB::addRunner(const char *nameUTF, int club, int card) { assert(rdb.size() == rwdb.size()); - rdb.push_back(RunnerDBEntry()); - rwdb.push_back(RunnerWDBEntry()); + rdb.emplace_back(); + rwdb.emplace_back(); rwdb.back().init(this, rdb.size()-1); RunnerWDBEntry &e=rwdb.back(); @@ -325,7 +350,7 @@ RunnerWDBEntry *RunnerDB::addRunner(const char *nameUTF, if (!nhash.empty()) { wstring wn; e.getName(wn); - nhash.insert(pair(canonizeName(wn.c_str()), rdb.size()-1)); + nhash.emplace(canonizeName(wn.c_str()), rdb.size()-1); } } return &e; @@ -406,7 +431,7 @@ void RunnerDB::importClub(oClub &club, bool matchName) else { // Completely new club chash [club.getId()] = cdb.size(); - cdb.push_back(oDBClubEntry(club, cdb.size(), this)); + cdb.emplace_back(club, cdb.size(), this); } } @@ -1139,17 +1164,41 @@ void RunnerDB::fillClubs(vector< pair > &out) const { out.reserve(cdb.size()); for (size_t k = 0; k &RunnerDB::getRunnerTB() { table->addColumn("Nationalitet", 70, false, true); table->addColumn("Kön", 50, false, true); table->addColumn("Födelseår", 70, true, true); - table->addColumn("Anmäl", 70, false, true); + table->addColumn("Anmäl", 120, false, true); table->setTableProp(Table::CAN_INSERT|Table::CAN_DELETE|Table::CAN_PASTE); table->setClearOnHide(false); @@ -1224,6 +1273,25 @@ const shared_ptr &RunnerDB::getRunnerTB() { if (runnerTable->getNumDataRows() != nr) runnerTable->update(); + else { + /*vector runners; + setupIdHash(); + oe->getRunners(0, 0, runners, false); + for (pRunner r : runners) { + int64_t extId = r->getExtIdentifier(); + if (extId != 0) { + int value; + if (idhash.lookup(extId, value)) { + try { + runnerTable->reloadRow(value + 1); + } + catch (const std::exception &) { + // Ignore any problems with the table. + } + } + } + }*/ + } return runnerTable; } @@ -1259,15 +1327,15 @@ void RunnerDB::refreshRunnerTableData(Table &table) { if (row) { row->setObject(oRDB[k]); - oClass *val = 0; + int runnerId; bool found = false; if (rdb[k].extId != 0) - found = runnerInEvent.lookup(rdb[k].extId, val); + found = runnerInEvent.lookup(rdb[k].extId, runnerId); - - if (found && row->getCellType(cellEntryIndex) == cellAction) { - row->updateCell(cellEntryIndex, cellEdit, val->getName()); + if (found) { + pRunner r = oe->getRunner(runnerId, 0); + row->updateCell(cellEntryIndex, cellEdit, r ? r->getClass(true) : L""); } else if (!found && row->getCellType(cellEntryIndex) == cellEdit) { row->updateCell(cellEntryIndex, cellAction, L"@+"); @@ -1343,11 +1411,11 @@ void oDBRunnerEntry::addTableRow(Table &table) const { table.set(row++, it, TID_SEX, sex, canEdit, cellEdit); table.set(row++, it, TID_YEAR, itow(rn.birthYear), canEdit, cellEdit); - oClass *val = 0; + int runnerId; bool found = false; if (rn.extId != 0) - found = db->runnerInEvent.lookup(rn.extId, val); + found = db->runnerInEvent.lookup(rn.extId, runnerId); if (canEdit) table.setTableProp(Table::CAN_DELETE|Table::CAN_INSERT|Table::CAN_PASTE); @@ -1357,8 +1425,10 @@ void oDBRunnerEntry::addTableRow(Table &table) const { RunnerDB::cellEntryIndex = row; if (!found) table.set(row++, it, TID_ENTER, L"@+", false, cellAction); - else - table.set(row++, it, TID_ENTER, val ? val->getName() : L"", false, cellEdit); + else { + pRunner r = oe->getRunner(runnerId, 0); + table.set(row++, it, TID_ENTER, r ? r->getClass(true) : L"", false, cellEdit); + } } const RunnerDBEntry &oDBRunnerEntry::getRunner() const { @@ -1427,7 +1497,7 @@ void oDBRunnerEntry::fillInput(int id, vector< pair > &out, siz RunnerDBEntry &r = db->rdb[index]; if (id==TID_CLUB) { db->fillClubs(out); - out.push_back(make_pair(L"-", 0)); + out.emplace_back(L"-", 0); selected = r.clubNo; } } @@ -1460,11 +1530,11 @@ bool oDBRunnerEntry::canRemove() const { } oDBRunnerEntry *RunnerDB::addRunner() { - rdb.push_back(RunnerDBEntry()); - rwdb.push_back(RunnerWDBEntry()); + rdb.emplace_back(); + rwdb.emplace_back(); rwdb.back().init(this, rdb.size() -1); - oRDB.push_back(oDBRunnerEntry(oe)); + oRDB.emplace_back(oe); oRDB.back().init(this, rdb.size() - 1); return &oRDB.back(); @@ -1475,7 +1545,7 @@ oClub *RunnerDB::addClub() { while (chash.count(freeCIx)) freeCIx++; - cdb.push_back(oDBClubEntry(oe, freeCIx, cdb.size(), this)); + cdb.emplace_back(oe, freeCIx, cdb.size(), this); chash.insert(freeCIx, cdb.size()-1); cnhash.clear(); diff --git a/code/RunnerDB.h b/code/RunnerDB.h index ec6d8bd..5fb2c39 100644 --- a/code/RunnerDB.h +++ b/code/RunnerDB.h @@ -107,6 +107,10 @@ struct RunnerDBEntry { bool isUTF() const { return (reserved & 2) == 2; } void setUTF() { reserved |= 2; } + + bool operator==(const RunnerDBEntry &d) const { + return memcmp(this, &d, sizeof(RunnerDBEntry)) == 0; + } }; class RunnerDB; @@ -168,7 +172,7 @@ private: bool check(const RunnerDBEntry &rde) const; - intkeymap runnerInEvent; + intkeymap runnerInEvent; /** Init name hash lazy */ void setupNameHash() const; @@ -345,6 +349,9 @@ public: void getAllNames(vector &givenName, vector &familyName); RunnerDB(oEvent *); + RunnerDB(const RunnerDB &in); + bool operator!=(const RunnerDB &other) const; + ~RunnerDB(void); friend class oDBRunnerEntry; friend class oDBClubEntry; @@ -373,6 +380,12 @@ public: void fillInput(int id, vector< pair > &out, size_t &selected) override; oDBRunnerEntry(oEvent *oe); + oDBRunnerEntry(oDBRunnerEntry &&in); + oDBRunnerEntry(const oDBRunnerEntry &in); + + const oDBRunnerEntry &operator=(oDBRunnerEntry &&in); + const oDBRunnerEntry &operator=(const oDBRunnerEntry &in); + virtual ~oDBRunnerEntry(); void remove(); diff --git a/code/TabAuto.cpp b/code/TabAuto.cpp index c276fe3..0a96136 100644 --- a/code/TabAuto.cpp +++ b/code/TabAuto.cpp @@ -366,7 +366,7 @@ int TabAuto::processButton(gdioutput &gdi, const ButtonInfo &bu) else par.setLegNumberCoded(0); - oe->generateListInfo(par, gdi.getLineHeight(), prm->listInfo); + oe->generateListInfo(par, prm->listInfo); } } prm->po.onlyChanged = gdi.isChecked("OnlyChanged"); diff --git a/code/TabClass.cpp b/code/TabClass.cpp index 2f40c2b..cd9e992 100644 --- a/code/TabClass.cpp +++ b/code/TabClass.cpp @@ -913,7 +913,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) readClassSettings(gdi); oEvent::DrawMethod method = (oEvent::DrawMethod)gdi.getSelectedItem("Method").first; int pairSize = gdi.getSelectedItem("PairSize").first; - + auto vp = readVacantPosition(gdi); bool drawCoursebased = drawInfo.coursesTogether; int maxST = 0; @@ -923,7 +923,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) const ClassInfo &ci=cInfo[k]; ClassDrawSpecification cds(ci.classId, 0, drawInfo.firstStart + drawInfo.baseInterval * ci.firstStart, - drawInfo.baseInterval * ci.interval, ci.nVacant); + drawInfo.baseInterval * ci.interval, ci.nVacant, vp); if (drawCoursebased) { pCourse pCrs = oe->getClass(ci.classId)->getCourse(); int id = pCrs ? pCrs->getId() : 101010101 + ci.classId; @@ -954,7 +954,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) for (size_t k=0; kgenerateListInfo(par, gdi.getLineHeight(), info); + oe->generateListInfo(par, info); oe->generateList(gdi, false, info, true); gdi.refresh(); } @@ -1013,8 +1013,10 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) gdi.fillRight(); gdi.addInput("FirstStart", oe->getAbsTime(3600), 10, 0, L"Första (ordinarie) start:"); gdi.addInput("MinInterval", L"2:00", 10, 0, L"Minsta startintervall:"); - gdi.fillDown(); gdi.addInput("Vacances", getDefaultVacant(), 10, 0, L"Andel vakanser:"); + gdi.fillDown(); + addVacantPosition(gdi); + gdi.popX(); createDrawMethod(gdi); @@ -1134,7 +1136,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) oListInfo info; par.listCode = EStdStartList; - oe->generateListInfo(par, gdi.getLineHeight(), info); + oe->generateListInfo(par, info); oe->generateList(gdi, false, info, true); gdi.dropLine(); gdi.addButton("Cancel", "Återgå", ClassesCB); @@ -1151,6 +1153,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) return 0; wstring minInterval = gdi.getText("MinInterval"); wstring vacances = gdi.getText("Vacances"); + auto vp = readVacantPosition(gdi); setDefaultVacant(vacances); bool lateBefore = gdi.isChecked("LateBefore"); bool allowNeighbourSameCourse = gdi.isChecked("AllowNeighbours"); @@ -1160,7 +1163,6 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) if (gdi.hasWidget("PairSize")) { pairSize = gdi.getSelectedItem("PairSize").first; } - oEvent::DrawMethod method = (oEvent::DrawMethod)gdi.getSelectedItem("Method").first; int baseInterval = convertAbsoluteTimeMS(minInterval) / 2; @@ -1174,7 +1176,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) throw meosException("Ogiltig första starttid. Måste vara efter nolltid."); clearPage(gdi, true); - oe->automaticDrawAll(gdi, firstStart, minInterval, vacances, + oe->automaticDrawAll(gdi, firstStart, minInterval, vacances, vp, lateBefore, allowNeighbourSameCourse, method, pairSize); oe->addAutoBib(); gdi.scrollToBottom(); @@ -1203,13 +1205,16 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) showClassSelection(gdi, bx, by, 0); gdi.pushX(); + + gdi.fillRight(); gdi.addInput("FirstStart", firstStart, 10, 0, L"Starttid:"); + gdi.addInput("Vacanses", lastNumVac, 10, 0, L"Antal vakanser:").setSynchData(&lastNumVac); gdi.dropLine(4); gdi.popX(); gdi.fillRight(); gdi.addButton("AssignStart", "Tilldela", ClassesCB).isEdit(true); - gdi.addButton("Cancel", "Avbryt", ClassesCB).setCancel(); + gdi.addButton("Cancel", "Återgå", ClassesCB).setCancel(); gdi.addButton("EraseStartAll", "Radera starttider...", ClassesCB).isEdit(true).setExtra(1); gdi.refresh(); @@ -1226,8 +1231,10 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) if (warnDrawStartTime(gdi, time)) return 0; + int nVacant = gdi.getTextNo("Vacanses"); + for (set::iterator it = classes.begin(); it!=classes.end();++it) { - simultaneous(*it, time); + simultaneous(*it, time, nVacant); } bi.id = "Simultaneous"; @@ -1412,7 +1419,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) for (set::const_iterator it = classes.begin(); it != classes.end(); ++it) { vector spec; - spec.push_back(ClassDrawSpecification(*it, 0, 0, 0, 0)); + spec.emplace_back(ClassDrawSpecification(*it, 0, 0, 0, 0, oEvent::VacantPosition::Mixed)); oe->drawList(spec, oEvent::DrawMethod::Random, 1, oEvent::DrawType::DrawAll); } @@ -1487,15 +1494,14 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) if (warnDrawStartTime(gdi, t, false)) return 0; } - //bool pairwise = false; -// if (gdi.hasWidget("Pairwise")) - // pairwise = gdi.isChecked("Pairwise"); int pairSize = 1; if (gdi.hasWidget("PairSize")) { pairSize = gdi.getSelectedItem("PairSize").first; } + auto vp = readVacantPosition(gdi); + int maxTime = 0, restartTime = 0; double scaleFactor = 1.0; @@ -1510,7 +1516,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) if (method == oEvent::DrawMethod::Random || method == oEvent::DrawMethod::SOFT || method == oEvent::DrawMethod::MeOS) { vector spec; - spec.push_back(ClassDrawSpecification(cid, leg, t, interval, vacanses)); + spec.emplace_back(cid, leg, t, interval, vacanses, vp); oe->drawList(spec, method, pairSize, dtype); } @@ -1523,7 +1529,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) scaleFactor); } else if (method == oEvent::DrawMethod::Simultaneous) { - simultaneous(cid, time); + simultaneous(cid, time, vacanses); } else if (method == oEvent::DrawMethod::Seeded) { ListBoxInfo seedMethod; @@ -1572,7 +1578,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) oListInfo info; par.listCode = EStdStartList; par.setLegNumberCoded(leg); - oe->generateListInfo(par, gdi.getLineHeight(), info); + oe->generateListInfo(par, info); oe->generateList(gdi, false, info, true); gdi.refresh(); @@ -1596,7 +1602,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) leg = gdi.getSelectedItem("Leg").first; } vector spec; - spec.push_back(ClassDrawSpecification(ClassId, leg, 0, 0, 0)); + spec.emplace_back(ClassId, leg, 0, 0, 0, oEvent::VacantPosition::Mixed); oe->drawList(spec, oEvent::DrawMethod::Random, 1, oEvent::DrawType::DrawAll); loadPage(gdi); @@ -1822,7 +1828,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) par.listCode = EStdStartList; } } - oe->generateListInfo(par, gdi.getLineHeight(), info); + oe->generateListInfo(par, info); oe->generateList(gdi, false, info, true); gdi.refresh(); @@ -1953,7 +1959,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) par.selection.insert(outClass.begin(), outClass.end()); oListInfo info; par.listCode = EStdStartList; - oe->generateListInfo(par, gdi.getLineHeight(), info); + oe->generateListInfo(par, info); oe->generateList(gdi, false, info, true); } else if (bi.id == "LockAllForks" || bi.id == "UnLockAllForks") { @@ -2069,7 +2075,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) par.selection.insert(ClassId); oListInfo info; par.listCode = EStdStartList; - oe->generateListInfo(par, gdi.getLineHeight(), info); + oe->generateListInfo(par, info); oe->generateList(gdi, false, info, true); gdi.refresh(); } @@ -2452,6 +2458,8 @@ void TabClass::showClassSettings(gdioutput &gdi) createDrawMethod(gdi); + addVacantPosition(gdi); + gdi.addSelection("PairSize", 150, 200, 0, L"Tillämpa parstart:"); gdi.addItem("PairSize", getPairOptions()); gdi.selectItemByData("PairSize", 1); @@ -3634,9 +3642,18 @@ void TabClass::drawDialog(gdioutput &gdi, oEvent::DrawMethod method, const oClas if (method != oEvent::DrawMethod::Simultaneous) gdi.addInput("Interval", formatTime(interval), 10, 0, L"Startintervall (min):").setSynchData(&lastInterval); - if ((method == oEvent::DrawMethod::Random || method == oEvent::DrawMethod::SOFT || method == oEvent::DrawMethod::Clumped || method == oEvent::DrawMethod::MeOS) && pc.getParentClass() == 0) + if ((method == oEvent::DrawMethod::Random || + method == oEvent::DrawMethod::SOFT || + method == oEvent::DrawMethod::Clumped || + method == oEvent::DrawMethod::MeOS || + method == oEvent::DrawMethod::Simultaneous) && pc.getParentClass() == 0) { gdi.addInput("Vacanses", itow(vac), 10, 0, L"Antal vakanser:").setSynchData(&lastNumVac); + if (method == oEvent::DrawMethod::SOFT || + method == oEvent::DrawMethod::Random || + method == oEvent::DrawMethod::MeOS) + addVacantPosition(gdi); + } if ((method == oEvent::DrawMethod::Random || method == oEvent::DrawMethod::SOFT || method == oEvent::DrawMethod::Seeded || method == oEvent::DrawMethod::MeOS) && pc.getNumStages() > 1 && pc.getClassType() != oClassPatrol) { gdi.addSelection("Leg", 90, 100, 0, L"Sträcka:", L"Sträcka att lotta"); for (unsigned k = 0; k < pc.getNumStages(); k++) @@ -3691,6 +3708,26 @@ void TabClass::drawDialog(gdioutput &gdi, oEvent::DrawMethod method, const oClas lastDrawMethod = method; } +void TabClass::addVacantPosition(gdioutput &gdi) { + gdi.addSelection("VacantPosition", 120, 80, nullptr, L"Vakansplacering:"); + vector> vp; + vp.emplace_back(lang.tl("Lottat"), size_t(oEvent::VacantPosition::Mixed)); + vp.emplace_back(lang.tl("Först"), size_t(oEvent::VacantPosition::First)); + vp.emplace_back(lang.tl("Sist"), size_t(oEvent::VacantPosition::Last)); + gdi.addItem("VacantPosition", vp); + int def = oe->getPropertyInt("VacantPosition", size_t(oEvent::VacantPosition::Mixed)); + gdi.selectItemByData("VacantPosition", def); +} + +oEvent::VacantPosition TabClass::readVacantPosition(gdioutput &gdi) const { + if (gdi.hasWidget("VacantPosition")) { + int val = gdi.getSelectedItem("VacantPosition").first; + oe->setProperty("VacantPosition", val); + return oEvent::VacantPosition(val); + } + return oEvent::VacantPosition::Mixed; +} + set TabClass::getSupportedDrawMethods(bool hasMulti) const { set base = { oEvent::DrawMethod::Random, oEvent::DrawMethod::SOFT, oEvent::DrawMethod::Clumped, oEvent::DrawMethod::MeOS, oEvent::DrawMethod::Simultaneous, oEvent::DrawMethod::Seeded }; @@ -3916,13 +3953,34 @@ void TabClass::enableLoadSettings(gdioutput &gdi) { } -void TabClass::simultaneous(int classId, const wstring &time) { +void TabClass::simultaneous(int classId, const wstring &time, int nVacant) { pClass pc = oe->getClass(classId); if (!pc) throw exception(); - pc->getNumStages(); + if (nVacant >= 0 && pc->getNumStages() <= 1) { + vector toRemove; + vector runners; + oe->getRunners(classId, 0, runners, true); + //Remove old vacances + for (pRunner r : runners) { + if (r->getTeam()) + continue; // Cannot remove team runners + if (r->isVacant()) { + + if (--nVacant < 0) + toRemove.push_back(r->getId()); + } + } + + oe->removeRunner(toRemove); + toRemove.clear(); + for (int i = 0; i < nVacant; i++) { + oe->addRunnerVacant(classId); + } + } + if (pc->getNumStages() == 0) { pCourse crs = pc->getCourse(); pc->setNumStages(1); diff --git a/code/TabClass.h b/code/TabClass.h index 8cf1c33..8da2c2d 100644 --- a/code/TabClass.h +++ b/code/TabClass.h @@ -79,8 +79,9 @@ class TabClass : set getSupportedDrawMethods(bool multiDay) const; void drawDialog(gdioutput &gdi, oEvent::DrawMethod method, const oClass &cls); - void pursuitDialog(gdioutput &gdi); + void addVacantPosition(gdioutput &gdi); + oEvent::VacantPosition TabClass::readVacantPosition(gdioutput &gdi) const; bool warnDrawStartTime(gdioutput &gdi, int time, bool absTime); bool warnDrawStartTime(gdioutput &gdi, const wstring &firstStart); @@ -117,7 +118,7 @@ class TabClass : void showClassSelection(gdioutput &gdi, int &bx, int &by, GUICALLBACK classesCB) const; // Set simultaneous start in a class - void simultaneous(int classId, const wstring &time); + void simultaneous(int classId, const wstring &time, int nVacant); void updateFairForking(gdioutput &gdi, pClass pc) const; void selectCourses(gdioutput &gdi, int legNo); diff --git a/code/TabClub.cpp b/code/TabClub.cpp index 718fc41..c63d578 100644 --- a/code/TabClub.cpp +++ b/code/TabClub.cpp @@ -144,12 +144,12 @@ int TabClub::clubCB(gdioutput &gdi, int type, void *data) oClub::definedPayModes(*oe, dpm); pc->generateInvoice(gdi, pay, paid, dpm, ppm); } - gdi.addButton(gdi.getWidth()+20, 15, gdi.scaleLength(120), + gdi.addButton(gdi.getWidth()+20, gdi.scaleLength(15), gdi.scaleLength(120), "Cancel", "Återgå", ClubsCB, "", true, false); - gdi.addButton(gdi.getWidth()+20, 45, gdi.scaleLength(120), + gdi.addButton(gdi.getWidth()+20, gdi.scaleLength(45), gdi.scaleLength(120), "Print", "Skriv ut...", ClubsCB, "Skriv ut fakturan", true, false); - gdi.addButton(gdi.getWidth()+20, 75, gdi.scaleLength(120), + gdi.addButton(gdi.getWidth()+20, gdi.scaleLength(75), gdi.scaleLength(120), "PDF", "PDF...", ClubsCB, "Spara som PDF.", true, false); gdi.refresh(); @@ -299,16 +299,20 @@ int TabClub::clubCB(gdioutput &gdi, int type, void *data) } else if (bi.id == "InvoiceSettings") { gdi.clearPage(true); + gdi.pushX(); gdi.addString("", boldLarge, "Fakturainställningar"); gdi.dropLine(); firstInvoice = oClub::getFirstInvoiceNumber(*oe); if (firstInvoice == 0) firstInvoice = oe->getPropertyInt("FirstInvoice", 1000); - gdi.addInput("FirstInvoice", itow(firstInvoice), 5, 0, L"Första fakturanummer:"); - - gdi.dropLine(); - gdi.addString("", boldText, "Organisatör"); + gdi.fillRight(); + gdi.addInput("InvoiceDate", oClub::getInvoiceDate(*oe), 16, nullptr, L"Fakturadatum:"); + gdi.addInput("FirstInvoice", itow(firstInvoice), 16, 0, L"Första fakturanummer:"); + gdi.fillDown(); + gdi.popX(); + gdi.dropLine(4); + gdi.addString("", fontMediumPlus, "Organisatör"); vector fields; gdi.pushY(); @@ -320,7 +324,7 @@ int TabClub::clubCB(gdioutput &gdi, int type, void *data) oe->getDI().buildDataFields(gdi, fields, 32); gdi.dropLine(); - gdi.addString("", boldText, "Betalningsinformation"); + gdi.addString("", fontMediumPlus, "Betalningsinformation"); fields.clear(); fields.push_back("Account"); fields.push_back("PaymentDue"); @@ -334,7 +338,7 @@ int TabClub::clubCB(gdioutput &gdi, int type, void *data) gdi.dropLine(2); gdi.popX(); - gdi.addString("", boldText, "Formatering"); + gdi.addString("", fontMediumPlus, "Formatering"); gdi.fillRight(); gdi.addString("", 0, "Koordinater (mm) för adressfält:"); @@ -353,12 +357,11 @@ int TabClub::clubCB(gdioutput &gdi, int type, void *data) gdi.dropLine(1); gdi.fillRight(); - gdi.addButton("SaveSettings", "Spara", ClubsCB); - gdi.addButton("Cancel", "Avbryt", ClubsCB); + gdi.addButton("SaveSettings", "Spara", ClubsCB).setDefault(); + gdi.addButton("Cancel", "Avbryt", ClubsCB).setCancel(); gdi.dropLine(2); gdi.setOnClearCb(ClubsCB); oe->getDI().fillDataFields(gdi); - } else if (bi.id == "SaveSettings") { oe->getDI().saveDataFields(gdi); @@ -376,6 +379,13 @@ int TabClub::clubCB(gdioutput &gdi, int type, void *data) else oe->setProperty("FirstInvoice", fn); + if (gdi.getText("InvoiceDate").empty()) { + gdi.setText("InvoiceDate", oClub::getInvoiceDate(*oe)); + dynamic_cast(gdi.getBaseInfo("InvoiceDate")).setBgColor(colorLightRed); + return 0; + } + oClub::setInvoiceDate(*oe, gdi.getText("InvoiceDate")); + int xc = gdi.getTextNo("XC"); int yc = gdi.getTextNo("YC"); diff --git a/code/TabCompetition.cpp b/code/TabCompetition.cpp index 2ba5af1..a4aa80c 100644 --- a/code/TabCompetition.cpp +++ b/code/TabCompetition.cpp @@ -621,7 +621,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) } else if (bi.id=="Browse") { vector< pair > ext; - ext.push_back(make_pair(lang.tl(L"Databaskälla"), L"*.xml;*.csv")); + ext.push_back(make_pair(L"Databaskälla", L"*.xml;*.csv")); wstring f = gdi.browseForOpen(ext, L"xml"); string id; @@ -1545,7 +1545,9 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) switch (startType) { case SMCommon: - oe->automaticDrawAll(gdi, formatTimeHMS(firstStart), L"0", L"0", false, false, oEvent::DrawMethod::Random, 1); + oe->automaticDrawAll(gdi, formatTimeHMS(firstStart), L"0", + L"0", oEvent::VacantPosition::Mixed, + false, false, oEvent::DrawMethod::Random, 1); drawn = true; break; @@ -1570,7 +1572,9 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) } } if (!skip) - oe->automaticDrawAll(gdi, formatTimeHMS(firstStart), L"2:00", L"2", true, false, oEvent::DrawMethod::MeOS, 1); + oe->automaticDrawAll(gdi, formatTimeHMS(firstStart), L"2:00", + L"2", oEvent::VacantPosition::Mixed, + true, false, oEvent::DrawMethod::MeOS, 1); drawn = true; break; } @@ -1766,7 +1770,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) par.setLegNumberCoded(-1); oListInfo li; par.selection = allTransfer; - oe->generateListInfo(par, gdi.getLineHeight(), li); + oe->generateListInfo(par, li); gdioutput tGdi("temp", gdi.getScale()); oe->generateList(tGdi, true, li, false); HTMLWriter::writeTableHTML(tGdi, save, oe->getName(), 0, 1.0); @@ -1851,7 +1855,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) par.showSplitTimes = true; par.setLegNumberCoded(-1); oListInfo li; - oe->generateListInfo(par, gdi.getLineHeight(), li); + oe->generateListInfo(par, li); gdioutput tGdi("temp", gdi.getScale()); oe->generateList(tGdi, true, li, false); HTMLWriter::writeTableHTML(tGdi, save, oe->getName(), 0, 1.0); @@ -3459,7 +3463,7 @@ void TabCompetition::entryForm(gdioutput &gdi, bool isGuide) { gdi.dropLine(3); } -TabCompetition::FlowOperation TabCompetition::saveEntries(gdioutput &gdi, bool removeRemoved, bool isGuide) { +FlowOperation TabCompetition::saveEntries(gdioutput &gdi, bool removeRemoved, bool isGuide) { wstring filename[5]; filename[0] = gdi.getText("FileNameCmp"); filename[1] = gdi.getText("FileNameCls"); @@ -3574,11 +3578,17 @@ int stageInfoCB(gdioutput *gdi, int type, void *data) } void mainMessageLoop(HACCEL hAccelTable, DWORD time); +FlowOperation importFilterGUI(oEvent *oe, + gdioutput & gdi, + const set& stages, + const vector &idProviders, + set & filter, + string &preferredIdProvider); -TabCompetition::FlowOperation TabCompetition::checkStageFilter(gdioutput & gdi, - const wstring & fname, - set& filter, - string &preferredIdProvider) { +FlowOperation TabCompetition::checkStageFilter(gdioutput & gdi, + const wstring & fname, + set& filter, + string &preferredIdProvider) { xmlparser xml; xml.read(fname); xmlobject xo = xml.getObject("EntryList"); @@ -3591,6 +3601,16 @@ TabCompetition::FlowOperation TabCompetition::checkStageFilter(gdioutput & gdi, reader.getIdTypes(idProviders); } } + return importFilterGUI(oe, gdi, scanFilter, idProviders, filter, preferredIdProvider); +} + +FlowOperation importFilterGUI(oEvent *oe, + gdioutput & gdi, + const set& stages, + const vector &idProviders, + set & filter, + string &preferredIdProvider) { + auto &scanFilter = stages; bool stageFilter = scanFilter.size() > 1; bool idtype = idProviders.size() > 1; @@ -3631,7 +3651,7 @@ TabCompetition::FlowOperation TabCompetition::checkStageFilter(gdioutput & gdi, gdi.fillRight(); gdi.addSelection("IdType", 150, 200, stageInfoCB, L"Välj vilken typ du vill importera:"); int i = 0; - for (string &sn : idProviders) { + for (const string &sn : idProviders) { gdi.addItem("IdType", gdi.widen(sn), i++); } } diff --git a/code/TabCompetition.h b/code/TabCompetition.h index 8689d70..465db29 100644 --- a/code/TabCompetition.h +++ b/code/TabCompetition.h @@ -22,7 +22,7 @@ ************************************************************************/ #include "tabbase.h" - +#include "gdiconstants.h" #include "oFreeImport.h" class PrefsEditor; @@ -31,12 +31,6 @@ class ImportFormats; class TabCompetition : public TabBase { - enum FlowOperation { - FlowContinue, - FlowCancel, - FlowAborted - }; - wstring eventorBase; wstring iofExportVersion; void textSizeControl(gdioutput &gdi) const; diff --git a/code/TabCourse.cpp b/code/TabCourse.cpp index 67bc4e4..7ebd275 100644 --- a/code/TabCourse.cpp +++ b/code/TabCourse.cpp @@ -510,7 +510,7 @@ int TabCourse::courseCB(gdioutput &gdi, int type, void *data) if (!gdi.ask(L"warning:drawresult")) return 0; } - courseDrawClasses.push_back(ClassDrawSpecification(cls[k]->getId(), 0, -1, -1, 0)); + courseDrawClasses.emplace_back(cls[k]->getId(), 0, -1, -1, 0, oEvent::VacantPosition::Mixed); if (!clsNames.empty()) clsNames += L", "; clsNames += cls[k]->getName(); @@ -586,7 +586,7 @@ int TabCourse::courseCB(gdioutput &gdi, int type, void *data) for (size_t k=0; kgenerateListInfo(par, gdi.getLineHeight(), info); + oe->generateListInfo(par, info); oe->generateList(gdi, false, info, true); gdi.refresh(); } diff --git a/code/TabList.cpp b/code/TabList.cpp index f27d06f..56f3d9b 100644 --- a/code/TabList.cpp +++ b/code/TabList.cpp @@ -449,7 +449,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) if (gdi.getSelectedItem("SavedInstance", lbi)) { oListParam &par = oe->getListContainer().getParam(lbi.data); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); currentList.getParam().sourceParam = lbi.data; generateList(gdi); } @@ -646,7 +646,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) lastSplitState = par.showSplitTimes; lastLargeSize = par.useLargeSize; - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); generateList(gdi); gdi.refresh(); @@ -676,7 +676,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) cnf.getIndividual(par.selection, false); readSettings(gdi, par, true); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); generateList(gdi); gdi.refresh(); } @@ -692,7 +692,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) par.showSplitTimes = true; par.setLegNumberCoded(-1); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); generateList(gdi); gdi.refresh(); } @@ -704,7 +704,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) getStartIndividual(par, cnf); readSettings(gdi, par, false); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); gdi.refresh(); @@ -715,7 +715,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) getStartClub(par); readSettings(gdi, par, false); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); gdi.refresh(); @@ -730,7 +730,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) readSettings(gdi, par, false); par.splitAnalysis = gdi.isChecked("SplitAnalysis"); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); gdi.refresh(); @@ -762,7 +762,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) getStartTeam(par, cnf); readSettings(gdi, par, false); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); gdi.refresh(); @@ -778,7 +778,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); cnf.getRaceNStart(race, par.selection); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); gdi.refresh(); @@ -794,7 +794,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); cnf.getLegNStart(race, par.selection); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); gdi.refresh(); @@ -806,7 +806,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) oe->getClassConfigurationInfo(cnf); getResultTeam(par, cnf); readSettings(gdi, par, true); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); generateList(gdi); gdi.refresh(); } @@ -818,7 +818,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); cnf.getRaceNRes(0, par.selection); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); generateList(gdi); gdi.refresh(); } @@ -833,7 +833,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); cnf.getRaceNRes(race, par.selection); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); gdi.refresh(); @@ -848,7 +848,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); cnf.getLegNRes(race, par.selection); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); gdi.refresh(); @@ -861,7 +861,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) getResultRogaining(par, cnf); readSettings(gdi, par, true); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); gdi.refresh(); @@ -891,7 +891,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) cnf.getIndividual(par.back().selection, true); } - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); generateList(gdi); gdi.refresh(); } @@ -902,7 +902,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) par.listCode = EStdRentedCard; par.showHeader = gdi.isChecked("ShowHeader"); par.setLegNumberCoded(-1); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); generateList(gdi); gdi.refresh(); } @@ -914,7 +914,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) par.listCode = EIndPriceList; par.showHeader = gdi.isChecked("ShowHeader"); par.filterMaxPer = gdi.getSelectedItem("ClassLimit").first; - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); generateList(gdi); gdi.refresh(); } @@ -984,7 +984,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) cnf.getPatrol(par.selection); } - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); gdi.refresh(); @@ -999,7 +999,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); par.selection = set(cnf.knockout.begin(), cnf.knockout.end()); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); gdi.refresh(); @@ -1019,7 +1019,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) else par.selection = set(cnf.lapcountsingle.begin(), cnf.lapcountsingle.end()); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); gdi.refresh(); @@ -1043,7 +1043,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) if (oListInfo::addTeams(type)) cnf.getTeamClass(par.selection); - oe->generateListInfo(par, gdi.getLineHeight(), currentList); + oe->generateListInfo(par, currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); gdi.refresh(); @@ -1965,6 +1965,7 @@ void TabList::htmlSettings(gdioutput &gdi, string targetTag) { html2IdToInfo[id] = t.desc; } gdi.addSelection("Format", 200, 100, 0, L"Format:").setHandler(&htmlClass); + gdi.autoGrow("Format"); if (!htmlTemplateTag2Id.count(tmpSettingsParam.htmlTypeTag)) tmpSettingsParam.htmlTypeTag = "free"; diff --git a/code/TabRunner.cpp b/code/TabRunner.cpp index 6e2efb6..91593b2 100644 --- a/code/TabRunner.cpp +++ b/code/TabRunner.cpp @@ -1944,7 +1944,7 @@ void TabRunner::showVacancyList(gdioutput &gdi, const string &method, int classI par.selection.insert(classId); oListInfo info; par.listCode = EStdStartList; - oe->generateListInfo(par, gdi.getLineHeight(), info); + oe->generateListInfo(par, info); oe->generateList(gdi, false, info, true); } } diff --git a/code/TabSI.cpp b/code/TabSI.cpp index 6eb9974..f66475d 100644 --- a/code/TabSI.cpp +++ b/code/TabSI.cpp @@ -944,6 +944,11 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) di.setString("Phone", gdi.getText("Phone")); + if (gdi.isChecked("NoTiming")) + r->setStatus(RunnerStatus::StatusNoTiming, true, oBase::ChangeType::Update, false); + else if (r->getStatus() == RunnerStatus::StatusNoTiming) + r->setStatus(RunnerStatus::StatusUnknown, true, oBase::ChangeType::Update, false); + r->setFlag(oRunner::FlagTransferSpecified, gdi.hasWidget("AllStages")); r->setFlag(oRunner::FlagTransferNew, gdi.isChecked("AllStages")); @@ -2938,9 +2943,11 @@ void TabSI::generateEntryLine(gdioutput &gdi, pRunner r) { gdi.setCX(gdi.getCX()+gdi.scaleLength(20)); gdi.addCheckbox("RentCard", "Hyrbricka", SportIdentCB, storedInfo.rentState); + gdi.addCheckbox("NoTiming", "Utan tidtagning", nullptr, false); + if (oe->hasNextStage()) gdi.addCheckbox("AllStages", "Anmäl till efterföljande etapper", SportIdentCB, storedInfo.allStages); - + if (r!=0) { if (r->getCardNo()>0) gdi.setText("CardNo", r->getCardNo()); @@ -2955,9 +2962,10 @@ void TabSI::generateEntryLine(gdioutput &gdi, pRunner r) { if (gdi.hasWidget("Fee")) gdi.setText("Fee", oe->formatCurrency(dci.getInt("Fee"))); + gdi.setText("StartTime", r->getStartTimeS()); gdi.setText("Phone", dci.getString("Phone")); gdi.setText("Bib", r->getBib()); - + gdi.check("NoTiming", r->hasFlag(oAbstractRunner::TransferFlags::FlagNoTiming)); gdi.check("RentCard", dci.getInt("CardFee") != 0); if (gdi.hasWidget("Paid")) gdi.check("Paid", dci.getInt("Paid")>0); diff --git a/code/TabTeam.cpp b/code/TabTeam.cpp index 36f6cb9..35cbf23 100644 --- a/code/TabTeam.cpp +++ b/code/TabTeam.cpp @@ -1215,8 +1215,9 @@ void TabTeam::loadTeamMembers(gdioutput &gdi, int ClassId, int ClubId, pTeam t) int numberPos = xp; xp += gdi.scaleLength(25); int dx[6] = {0, 184, 220, 290, 316, 364}; - for (int i = 0; i<6; i++) - dx[i] = gdi.scaleLength(dx[i]); + dx[1] = gdi.getInputDimension(18).first + gdi.scaleLength(4); + for (int i = 2; i<6; i++) + dx[i] = dx[1] + gdi.scaleLength(dx[i]-188); gdi.addString("", yp, xp + dx[0], 0, "Namn:"); gdi.addString("", yp, xp + dx[2], 0, "Bricka:"); @@ -1224,38 +1225,36 @@ void TabTeam::loadTeamMembers(gdioutput &gdi, int ClassId, int ClubId, pTeam t) gdi.addString("", yp, xp + dx[5], 0, "Status:"); gdi.dropLine(0.5); - for (unsigned i=0;igetNumStages();i++) { - yp = gdi.getCY(); + for (unsigned i = 0; i < pc->getNumStages(); i++) { + yp = gdi.getCY() - gdi.scaleLength(3); sprintf_s(bf, "R%d", i); gdi.pushX(); bool hasSI = false; gdi.addStringUT(yp, numberPos, 0, pc->getLegNumber(i) + L"."); - if (pc->getLegRunner(i)==i) { + if (pc->getLegRunner(i) == i) { gdi.addInput(xp + dx[0], yp, bf, L"", 18, TeamCB);//Name - gdi.addButton(xp + dx[1], yp-2, gdi.scaleLength(28), "DR" + itos(i), "<>", TeamCB, "Knyt löpare till sträckan.", false, false); // Change + gdi.addButton(xp + dx[1], yp - 2, gdi.scaleLength(28), "DR" + itos(i), "<>", TeamCB, "Knyt löpare till sträckan.", false, false); // Change sprintf_s(bf_si, "SI%d", i); hasSI = true; gdi.addInput(xp + dx[2], yp, bf_si, L"", 5, TeamCB).setExtra(i); //Si - gdi.addCheckbox(xp + dx[3], yp + gdi.scaleLength(10), "RENT"+itos(i), "", 0, false); //Rentcard + gdi.addCheckbox(xp + dx[3], yp + gdi.scaleLength(10), "RENT" + itos(i), "", 0, false); //Rentcard } else { - //gdi.addInput(bf, "", 24); gdi.addInput(xp + dx[0], yp, bf, L"", 18, 0);//Name gdi.disableInput(bf); } - gdi.addButton(xp + dx[4], yp-2, gdi.scaleLength(38), "MR" + itos(i), "...", TeamCB, "Redigera deltagaren.", false, false); // Change + gdi.addButton(xp + dx[4], yp - 2, gdi.scaleLength(38), "MR" + itos(i), "...", TeamCB, "Redigera deltagaren.", false, false); // Change - gdi.addString(("STATUS"+itos(i)).c_str(), yp+gdi.scaleLength(5), xp + dx[5], 0, "#MMMMMMMMMMMMMMMM"); - gdi.setText("STATUS"+itos(i), L"", false); + gdi.addString(("STATUS" + itos(i)).c_str(), yp + gdi.scaleLength(5), xp + dx[5], 0, "#MMMMMMMMMMMMMMMM"); + gdi.setText("STATUS" + itos(i), L"", false); gdi.dropLine(0.5); gdi.popX(); - if (t) { - pRunner r=t->getRunner(i); + pRunner r = t->getRunner(i); if (r) { gdi.setText(bf, r->getNameRaw())->setExtra(r->getId()); @@ -1265,7 +1264,7 @@ void TabTeam::loadTeamMembers(gdioutput &gdi, int ClassId, int ClubId, pTeam t) warnDuplicateCard(gdi, bf_si, cno, r); gdi.check("RENT" + itos(i), r->getDCI().getInt("CardFee") != 0); } - string sid = "STATUS"+itos(i); + string sid = "STATUS" + itos(i); if (r->statusOK(true)) { TextInfo * ti = (TextInfo *)gdi.setText(sid, L"OK, " + r->getRunningTimeS(true), false); if (ti) diff --git a/code/Table.cpp b/code/Table.cpp index fe4c017..aaed750 100644 --- a/code/Table.cpp +++ b/code/Table.cpp @@ -2045,7 +2045,7 @@ void Table::importClipboard(gdioutput &gdi) tw = max(tw, table[k].size()); if (tw > columns.size()) - throw std::exception("Antalet columner i urklippet är större än antalet kolumner i tabellen."); + throw meosException("Antalet kolumner i urklippet är större än antalet kolumner i tabellen."); if (upperRow == -1) { if (!gdi.ask(L"Vill du klistra in X nya rader i tabellen?#"+itow(table.size()))) @@ -2059,10 +2059,10 @@ void Table::importClipboard(gdioutput &gdi) getRowRange(row1, row2); if ( (row1 + table.size()) > sortIndex.size() ) - throw std::exception("Antalet rader i urklipp får inte plats i selektionen."); + throw meosException("Antalet rader i urklipp får inte plats i selektionen."); if ( (col1 + tw) > columns.size() ) - throw std::exception("Antalet kolumner i urklipp får inte plats i selektionen."); + throw meosException("Antalet kolumner i urklipp får inte plats i selektionen."); bool wrongSize = false; diff --git a/code/danish.lng b/code/danish.lng index 3121f11..daf8564 100644 --- a/code/danish.lng +++ b/code/danish.lng @@ -2410,3 +2410,70 @@ help:custom_text_lines = Du kan indsætte egne data ved at skrive [Symbol Name]. htmlhelp = HTML kan eksporteres som en struktureret tabel eller som et frit formateret dokument (mere i stil med MeOS lister). Du kan også bruge eksporter skabeloner med egen formatering: kolonner, JavaScript base page flips, automatisk rulning, o.s.v. Det er muligt at tilføje egne skabeloner ved at tilføje '.template' filer i MeOS mappen. Hvis du bruger skabeloner er der et antal parametre der skal angives, se nedenfor. Den præcise fortolkning af parametrene afhænger af skabelonen.\n\nHvis du vælger bliver listen og dens opsætning gemt permanent i løbet. Du kan så tilgå listen ved at bruge MeOS som Web server (Tjenesten 'Information Server') eller ved at eksportere listen ved jævne mellemrum. info:pageswithcolumns = Vis listen en side af gangen med det angivne antal kolonner. Genindlæs listen automatisk efter hvert gennemløb. Övrigt = Øvrigt +Age (on last day of current year) = Alder ved årets udgang +Age above or equal implies senior/pensioner = Aldersgrænse for seniorer +Age below or equal implies youth = Aldersgrænse for ungdom +Anmälningsdatum = Tilmeldingsdato +ClassAvailableMaps = Tilgængelige kort for klasse +ClassNumEntries = Antal tilmeldte i klassen +ClassTotalMaps = Antal kort total +EFilterWrongFee = Forkert gebyr +Flera lopp i valfri ordning = Flere løb i vilkårlig rækkefølge +Flytta ner = Flyt ned +Flytta upp = Flyt op +Från första = Fra første +Förväntad = Forventet +Heat = Heat +Inkludera bana = Inkluder bane +Kartor = Kort +Klassen är full = Klassen er fuld +Knockout sammanställning = Knock-out opsummering +Kunde inte ladda upp löpardatabasen (X) = Kunne ikke overføre løberdatabase (X) +Kunde inte öppna databasen (X) = Kunne ikke åbne database (X) +Kvalschema = Kvalifikationsskema +Lagändringblankett = Holdændringsskema +Lås funktion = Lås funktion +Lås upp = Lås op +Låst gaffling = Låst gaffling +Mappa rootadressen (http:///localhost:port/) till funktion = Map root addresse (http:///localhost:port/) til funktion +Målstämpling tillåts inte (X) = Målstempling ikke tilladt (X) +Plac. E[stageno] = Plac. E +Poäng E[stageno] = Point E +Poängreduktion = Reduktion +Radio = Radio +Radio tillåts inte (X) = Radioposter ikke tilladt (X) +Referens = Reference +Registrera hyrbrickor = Registrer lejebrikker +Result module = Resultat modul +Rogaining points before automatic reduction = Rogaining point før automatisk reduktion +Runner check time = Løbers check tid +Runner/team earlier stage places = Deltagere/hold: placering på tidligere etapper +Runner/team earlier stage points = Deltagere/hold: point på tidligere etapper +Runner/team earlier stage running times = Deltagere/hold: tid på tidligere etapper +Runner/team earlier stage statuses = Deltagere/hold: status for tidligere etapper +RunnerExpectedFee = Forventet deltagerafgift +Senast sedd: X vid Y = Sidst set: X ved Y +Startstämpling tillåts inte (X) = Startstempling ikke tilladt (X) +Status E[stageno] = Status E +Status code for cancelled entry = Statuskode for afbud +Status code for no timing = Statuskode for uden tidsstempling +Status code for running out-of-competition = Statuskode for udenfor konkurrance (OOC) +Stigning = Stigning +There is no result module with X as identifier = Der er intet resultatmodul med identifikator X +Tid E[stageno] = Tid E +Till sista = Til sidste +Tillåt = Tillad +Unexpected Fee = Uventet tilmeldingsgebyr +Utom tävlan = Udenfor konkurrance OOC +Vill du sätta hyrbricka på befintliga löpare med dessa brickor? = Vil du tildele lejebrikker til eksisterende løbere med disse brikker? +Vill du ta bort brickan från hyrbrickslistan? = Vil du fjerne brikken fra listen af lejebrikker? +Vill du tömma listan med hyrbrickor? = Vil du slette listen med lejebrikker? +X anmälda = X tilmeldte +ask:usecourseinclass = Banen bruges ikke af andre deltagere i klassen.\n\nV il du alligevel bruge den? +help:registerhiredcards = Forregistrer Si-brikker som lejebrikker så de automatisk får status lejebrik når de tildeles. +info:advanceinfo = Det var ikke muligt at starte tjesten for forhåndsinformation om resultat. Resultatet vil komme med nogle skunders forsinkelse. Dette kan forventes hvis der kører flere udgaver at MeOS på maskinen samtidigt. +prefsLastExportTarget = Seneste mål for eksport +prefsServiceRootMap = Standardfunktion for webserverens root +prefsshowheader = Vis overskrifter på siderne +warn:printmodeonly = Bemærk: Du laver kun en udskrift af Si-brikkens indhold. Hvis du vil gemme resultatet skal du bruge funktionen aflæs/radiotider. +Åldersfiltrering = Aldersfiltrering diff --git a/code/english.lng b/code/english.lng index 542a635..b86c8c7 100644 --- a/code/english.lng +++ b/code/english.lng @@ -1582,7 +1582,7 @@ Inga bommar registrerade = No control mistakes detected Inställningar sträcktidsutskrift = Print Split Times Settings Med km-tid = Include tempo (min/km) Tidsförluster (kontroll-tid) = Time loss (control/time) -Underlag saknas för bomanalys = No data for control mistakes +Underlag saknas för bomanalys = No data for control mistakes min/km = min/km X har redan bricknummer Y. Vill du ändra det? = X already has card number Y. Do you want to change it? Avmarkera 'X' för att hantera alla bricktildelningar samtidigt = Uncheck 'X' to handle all card assignments on one page @@ -2426,7 +2426,7 @@ prefsServiceRootMap = Standard function for web server root prefsshowheader = Show page headers help:registerhiredcards = Preregister punching cards as rental cards to get automatic hired card status when the card is assigned. Lagändringblankett = Team Change Form -Mappa rootadresssen (http:///localhost:port/) till funktion = Map root address (http:///localhost:port/) to function +Mappa rootadressen (http:///localhost:port/) till funktion = Map root address (http:///localhost:port/) to function ClassAvailableMaps = Available maps for class ClassTotalMaps = Total number of maps for class Kunde inte öppna databasen (X) = Could not connect to database (X) @@ -2474,4 +2474,22 @@ Status code for cancelled entry = Status code for cancelled entry Age (on last day of current year) = Age (on last day of current year) Age above or equal implies senior/pensioner = Age above or equal implies senior/pensioner Age below or equal implies youth = Age below or equal implies youth -Status code for not no timing = Status code for not no timing +Det finns multiplia Id-nummer för personer = There are multiple ID:s for persons +Välj vilken typ du vill importera = Select type of ID to import +Status code for no timing = Status code for no timing +prefsVacantPercent = Percent vacant default +ClassNumEntries = Number of entries +Före X = Before X +Efter X = After X +Slå ihop X = Merge with X +Slå ihop med = Merge with +Mellan X och Y = Between X and Y +Ett okänt fel inträffade = An unknown error occurred +Antalet kolumner i urklippet är större än antalet kolumner i tabellen = The number of columns in the clipboard is greater than the number of columns in the table +Antalet kolumner i urklipp får inte plats i selektionen = The number of columns in the clipboard does not fit the selection +prefsVacantPosition = Placement of vacancies +Vakansplacering = Vacancy placement +Först = First +Lottat = Drawn +Sist = Last +Fakturadatum = Fakturadatum diff --git a/code/french.lng b/code/french.lng index 1252241..9657218 100644 --- a/code/french.lng +++ b/code/french.lng @@ -2429,7 +2429,61 @@ prefsServiceRootMap = Fonctions standard pour la source du serveur Web prefsshowheader = Afficher les titres help:registerhiredcards = Pré-inscrivez des puces comme puces louées pour assigner automatiquement le statut et le tarif correspondant quand cette puce est assignée. Lagändringblankett = Modifications d'équipes -Mappa rootadresssen (http:///localhost:port/) till funktion = Map root address (http:///localhost:port/) to function ($2428) +Mappa rootadressen (http:///localhost:port/) till funktion = Map root address (http:///localhost:port/) to function ($2428) ClassAvailableMaps = Cartes disponibles pour la catégorie ClassTotalMaps = Nombre total de cartes pour la catégorie Export split times = Exporter les temps intermédiaires +Age (on last day of current year) = Age (au dernier jour de l'année en cours) +Age above or equal implies senior/pensioner = Limite d'âge haute des jeunes (défini dans Compétition/Configuration de la competition) +Age below or equal implies youth = Limite d'âge haute des jeunes (défini dans Compétition/Configuration de la competition) +ClassNumEntries = Nombres d'inscrits dans la catégorie +Endast tidtagning = chronométrage seul +Flera lopp i valfri ordning = Plusieurs circuits dans un ordre quelconque +Från första = Dép. au premier poste +Heat = Manche +Inkludera bana = Inclure le circuit +Kartor = Cartes +Knockout sammanställning = Résumé des éliminations +Kunde inte ladda upp löpardatabasen (X) = Impossible d'importer la base de données coureurs (X) +Kunde inte öppna databasen (X) = Impossible de se connecter à la base de données (X) +Kvalschema = Procédé de qualification +Lås funktion = Verr. fonction +Lås upp = Déverouiller +Låst gaffling = Variations verrouillées +Målstämpling tillåts inte (X) = Arrivée radio désactivée (x) [Finish punch disallowed] +Plac. E[stageno] = Place E +Poäng E[stageno] = Points E +Poängreduktion = Réduction +Radio = Radio +Radio tillåts inte (X) = Postes de contrôle radios désactivés (X) [Radio controls disallowed] +Referens = Référence +Result module = Module de résultats +Rogaining points before automatic reduction = Points du circuit avant réduction automatique +Runner check time = Heure de contrôle du coureur +Runner/team earlier stage places = Places du coureur/de l'équipe aux étapes précédentes +Runner/team earlier stage points = Points du coureur/de l'équipe aux étapes précédentes +Runner/team earlier stage running times = Temps du coureur/de l'équipe aux étapes précédentes +Runner/team earlier stage statuses = Statuts du coureur/de l'équipe aux étapes précédentes +Senast sedd: X vid Y = Dernières infos: à X au poste Y +Startstämpling tillåts inte (X) = Départ radio désactivé (X) [Start punch disallowed] +Status E[stageno] = Statut E +Status code for cancelled entry = Code de statut pour une inscription annulée +Status code for no timing = Code de statut pour une course non chronométrée +Status code for running out-of-competition = Code de statut pour non classé (NC) +Stigning = Dénivellé +Tid E[stageno] = Temps E +Till sista = Arr. au dernier poste +Tillåt = Autoriser +Utom tävlan = NC +X anmälda = X inscrits +ask:usecourseinclass = Le circuit n'est utilisé par aucun autre coureur dans cette catégorie\n\nVoulez-vous tout de même l'utiliser ? +warn:printmodeonly = Attention, notez bien que vous ne faites qu'imprimer les données de la puce sans l'enregistrer.\n\nPour l'intégrer à la course, utilisez la fonction Lecture des puces/Radios. +Åldersfiltrering = Filtrer par âge +There is no result module with X as identifier = Aucun module de résultat n'a X comme identifiant +prefsVacantPercent = Pourcentage de vacants +ClassNumEntries = Nombre d'inscrits dans la catégorie +Före X = Avant X +Efter X = Après X +Slå ihop X = Fusionner X +Slå ihop med = Fusionner avec +Ett okänt fel inträffade = Une erreur inconnue s'est produite diff --git a/code/gdiconstants.h b/code/gdiconstants.h index 4b19a3c..9dc3cc9 100644 --- a/code/gdiconstants.h +++ b/code/gdiconstants.h @@ -40,5 +40,14 @@ enum KeyCommandCode { KC_AUTOCOMPLETE, }; +/** Enum used to stack GUI command control, "command line wizard" */ +enum FlowOperation { + FlowContinue, + FlowCancel, + FlowAborted +}; + + + const int GDI_BUTTON_SPACING = 8; #endif diff --git a/code/gdioutput.cpp b/code/gdioutput.cpp index 480782c..c3a8124 100644 --- a/code/gdioutput.cpp +++ b/code/gdioutput.cpp @@ -1358,6 +1358,15 @@ HFONT gdioutput::getGUIFont() const return getCurrentFont().getGUIFont(); } +pair gdioutput::getInputDimension(int length) const { + HDC hDC = GetDC(hWndTarget); + SelectObject(hDC, getGUIFont()); + SIZE size; + GetTextExtentPoint32(hDC, L"M", 1, &size); + ReleaseDC(hWndTarget, hDC); + return make_pair(length*size.cx + scaleLength(8), size.cy + scaleLength(6)); +} + InputInfo &gdioutput::addInput(int x, int y, const string &id, const wstring &text, int length, GUICALLBACK cb, const wstring &explanation, const wstring &help) { @@ -1367,30 +1376,25 @@ InputInfo &gdioutput::addInput(int x, int y, const string &id, const wstring &te } InputInfo ii; - SIZE size; - - HDC hDC=GetDC(hWndTarget); - SelectObject(hDC, getGUIFont()); - GetTextExtentPoint32(hDC, L"M", 1, &size); - ReleaseDC(hWndTarget, hDC); - + + auto dim = getInputDimension(length); int ox=OffsetX; int oy=OffsetY; ii.hWnd=CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", text.c_str(), WS_TABSTOP|WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | ES_AUTOHSCROLL | WS_BORDER, - x-ox, y-oy, length*size.cx+scaleLength(8), size.cy+scaleLength(6), + x-ox, y-oy, dim.first, dim.second, hWndTarget, NULL, (HINSTANCE)GetWindowLong(hWndTarget, GWL_HINSTANCE), NULL); - - updatePos(x, y, length*size.cx+scaleLength(12), size.cy+scaleLength(10)); + int mrg = scaleLength(4); + updatePos(x, y, dim.first+mrg, dim.second+mrg); SendMessage(ii.hWnd, WM_SETFONT, (WPARAM) getGUIFont(), 0); ii.xp=x; ii.yp=y; - ii.width = length*size.cx+scaleLength(8); - ii.height = size.cy+scaleLength(6); + ii.width = dim.first; + ii.height = dim.second; ii.text = text; ii.original = text; ii.focusText = text; diff --git a/code/gdioutput.h b/code/gdioutput.h index 601cdfe..d83bc12 100644 --- a/code/gdioutput.h +++ b/code/gdioutput.h @@ -684,6 +684,9 @@ public: bool isInputChanged(const string &exclude); + /** Get width of input widget with specified length (chars)*/ + pair getInputDimension(int length) const; + InputInfo &addInput(const string &id, const wstring &text = L"", int length=16, GUICALLBACK cb=0, const wstring &Explanation = L"", const wstring &tooltip=L""); InputInfo &addInput(int x, int y, const string &id, const wstring &text, int length, diff --git a/code/generalresult.cpp b/code/generalresult.cpp index e847625..9106986 100644 --- a/code/generalresult.cpp +++ b/code/generalresult.cpp @@ -1063,7 +1063,7 @@ void DynamicResult::declareSymbols(DynamicMethods m, bool clear) const { parser.declareSymbol("StatusDQ", "Status code for disqualification", false); parser.declareSymbol("StatusOutOfCompetition", "Status code for running out-of-competition", false); parser.declareSymbol("StatusNotCompetiting", "Status code for not competing", false); - parser.declareSymbol("StatusNoTiming", "Status code for not no timing", false); + parser.declareSymbol("StatusNoTiming", "Status code for no timing", false); parser.declareSymbol("ShortestClassTime", "Shortest time in class", false); diff --git a/code/html1.htm b/code/html1.htm index 76a685a..832017e 100644 --- a/code/html1.htm +++ b/code/html1.htm @@ -1,28 +1,29 @@ -
-

Documentation of MeOS REST API

+ +
+

Documentation of MeOS REST API

-

Competition

-Syntax: +

Competition

+ Syntax:
/meos?get=competition
-Returns: -

MOP Competition XML

+ Returns: +

MOP Competition XML

-Example: + Example:
 *MOPComplete>
  *competition date="2015-09-06" organizer="Orienteringsklubben Linné" homepage="http://www.oklinne.nu">Stafett-DM, Uppland*/competition>
 */MOPComplete>
 
-

Classes

-Syntax: +

Classes

+ Syntax:
/meos?get=class
-Returns: -

MOP Classes XML

+ Returns: +

MOP Classes XML

-Example: + Example:
 *MOPComplete>
  *cls id="1" ord="10" radio="90,130;90,130;90,130">D21*/cls>
@@ -30,20 +31,20 @@
 */MOPComplete>
 
-Remarks: The attribute ord should be used to sort classes. The attribute radio -lists the MeOS default radio control for each class and leg. -Each leg of the class is separated by ';' and each radio control within a leg with ','. -Note that you may query for results at any control; the listed -controls are only for convenience. + Remarks: The attribute ord should be used to sort classes. The attribute radio + lists the MeOS default radio control for each class and leg. + Each leg of the class is separated by ';' and each radio control within a leg with ','. + Note that you may query for results at any control; the listed + controls are only for convenience. -

Controls

-Syntax: +

Controls

+ Syntax:
/meos?get=control
-Returns: -

MOP Controls XML

+ Returns: +

MOP Controls XML

-Example: + Example:
 *MOPComplete>
   *control id="31">[31]*/control>
@@ -52,54 +53,60 @@ controls are only for convenience.
 */MOPComplete>
 
-

Competitors

-Syntax: +

Competitors

+ Syntax:
/meos?get=competitor
/meos?get=competitor#class=*c1>,*c2>,...
-Arguments: -
  • class A list of one or more class id:s, separated by comma. The default is all classes. -
+ Arguments: +
    +
  • + class A list of one or more class id:s, separated by comma. The default is all classes. +
  • +
-Returns: -

MOP Competitors XML

+ Returns: +

MOP Competitors XML

-Example: + Example:
 *MOPComplete>
-  *cmp id="3565">
-    *base org="570" cls="5" stat="1" st="360000" rt="20580">Elsa Winter*/base>
+  *cmp id="3565" card="101050">
+    *base org="570" cls="5" stat="1" st="360000" rt="20580" nat="NOR">Elsa Winter*/base>
     *input it="20340" tstat="1"/>
   */cmp>
   *cmp id="55851">
-    *base org="134" cls="7" stat="1" st="380710" rt="46910">Anna Spring*/base>
+    *base org="134" cls="7" stat="1" st="380710" rt="46910" nat="SWE">Anna Spring*/base>
   */cmp>
 */MOPComplete>
 
-Remarks: -Please refer to the MOP documentation for a complete description of all attributes. Below is a quick reference. -
    -
  • stat Status code. 1 is OK, 0 is unknown. Higher codes are used for disqualifications.
  • -
  • st Start time. In 1/10 seconds after 00:00:00.
  • -
  • rt Running time. In 1/10 seconds.
  • -
  • input/it Input running time accumulated from earlier races. In 1/10 seconds.
  • -
  • input/st Input status from earlier races.
  • -
+ Remarks: + Please refer to the MOP documentation for a complete description of all attributes. Below is a quick reference. +
    +
  • stat Status code. 1 is OK, 0 is unknown, 2 is no timing. Higher codes are used for disqualifications etc.
  • +
  • st Start time. In 1/10 seconds after 00:00:00.
  • +
  • rt Running time. In 1/10 seconds.
  • +
  • input/it Input running time accumulated from earlier races. In 1/10 seconds.
  • +
  • input/st Input status from earlier races.
  • +
-

Teams

-Syntax: +

Teams

+ Syntax:
/meos?get=team
/meos?get=team#class=*c1>,*c2>,...
-Arguments: -
  • class A list of one or more class id:s, separated by comma. The default is all classes. -
+ Arguments: +
    +
  • + class A list of one or more class id:s, separated by comma. The default is all classes. +
  • +
-Returns: -

MOP Teams XML

+ Returns: +

MOP Teams XML

-Example: + Example:
 *MOPComplete>
   *tm id="2928771">
@@ -113,25 +120,27 @@ Please refer to the MOP documentation for a complete description of all attribut
 */MOPComplete>
 
-Remarks: -Please refer to the MOP documentation for a complete description of all attributes. Below is a quick reference. -
    -
  • stat Status code. 1 is OK, 0 is unknown. Higher codes are used for disqualifications.
  • -
  • st Start time. In 1/10 seconds after 00:00:00.
  • -
  • rt Running time (At finish, last leg). In 1/10 seconds.
  • -
  • r List of runners in the team. A ';' is used to separate the legs, a ',' within a legs - are used to list paralell runners. A '0' indicates a vacant leg, which may or may - not be valid for the team, depending on the rules.
  • -
+ Remarks: + Please refer to the MOP documentation for a complete description of all attributes. Below is a quick reference. +
    +
  • stat Status code. 1 is OK, 0 is unknown. Higher codes are used for disqualifications.
  • +
  • st Start time. In 1/10 seconds after 00:00:00.
  • +
  • rt Running time (At finish, last leg). In 1/10 seconds.
  • +
  • + r List of runners in the team. A ';' is used to separate the legs, a ',' within a legs + are used to list paralell runners. A '0' indicates a vacant leg, which may or may + not be valid for the team, depending on the rules. +
  • +
-

Organizations and Clubs

-Syntax: +

Organizations and Clubs

+ Syntax:
/meos?get=organization
-Returns: -

MOP Organization XML

+ Returns: +

MOP Organization XML

-Example: + Example:
 *MOPComplete>
   *org id="1012">BAOC*/org>
@@ -141,8 +150,42 @@ Please refer to the MOP documentation for a complete description of all attribut
 */MOPComplete>
 
-

Results

-Syntax: +

Recent Competition Changes

+ Purpose: + Keep a separate database with results synchronized with MeOS. +
+ Syntax: +
/meos?difference=zero
+
/meos?difference=*nextdifference>
+ + Returns: +

+ Complete or difference MOP of changes since last time API was called. Use litteral 'zero' to obtain + a new set of differences (the difference from zero). + The returned data includes an attribute nextdifference that you use as argument in + the nex call to difference, to get changes since this call. +

+ Example: +

+ Start by invoking /meos?difference=zero. The returned data will look like: +

+*MOPComplete nextdifference="332617">
+  ...
+*/MOPComplete>
+
+ + Next time, you invoke /meos?difference=332617, which may return +
+*MOPDiff nextdifference="324254">
+  ... 
+*/MOPDiff>
+
+ + Next time you use difference=324254 and so on. Note that under some circumstances, + you may also get a complete event instead of a difference. See the general MOP documentation. +

+

Results

+ Syntax:
/meos?get=result
/meos?get=result#class=*c1>,*c2>,...
/meos?get=result#to=*c1>
@@ -154,46 +197,59 @@ Please refer to the MOP documentation for a complete description of all attribut
/meos?get=result#total=*true/false>
/meos?get=result#type=*type>
-Arguments: -
    -
  • class A list of one or more class id:s, separated by comma. The default is all classes.
  • -
  • from Returns the result measuring time from a specific control. If a specified class - does not visit the control, an empty result set is returned; no error is given. - The default is the start.
  • -
  • to Returns the result at a specific control. If a specified class does not visit the control, - an empty result set is returned; no error is given. The default is the finish.
  • - -
  • leg In a team competition, returns result for a specified leg. If the leg is parallel, - the result is for the completed parallel leg. The leg number refer to the flat view in - MeOS class settings, i.e., for a setup "1, 2a, 2b, 2c, 3", correspons to flat leg numbers, - "1, 2, 3, 4, 5". The default is the last leg.
  • -
  • module The result module tag to use for the result calculation. - The support of the leg and control options depends on result module. The default is no module.
  • -
  • argument A numeric argument, defined and used by the result module.
  • + Arguments: +
      +
    • class A list of one or more class id:s, separated by comma. The default is all classes.
    • +
    • + from Returns the result measuring time from a specific control. If a specified class + does not visit the control, an empty result set is returned; no error is given. + The default is the start. +
    • +
    • + to Returns the result at a specific control. If a specified class does not visit the control, + an empty result set is returned; no error is given. The default is the finish. +
    • -
    • limit Limit the number of results per class. If there is a tie, the number of returned results - may be higher than the requested. The default is no limit.
    • -
    • total Set to true if you want to calculate total results, i.e., results including earliers stages.
    • - -
    • type Use one of ClassIndividual (default for individual classes), CourseIndividual, GlobalIndividual, or - LegIndividual to calculate individual results. CourseIndividual calculates result - per course, ignoring class. GlobalIndividual calculates results without - considering classes, LegIndividual calculates results considering classes and legs. - Use one of ClassTeam (default for team classes) or GlobalTeam to calculate team - results. GlobalTeam calculates team results without considering classes.
    • -
    +
  • + leg In a team competition, returns result for a specified leg. If the leg is parallel, + the result is for the completed parallel leg. The leg number refer to the flat view in + MeOS class settings, i.e., for a setup "1, 2a, 2b, 2c, 3", correspons to flat leg numbers, + "1, 2, 3, 4, 5". The default is the last leg. +
  • +
  • + module The result module tag to use for the result calculation. + The support of the leg and control options depends on result module. The default is no module. +
  • +
  • argument A numeric argument, defined and used by the result module.
  • + +
  • + limit Limit the number of results per class. If there is a tie, the number of returned results + may be higher than the requested. The default is no limit. +
  • +
  • total Set to true if you want to calculate total results, i.e., results including earliers stages.
  • + +
  • + type Use one of ClassIndividual (default for individual classes), CourseIndividual, GlobalIndividual, or + LegIndividual to calculate individual results. CourseIndividual calculates result + per course, ignoring class. GlobalIndividual calculates results without + considering classes, LegIndividual calculates results considering classes and legs. + Use one of ClassTeam (default for team classes) or GlobalTeam to calculate team + results. GlobalTeam calculates team results without considering classes. +
  • +
-Returns: -

MOP Individual or Team Result XML. The result list is sorted according to the result. Note than disqualified - competitors are also included; for example a competitor with status *DNS> will be returned with this status for - all radio controls. You may want to filter out such entries, depending on your application. -

+ Returns: +

+ MOP Individual or Team Result XML. The result list is sorted according to the result. Note than disqualified + competitors are also included; for example a competitor with status *DNS> will be returned with this status for + all radio controls. You may want to filter out such entries, depending on your application. +

-Examples: + Examples: -

Individual results at the finish.

+

Individual results at the finish.

 *MOPComplete>
   *results location="Finish">
@@ -215,7 +271,7 @@ Please refer to the MOP documentation for a complete description of all attribut
 */MOPComplete>
 
-

Team results, leg 2 at Radio 1, leg has three parallel runners.

+

Team results, leg 2 at Radio 1, leg has three parallel runners.

 *MOPComplete>
   *results leg="2" location="Radio 1">
@@ -241,7 +297,7 @@ Please refer to the MOP documentation for a complete description of all attribut
 */MOPComplete>
 
-

Roganing results, Invoke with argument module=rogaining, which is a built in module.

+

Roganing results, Invoke with argument module=rogaining, which is a built in module.

 *MOPComplete>
   *results module="rogaining" location="Finish">
@@ -264,35 +320,35 @@ Please refer to the MOP documentation for a complete description of all attribut
 */MOPComplete>
 
-Remarks: -When and only when the type is CourseIndividual, the attribute course is included. + Remarks: + When and only when the type is CourseIndividual, the attribute course is included.
 *person cls="1" stat="1" st="324000" rt="72550" place="1" course=28>
   *name id="4">Jordan Griesmer*/name>
 */person>
-
+ -

Status

-Syntax: +

Status

+ Syntax:
/meos?get=status
-Returns: -

MeOS Status Data. Includes MeOS version and competition name id, which also is the name of the database used, if any.

+ Returns: +

MeOS Status Data. Includes MeOS version and competition name id, which also is the name of the database used, if any.

-Example: + Example:
 *MOPComplete>
  *status version="3.6.1029" eventNameId="meos_20190223_212818_2FD" onDatabase="1"/>
 */MOPComplete>
 
-

Entry Classes

-Syntax: +

Entry Classes

+ Syntax:
/meos?get=entryclass
-Returns: -

Classes where entry is allowed via the API.

+ Returns: +

Classes where entry is allowed via the API.

 *EntryClasses>
@@ -306,24 +362,24 @@ When and only when the type is CourseIndividual, the attribute course<
 */EntryClasses>
 
-

Lookup competitor

-Syntax: +

Lookup competitor

+ Syntax:
/meos?lookup=competitor&id=*id>
/meos?lookup=competitor&card=*card>
/meos?lookup=competitor&bib=*bib>
/meos?lookup=competitor&name=*name>&club=*club>
-Returns: -

Competitor including individual result.

+ Returns: +

Competitor including individual result.

-Arguments: -
    -
  • id Competitor id. MeOS internal id.
  • -
  • card Card number.
  • -
  • bib Bib or start number.
  • -
  • name Name of competitor.
  • -
  • club Name of club.
  • -
+ Arguments: +
    +
  • id Competitor id. MeOS internal id.
  • +
  • card Card number.
  • +
  • bib Bib or start number.
  • +
  • name Name of competitor.
  • +
  • club Name of club.
  • +
 *Competitors>
@@ -359,23 +415,25 @@ When and only when the type is CourseIndividual, the attribute course<
 
-

Lookup Database Competitor

-Syntax: +

Lookup Database Competitor

+ Syntax:
/meos?lookup=dbcompetitor&id=*id>
/meos?lookup=dbcompetitor&card=*card>
/meos?lookup=dbcompetitor&name=*name>&club=*club>
-Returns: -

Competitor from runner database. Note that a partial name may be submitted, and that several matching result may be returned, sorted by relevance. -This query is suitable for auto complete functionality.

+ Returns: +

+ Competitor from runner database. Note that a partial name may be submitted, and that several matching result may be returned, sorted by relevance. + This query is suitable for auto complete functionality. +

-Arguments: -
    -
  • id External id from runner database.
  • -
  • card Card number.
  • -
  • name Name of competitor. Possibly a partial name.
  • -
  • club Name of club.
  • -
+ Arguments: +
    +
  • id External id from runner database.
  • +
  • card Card number.
  • +
  • name Name of competitor. Possibly a partial name.
  • +
  • club Name of club.
  • +
@@ -392,20 +450,22 @@ This query is suitable for auto complete functionality.

-

Lookup Database Club

-Syntax: +

Lookup Database Club

+ Syntax:
/meos?lookup=dbclub&id=*id>
/meos?lookup=dbclub&name=*name>
-Returns: -

Club from club database. Note that a partial name may be submitted, and that several matching result may be returned, sorted by relevance. -This query is suitable for auto complete functionality.

+ Returns: +

+ Club from club database. Note that a partial name may be submitted, and that several matching result may be returned, sorted by relevance. + This query is suitable for auto complete functionality. +

-Arguments: -
    -
  • id External id from club database.
  • -
  • name Name of club. Possibly a partial name.
  • -
+ Arguments: +
    +
  • id External id from club database.
  • +
  • name Name of club. Possibly a partial name.
  • +
 *DatabaseClubs>
@@ -415,25 +475,26 @@ This query is suitable for auto complete functionality.

*/DatabaseClubs>
-

API New Entry

-Syntax: +

API New Entry

+ Syntax:
/meos?entry&id=*id>&class=*classid>&card=*card>
-
/meos?entry&name=*name>&club=*club>&class=*classid>&card=*card>
+
/meos?entry&name=*name>&club=*club>&class=*classid>&card=*card>¬iming
-Arguments: -
    -
  • id External id of runner from runner database.
  • -
  • name Name of runner.
  • -
  • club Name of runner's club.
  • -
  • class Id of class.
  • -
  • card Card number.
  • -
+ Arguments: +
    +
  • id External id of runner from runner database.
  • +
  • name Name of runner.
  • +
  • club Name of runner's club.
  • +
  • class Id of class.
  • +
  • card Card number.
  • +
  • notiming Set status no timing.
  • +
-

Returns: -Status.

+

Returns: + Status.

-Note:

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

+

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

 *Answer>
@@ -450,44 +511,44 @@ Status.

*/Answer>
-

Page template

-Syntax: +

Page template

+ Syntax:
/meos?page=*page>
-Returns: -Installed template file with the specified tag. + Returns: + Installed template file with the specified tag. -

Image

-Syntax: +

Image

+ Syntax:
/meos?image=*image>
-Returns: -Image, *image>.png, if installed in MeOS datafolder. MeOS logo if *image> is meos. + Returns: + Image, *image>.png, if installed in MeOS datafolder. MeOS logo if *image> is meos. -

IOF XML Results

+

IOF XML Results

-Syntax: + Syntax:
/meos?get=iofresult
/meos?get=iofresult#class=*c1>,*c2>,...
-Arguments: -
    -
  • class A list of one or more class id:s, separated by comma. The default is all classes.
  • -
+ Arguments: +
    +
  • class A list of one or more class id:s, separated by comma. The default is all classes.
  • +
-Returns: -

IOF XML version 3.0 result list.

+ Returns: +

IOF XML version 3.0 result list.

-

IOF XML Startlist

+

IOF XML Startlist

-Syntax: + Syntax:
/meos?get=iofstart
/meos?get=iofstart#class=*c1>,*c2>,...
-Arguments: -
    -
  • class A list of one or more class id:s, separated by comma. The default is all classes.
  • -
+ Arguments: +
    +
  • class A list of one or more class id:s, separated by comma. The default is all classes.
  • +
-Returns: -

IOF XML version 3.0 start list.

+ Returns: +

IOF XML version 3.0 start list.

diff --git a/code/infoserver.cpp b/code/infoserver.cpp index bb179db..475ce56 100644 --- a/code/infoserver.cpp +++ b/code/infoserver.cpp @@ -427,9 +427,12 @@ void InfoMeosStatus::serialize(xmlbuffer &xml, bool diffOnly) const { bool InfoOrganization::synchronize(oClub &c) { const wstring &n = c.getDisplayName(); - if (n == name) + const wstring &nat = c.getDCI().getString("Nationality"); + + if (n == name && nat == nationality) return false; else { + nationality = nat; name = n; modified(); } @@ -439,6 +442,8 @@ bool InfoOrganization::synchronize(oClub &c) { void InfoOrganization::serialize(xmlbuffer &xml, bool diffOnly) const { vector< pair > prop; prop.push_back(make_pair("id", itow(getId()))); + if (!nationality.empty()) + prop.emplace_back("nat", nationality); xml.write("org", prop, name); } @@ -461,9 +466,11 @@ void InfoBaseCompetitor::serialize(xmlbuffer &xml, bool diffOnly, int course) co if (course != 0) prop.emplace_back("crs", itow(course)); - if (!bib.empty()) { + if (!bib.empty()) prop.emplace_back("bib", bib); - } + + if (!nationality.empty()) + prop.emplace_back("nat", nationality); xml.write("base", prop, name); } @@ -488,11 +495,21 @@ bool InfoBaseCompetitor::synchronizeBase(oAbstractRunner &bc) { ch = true; } - RunnerStatus s = bc.getStatus(); + const wstring &nat = bc.getDCI().getString("Nationality"); + if (nat != nationality) { + nationality = nat; + ch = true; + } + + RunnerStatus s = bc.getStatusComputed(); + int rt = bc.getRunningTime(true) * 10; if (rt > 0) { if (s == RunnerStatus::StatusUnknown) s = RunnerStatus::StatusOK; + + if (s == RunnerStatus::StatusNoTiming) + rt = 0; } else if (isPossibleResultStatus(s)) s = StatusUnknown; @@ -583,7 +600,7 @@ bool InfoCompetitor::synchronize(const InfoCompetition &cmp, oRunner &r) { } bool nr; - if ((r.getStatus() == StatusUnknown || r.getStatus() == StatusOutOfCompetition || r.getStatus() == StatusNoTiming) && r.getFinishTime() <= 0) { + if ((r.getStatus() == StatusUnknown || isPossibleResultStatus(r.getStatus())) && r.getFinishTime() <= 0) { vector pv; r.getEvent()->getPunchesForRunner(r.getId(), false, pv); nr = pv.size() > 0; @@ -597,7 +614,7 @@ bool InfoCompetitor::synchronize(const InfoCompetition &cmp, oRunner &r) { } vector newRT; - if (r.getClassId(false) > 0) { + if (r.getClassId(false) > 0 && r.getStatusComputed() != RunnerStatus::StatusNoTiming) { const vector &radios = cmp.getControls(r.getClassId(true), r.getLegNumber()); for (size_t k = 0; k < radios.size(); k++) { RadioTime radioTime; @@ -611,7 +628,7 @@ bool InfoCompetitor::synchronize(const InfoCompetition &cmp, oRunner &r) { } } } - changeRadio = radioTimes.size() > 0;//false; // Always write full attributes + changeRadio = radioTimes.size() > 0; // Always write full attributes if (newRT != radioTimes) { ch = true; changeRadio = true; @@ -879,3 +896,33 @@ bool xmlbuffer::commit(xmlparser &xml, int count) { return !blocks.empty(); } + +void xmlbuffer::commitCopy(xmlparser &xml) { + vector p2; + for (block &block : blocks) { + if (block.subValues.empty()) { + xml.write(block.tag.c_str(), block.prop, block.value); + } + else { + if (block.prop.size() > 1) { + p2.resize(block.prop.size() * 2); + for (size_t k = 0; k < block.prop.size(); k++) { + p2[k * 2] = gdi_main->widen(block.prop[k].first); + p2[k * 2 + 1] = block.prop[k].second; + } + xml.startTag(block.tag.c_str(), p2); + } + else if (block.prop.size() == 1) { + xml.startTag(block.tag.c_str(), block.prop[0].first.c_str(), block.prop[0].second); + } + else if (block.prop.empty()) { + xml.startTag(block.tag.c_str()); + } + + for (size_t k = 0; k < block.subValues.size(); k++) + block.subValues[k].commitCopy(xml); + + xml.endTag(); + } + } +} diff --git a/code/infoserver.h b/code/infoserver.h index b804aa3..a29f9e7 100644 --- a/code/infoserver.h +++ b/code/infoserver.h @@ -64,7 +64,9 @@ public: size_t size() const {return blocks.size();} bool commit(xmlparser &xml, int count); + void commitCopy(xmlparser &xml); + bool isComplete() const { return complete; } void startXML(xmlparser &xml, const wstring &dest); }; @@ -140,6 +142,7 @@ class InfoMeosStatus : public InfoBase { class InfoOrganization : public InfoBase { protected: wstring name; + wstring nationality; public: InfoOrganization(int id); virtual ~InfoOrganization() {} @@ -169,6 +172,7 @@ class InfoBaseCompetitor : public InfoBase { int startTime; int runningTime; wstring bib; + wstring nationality; void serialize(xmlbuffer &xml, bool diffOnly, int course) const; bool synchronizeBase(oAbstractRunner &bc); public: diff --git a/code/intkeymap.hpp b/code/intkeymap.hpp index 07a4982..78715c0 100644 --- a/code/intkeymap.hpp +++ b/code/intkeymap.hpp @@ -47,13 +47,13 @@ private: T &rehash(int size, KEY key, const T &value); T &get(const KEY key); - const intkeymap &operator=(const intkeymap &co); void *lookup(KEY key) const; public: virtual ~intkeymap(); intkeymap(int size); intkeymap(); intkeymap(const intkeymap &co); + const intkeymap &operator=(const intkeymap &co); bool empty() const; int size() const; diff --git a/code/intkeymapimpl.hpp b/code/intkeymapimpl.hpp index a821069..43e4123 100644 --- a/code/intkeymapimpl.hpp +++ b/code/intkeymapimpl.hpp @@ -83,6 +83,34 @@ template intkeymap::intkeymap(const intkeymap &co) } } +template +const intkeymap &intkeymap::operator=(const intkeymap &co) { + clear(); + delete[] keys; + + allocFactor = co.allocFactor; + siz = co.siz; + keys = new keypair[siz]; + hash1 = co.hash1; + hash2 = co.hash2; + used = co.used; + level = co.level; + dummy = co.dummy; + noValue = co.noValue; + + for (unsigned k = 0; k(*co.next); + next->parent = this; + } + return *this; +} + template intkeymap::~intkeymap() { delete[] keys; diff --git a/code/iof30interface.cpp b/code/iof30interface.cpp index 69b5fac..d2e272f 100644 --- a/code/iof30interface.cpp +++ b/code/iof30interface.cpp @@ -492,7 +492,6 @@ void IOF30Interface::assignTeamCourse(gdioutput &gdi, oTeam &team, xmlList &xAss } } - void IOF30Interface::classAssignmentObsolete(gdioutput &gdi, xmlList &xAssignment, const map &courses, const map > &coursesFamilies) { @@ -690,6 +689,23 @@ void IOF30Interface::classAssignmentObsolete(gdioutput &gdi, xmlList &xAssignmen } } +void IOF30Interface::prescanCompetitorList(xmlobject &xo) { + xmlList xl; + xo.getObjects(xl); + + xmlList::const_iterator it; + xmlList work; + string wstring; + for (it = xl.begin(); it != xl.end(); ++it) { + if (it->is("Competitor")) { + xmlobject person = it->getObject("Person"); + if (person) { + readIdProviders(person, work, wstring); + } + } + } +} + void IOF30Interface::readCompetitorList(gdioutput &gdi, const xmlobject &xo, int &personCount) { if (!xo) return; @@ -704,7 +720,7 @@ void IOF30Interface::readCompetitorList(gdioutput &gdi, const xmlobject &xo, int xmlList::const_iterator it; - for (it=xl.begin(); it != xl.end(); ++it) { + for (it = xl.begin(); it != xl.end(); ++it) { if (it->is("Competitor")) { if (readXMLCompetitorDB(*it)) personCount++; @@ -736,15 +752,15 @@ void IOF30Interface::readClubList(gdioutput &gdi, const xmlobject &xo, int &club void IOF30Interface::prescanEntryList(xmlobject &xo, set &definedStages) { definedStages.clear(); - xmlList pEntries; + xmlList pEntries, work; xo.getObjects("PersonEntry", pEntries); for (size_t k = 0; k < pEntries.size(); k++) { - prescanEntry(pEntries[k], definedStages); + prescanEntry(pEntries[k], definedStages, work); } xo.getObjects("TeamEntry", pEntries); for (size_t k = 0; k < pEntries.size(); k++) { - prescanEntry(pEntries[k], definedStages); + prescanEntry(pEntries[k], definedStages, work); } } @@ -1598,8 +1614,8 @@ int IOF30Interface::getIndexFromLegPos(int leg, int legorder, const vector &stages) { - xmlList races; +void IOF30Interface::prescanEntry(xmlobject &xo, set &stages, xmlList &work) { + xmlList &races = work; xo.getObjects("RaceNumber", races); if (races.empty()) xo.getObjects("Race", races); // For unclear reason the attribute is called Race for teams and RaceNumber for persons. @@ -1621,16 +1637,21 @@ void IOF30Interface::prescanEntry(xmlobject &xo, set &stages) { person = teamPerson.getObject("Person"); } - xmlList ids; if (person) { - person.getObjects("Id", ids); + xmlList &ids = work; string type; - if (ids.size() > 1) { - for (auto &id : ids) { - id.getObjectString("type", type); - if (!type.empty()) { - idProviders.insert(type); - } + readIdProviders(person, ids, type); + } +} + +void IOF30Interface::readIdProviders(xmlobject &person, xmlList &ids, std::string &type) +{ + person.getObjects("Id", ids); + if (ids.size() > 1) { + for (auto &id : ids) { + id.getObjectString("type", type); + if (!type.empty()) { + idProviders.insert(type); } } } @@ -1767,6 +1788,22 @@ pRunner IOF30Interface::readPersonEntry(gdioutput &gdi, xmlobject &xo, pTeam tea } } + bool hasTime = true; + xmlobject ext = xo.getObject("Extensions"); + if (ext) { + xmlList exts; + ext.getObjects(exts); + for (xmlobject &xx : exts) { + if (xx.is("TimePresentation")) { + hasTime = xx.getObjectBool(nullptr); + } + } + } + if (!hasTime) + r->setStatus(StatusNoTiming, true, oBase::ChangeType::Update); + else if (r->getStatus() == StatusNoTiming) + r->setStatus(StatusUnknown, true, oBase::ChangeType::Update); + r->synchronize(); return r; } @@ -2766,7 +2803,6 @@ void IOF30Interface::writeCourseInfo(xmlparser &xml, const oCourse &c) { xml.write("Climb", climb); } - wstring formatStatus(RunnerStatus st, bool hasTime) { switch (st) { case StatusNoTiming: @@ -2870,6 +2906,9 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o if (r.getStartTime() > 0) xml.write("StartTime", oe.getAbsDateTimeISO(r.getStartTime(), true, useGMT)); + bool hasTiming = (!r.getClassRef(false) || r.getClassRef(true)->getNoTiming() == false) && + r.getStatusComputed() != RunnerStatus::StatusNoTiming; + int finishTime, runningTime, place, after; RunnerStatus status; if (!patrolResult) { @@ -2892,13 +2931,13 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o status = r.getTeam()->getLegStatus(pl, true, false); } - if ((r.getClassRef(false) && r.getClassRef(true)->getNoTiming()) || - r.getStatusComputed() == StatusNoTiming) { + if (!hasTiming) { after = -1; runningTime = 0; + finishTime = 0; } - if (r.getFinishTime() > 0) + if (finishTime > 0) xml.write("FinishTime", oe.getAbsDateTimeISO(finishTime, true, useGMT)); if (runningTime > 0) @@ -2918,7 +2957,7 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o if (r.getClassRef(false)) { - if (r.statusOK(true) && r.getClassRef(false)->getNoTiming() == false) { + if (r.statusOK(true) && hasTiming) { if (!teamMember && place > 0 && place < 50000) { xml.write("Position", place); } @@ -2941,11 +2980,11 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o } if ( (r.getTeam() && r.getClassRef(false)->getClassType() != oClassPatrol && !teamsAsIndividual) || hasInputTime) { xml.startTag("OverallResult"); + int rt = r.getTotalRunningTime(); - if (rt > 0) + if (rt > 0 && hasTiming) xml.write("Time", rt); - bool hasTiming = r.getClassRef(false)->getNoTiming() == false; RunnerStatus stat = r.getTotalStatus(); int tleg = r.getLegNumber() >= 0 ? r.getLegNumber() : 0; @@ -3002,7 +3041,7 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o else xml.startTag("SplitTime"); xml.write("ControlCode", crs->getControl(k)->getFirstNumber()); - if (sp[k].hasTime()) + if (sp[k].hasTime() && hasTiming) xml.write("Time", sp[k].time - r.getStartTime()); xml.endTag(); } @@ -3014,6 +3053,21 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o xml.write("Time", it->first); xml.endTag(); } + + oCard *card = r.getCard(); + if (card) { // Write additional punches + vector plist; + card->getPunches(plist); + for (pPunch p : plist) { + if (p->getTypeCode() >= 30 && !p->isUsedInCourse()) { + xml.startTag("SplitTime", "status", "Additional"); + xml.write("ControlCode", p->getTypeCode()); + if (p->getTimeInt() > r.getStartTime()) + xml.write("Time", p->getTimeInt() - r.getStartTime()); + xml.endTag(); + } + } + } } } } @@ -3023,6 +3077,12 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o writeFees(xml, rPerson); + if (!hasTiming) { + xml.startTag("Extensions"); + xml.write("TimePresentation", L"false"); + xml.endTag(); + } + xml.endTag(); } @@ -3463,10 +3523,14 @@ bool IOF30Interface::readXMLCompetitorDB(const xmlobject &xCompetitor) { xmlobject person = xCompetitor.getObject("Person"); if (!person) return false; - + + int pidI; + long long pid; + readId(person, pidI, pid); + /* wstring pidS; - person.getObjectString("Id", pidS); - long long pid = oBase::converExtIdentifierString(pidS); + person.getObjectString("Id", pidS);xxx + long long pid = oBase::converExtIdentifierString(pidS);*/ xmlobject pname = person.getObject("Name"); if (!pname) return false; diff --git a/code/iof30interface.h b/code/iof30interface.h index 4f07eda..fc891cb 100644 --- a/code/iof30interface.h +++ b/code/iof30interface.h @@ -167,7 +167,8 @@ class IOF30Interface { static int getIndexFromLegPos(int leg, int legorder, const vector &setup); - void prescanEntry(xmlobject & xo, set& stages); + void prescanEntry(xmlobject & xo, set& stages, xmlList &work); + void readIdProviders(xmlobject &person, xmlList &ids, std::string &type); void setupClassConfig(int classId, const xmlobject &xTeam, map > &teamClassConfig); void setupRelayClasses(const map > &teamClassConfig); @@ -298,6 +299,7 @@ public: void readClassList(gdioutput &gdi, xmlobject &xo, int &entRead, int &entFail); + void prescanCompetitorList(xmlobject &xo); void readCompetitorList(gdioutput &gdi, const xmlobject &xo, int &personCount); void readClubList(gdioutput &gdi, const xmlobject &xo, int &clubCount); diff --git a/code/listeditor.cpp b/code/listeditor.cpp index 3697e46..6773c10 100644 --- a/code/listeditor.cpp +++ b/code/listeditor.cpp @@ -225,7 +225,7 @@ void ListEditor::show(gdioutput &gdi) { gdi.fillDown(); try { - currentList->interpret(oe, gdi, par, gdi.getLineHeight(), li); + currentList->interpret(oe, gdi, par, li); rc.left = gdi.getCX(); rc.right = gdi.getCX() + gdi.getWidth() - 20; rc.top = gdi.getCY(); diff --git a/code/meosversion.cpp b/code/meosversion.cpp index d77aebd..7e33fe8 100644 --- a/code/meosversion.cpp +++ b/code/meosversion.cpp @@ -30,24 +30,18 @@ //V35: abcdef //V36: abcdef int getMeosBuild() { - string revision("$Rev: 972 $"); + string revision("$Rev: 1004 $"); return 174 + atoi(revision.substr(5, string::npos).c_str()); } -//ABCDEFGHIJKILMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz -//V2: abcdefgh -//V3: abcdefghijklmnopqrstuvxyz -//V31: abcde -//V32: abcdefgh -//V33: abcdefghij -//V34: abcdfge +//V37: a wstring getMeosDate() { - wstring date(L"$Date: 2020-01-18 15:14:04 +0100 (lö, 18 jan 2020) $"); + wstring date(L"$Date: 2020-02-25 21:05:03 +0100 (ti, 25 feb 2020) $"); return date.substr(7,10); } wstring getBuildType() { - return L"Beta 1"; // No parantheses (...) + return L"RC1"; // No parantheses (...) } wstring getMajorVersion() { @@ -149,5 +143,8 @@ void getSupporters(vector &supp, vector &developSupp) supp.emplace_back(L"Finspångs SOK"); supp.emplace_back(L"OK Gorm, Denmark"); supp.emplace_back(L"Nyköpings OK"); + supp.emplace_back(L"Thomas Engberg, VK Uvarna"); + supp.emplace_back(L"LG Axmalm, Sävedalens AIK"); + reverse(supp.begin(), supp.end()); } diff --git a/code/metalist.cpp b/code/metalist.cpp index 0dd2111..ab660c9 100644 --- a/code/metalist.cpp +++ b/code/metalist.cpp @@ -358,8 +358,7 @@ static void setFixedWidth(oPrintPost &added, added.fixedWidth = 0; } -void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par, - int lineHeight, oListInfo &li) const { +void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par, oListInfo &li) const { const MetaList &mList = *this; Position pos; const bool large = par.useLargeSize; @@ -376,19 +375,20 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par if (fontFaces[k].scale > 0 && fontFaces[k].scale != 100) { face += L";" + itow(fontFaces[k].scale/100) + L"." + itow(fontFaces[k].scale%100); } - fontHeight[make_pair(it->first, int(k))] = gdi.getLineHeight(it->first, face.c_str()); + fontHeight[make_pair(it->first, int(k))] = int(gdi.getLineHeight(it->first, face.c_str()) / gdi.getScale()); } } - + int lineHeight; if (large) { s_factor = 0.9; normal = fontLarge; header = boldLarge; small = normalText; - lineHeight = int(lineHeight *1.6); + lineHeight = 22; italic = italicMediumPlus; } else { + lineHeight = 14; s_factor = 1.0; normal = normalText; header = boldText; @@ -511,6 +511,7 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par oPrintPost::encodeFont(fontFaces[i].font, fontFaces[i].scale).c_str(), large, max(mp.blockWidth, extraMinWidth)); + ++linePostCount[make_pair(i, j)]; // Count how many positions on this line indexPosToWidth[tuple(i, j, k)] = width; } @@ -584,7 +585,7 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par resultToIndex.clear(); /*if (large == false && par.pageBreak == false) {*/ { - int head_dy = gdi.scaleLength(mList.getExtraSpace(MLHead)); + int head_dy = mList.getExtraSpace(MLHead); for (size_t j = 0; j &cline = mList.getHead()[j]; next_dy = 0; @@ -639,7 +640,7 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par } dy = lineHeight; - int subhead_dy = gdi.scaleLength(mList.getExtraSpace(MLSubHead)); + int subhead_dy = mList.getExtraSpace(MLSubHead); last = 0; base = 0; @@ -689,7 +690,7 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par last = 0; base = 0; - int list_dy = gdi.scaleLength(mList.getExtraSpace(MLList));//mList.getSubList().size() > 0 ? lineHeight/2 : 0; + int list_dy = mList.getExtraSpace(MLList); dy = 0; for (size_t j = 0; j &cline = mList.getList()[j]; @@ -731,7 +732,7 @@ void MetaList::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par dy += next_dy; } - int sublist_dy = gdi.scaleLength(mList.getExtraSpace(MLSubList)); + int sublist_dy = mList.getExtraSpace(MLSubList); last = 0; base = 0; dy = 0; @@ -2309,12 +2310,11 @@ wstring MetaListContainer::makeUniqueParamName(const wstring &nameIn) const { } -bool MetaListContainer::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par, - int lineHeight, oListInfo &li) const { +bool MetaListContainer::interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par, oListInfo &li) const { map::const_iterator it = globalIndex.find(par.listCode); if (it != globalIndex.end()) { - data[it->second].second.interpret(oe, gdi, par, lineHeight, li); + data[it->second].second.interpret(oe, gdi, par, li); return true; } return false; diff --git a/code/metalist.h b/code/metalist.h index 8450d09..94f57e5 100644 --- a/code/metalist.h +++ b/code/metalist.h @@ -307,8 +307,7 @@ public: void save(xmlparser &xml, const oEvent *oe) const; void load(const xmlobject &xDef); - void interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par, - int lineHeight, oListInfo &li) const; + void interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par, oListInfo &li) const; MetaList &setListName(const wstring &title) {listName = title; return *this;} @@ -424,8 +423,7 @@ public: void synchronizeTo(MetaListContainer &dst) const; - bool interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par, - int lineHeight, oListInfo &li) const; + bool interpret(oEvent *oe, const gdioutput &gdi, const oListParam &par, oListInfo &li) const; void enumerateLists(vector< pair > > &out) const; }; diff --git a/code/oBase.cpp b/code/oBase.cpp index c773d56..53e78a5 100644 --- a/code/oBase.cpp +++ b/code/oBase.cpp @@ -54,6 +54,59 @@ oBase::oBase(oEvent *poe) { localObject = false; } +oBase::oBase(const oBase &in) { + Removed = in.Removed; + oe = in.oe; + Id = in.Id; + changed = false; + counter = in.counter; + Modified.update(); + correctionNeeded = in.correctionNeeded; + localObject = in.localObject; + implicitlyAdded = in.implicitlyAdded; + addedToEvent = in.addedToEvent; + sqlUpdated = in.sqlUpdated; + localObject = in.localObject; + transientChanged = in.transientChanged; +} + +oBase::oBase(oBase &&in) { + Removed = in.Removed; + oe = in.oe; + Id = in.Id; + changed = false; + counter = in.counter; + Modified.update(); + correctionNeeded = in.correctionNeeded; + localObject = in.localObject; + implicitlyAdded = in.implicitlyAdded; + addedToEvent = in.addedToEvent; + sqlUpdated = in.sqlUpdated; + localObject = in.localObject; + transientChanged = in.transientChanged; + if (in.myReference) { + myReference.swap(in.myReference); + myReference->ref = this; + } +} + +const oBase &oBase::operator=(const oBase &in) { + Removed = in.Removed; + oe = in.oe; + Id = in.Id; + changed = false; + counter = in.counter; + Modified.update(); + correctionNeeded = in.correctionNeeded; + localObject = in.localObject; + implicitlyAdded = in.implicitlyAdded; + addedToEvent = in.addedToEvent; + sqlUpdated = in.sqlUpdated; + localObject = in.localObject; + transientChanged = in.transientChanged; + return *this; +} + oBase::~oBase(){ if (myReference) myReference->ref = nullptr; @@ -64,6 +117,7 @@ void oBase::remove() { myReference->ref = nullptr; } + bool oBase::synchronize(bool writeOnly) { if (oe && (changed || transientChanged)) { diff --git a/code/oBase.h b/code/oBase.h index 1899dbd..bedf2f4 100644 --- a/code/oBase.h +++ b/code/oBase.h @@ -65,21 +65,19 @@ public: }; private: - // Changed in client, not yet sent to server - bool changed; - // Changed in client, silent mode, should not be sent to server - bool transientChanged; - - bool localObject; - const static unsigned long long BaseGenStringFlag = 1ull << 63; - const static unsigned long long Base36StringFlag = 1ull << 62; - const static unsigned long long ExtStringMask = ~(BaseGenStringFlag|Base36StringFlag); - shared_ptr myReference; protected: int Id; TimeStamp Modified; string sqlUpdated; //SQL TIMESTAMP + +private: + const static unsigned long long BaseGenStringFlag = 1ull << 63; + const static unsigned long long Base36StringFlag = 1ull << 62; + const static unsigned long long ExtStringMask = ~(BaseGenStringFlag | Base36StringFlag); + shared_ptr myReference; + +protected: int counter; oEvent *oe; bool Removed; @@ -93,6 +91,11 @@ private: bool implicitlyAdded = false; bool addedToEvent = false; + // Changed in client, not yet sent to server + bool changed; + // Changed in client, silent mode, should not be sent to server + bool transientChanged; + bool localObject; protected: @@ -186,6 +189,9 @@ public: static __int64 converExtIdentifierString(const wstring &str); oBase(oEvent *poe); + oBase(const oBase &in); + oBase(oBase &&in); + const oBase &operator=(const oBase &in); virtual ~oBase(); friend class RunnerDB; diff --git a/code/oClub.cpp b/code/oClub.cpp index 8ea0cd6..b4e1e22 100644 --- a/code/oClub.cpp +++ b/code/oClub.cpp @@ -656,11 +656,14 @@ void oClub::generateInvoice(gdioutput &gdi, int &toPay, int &hasPaid, data.resPos = gdi.scaleLength(550); gdi.addString("", ys, xs+data.adrPos, boldHuge, "FAKTURA"); - if (number>0) - gdi.addStringUT(ys+lh*3, xs+data.adrPos, fontMedium, lang.tl("Faktura nr")+ L": " + itow(number)); + if (number > 0) { + gdi.addStringUT(ys + lh * 3, xs + data.adrPos, fontMedium, lang.tl("Faktura nr") + L": " + itow(number)); + gdi.addStringUT(ys + lh * 4, xs + data.adrPos, fontMedium, getInvoiceDate(*oe)); + } + int &yp = data.yp; - yp = ys+lh; + yp = ys+lh * 2; wstring ostreet = oe->getDI().getString("Street"); wstring oaddress = oe->getDI().getString("Address"); wstring oco = oe->getDI().getString("CareOf"); @@ -684,7 +687,7 @@ void oClub::generateInvoice(gdioutput &gdi, int &toPay, int &hasPaid, wstring city = getDCI().getString("ZIP") + L" " + getDCI().getString("City"); wstring country = getDCI().getString("Country"); - int ayp = ys + 122; + int ayp = ys + gdi.scaleLength(122); const int absX = oe->getPropertyInt("addressxpos", 125); int absY = oe->getPropertyInt("addressypos", 50); @@ -706,7 +709,7 @@ void oClub::generateInvoice(gdioutput &gdi, int &toPay, int &hasPaid, if (!country.empty()) gdi.addStringUT(ayp, xs+data.adrPos, fontMedium, country).setAbsPrintPos(absX,absY), ayp+=lh, absY+=absYL; - yp = ayp+30; + yp = ayp+gdi.scaleLength(36); gdi.addString("", yp, xs, boldSmall, "Deltagare"); gdi.addString("", yp, xs+data.clsPos, boldSmall, "Klass"); @@ -1148,3 +1151,17 @@ bool oClub::operator<(const oClub &c) const { name.c_str(), name.length(), c.name.c_str(), c.name.length()) == CSTR_LESS_THAN; } + +wstring oClub::getInvoiceDate(oEvent &oe) { + wstring invoiceDate = oe.getDCI().getDate("InvoiceDate"); + int invoiceDateI = oe.getDCI().getInt("InvoiceDate"); + if (invoiceDateI == 0) { + invoiceDate = getLocalDate(); + setInvoiceDate(oe, invoiceDate); + } + return invoiceDate; +} + +void oClub::setInvoiceDate(oEvent &oe, const wstring &id) { + oe.getDI().setDate("InvoiceDate", id); +} diff --git a/code/oClub.h b/code/oClub.h index 3fa453f..2fac595 100644 --- a/code/oClub.h +++ b/code/oClub.h @@ -147,10 +147,11 @@ public: static void assignInvoiceNumber(oEvent &oe, bool reset); static int getFirstInvoiceNumber(oEvent &oe); + static wstring getInvoiceDate(oEvent &oe); + static void setInvoiceDate(oEvent &oe, const wstring &id); static void definedPayModes(oEvent &oe, map &definedPayModes); - /** Remove all clubs from a competion (and all belong to club relations)*/ static void clearClubs(oEvent &oe); diff --git a/code/oEvent.cpp b/code/oEvent.cpp index 4b8ede5..e9f376e 100644 --- a/code/oEvent.cpp +++ b/code/oEvent.cpp @@ -249,7 +249,7 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) ZeroTime=st.wHour*3600; oe=this; - runnerDB = new RunnerDB(this); + runnerDB = make_shared(this); meosFeatures = new MeOSFeatures(); openFileLock = new MeOSFileLock(); @@ -326,7 +326,7 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oEventData->addVariableInt("CurrencyPreSymbol", oDataContainer::oIS8, "Symbolläge"); oEventData->addVariableString("CurrencyCode", 5, "Valutakod"); oEventData->addVariableInt("UTC", oDataContainer::oIS8, "UTC"); - + oEventData->addVariableInt("Analysis", oDataContainer::oIS8, "Utan analys"); // With split time analysis (0 = default, with analysis, with min/km) // bit 1: without analysis @@ -342,6 +342,8 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oEventData->addVariableInt("LongTimes", oDataContainer::oIS8U, "Långa tider"); oEventData->addVariableString("PayModes", "Betalsätt"); oEventData->addVariableInt("TransferFlags", oDataContainer::oIS32, "Överföring"); + oEventData->addVariableDate("InvoiceDate", "Fakturadatum"); + oEventData->initData(this, dataSize); oClubData=new oDataContainer(oClub::dataSize); @@ -520,9 +522,8 @@ oEvent::~oEvent() { //Clean up things in the right order. clear(); - delete runnerDB; + runnerDB.reset(); delete meosFeatures; - runnerDB = 0; meosFeatures = 0; delete oEventData; @@ -1595,6 +1596,17 @@ void oEvent::updateRunnerDatabase(pRunner r, map &clubIdMap) runnerDB->updateAdd(*r, clubIdMap); } +void oEvent::backupRunnerDatabase() { + if (!runnerDBCopy) + runnerDBCopy = make_shared(*runnerDB); +} + +void oEvent::restoreRunnerDatabase() { + if (runnerDBCopy && *runnerDB != *runnerDBCopy) { + runnerDB = make_shared(*runnerDBCopy); + } +} + pCourse oEvent::addCourse(const wstring &pname, int plengh, int id) { oCourse c(this, id); c.Length = plengh; @@ -2181,6 +2193,7 @@ void oEvent::setDate(const wstring &m, bool manualSet) throw meosException(L"Felaktigt datumformat 'X' (Använd ÅÅÅÅ-MM-DD).#" + m); wstring nDate = formatDate(d, true); if (Date != nDate) { + Date = nDate; if (manualSet) setFlag(TransferFlags::FlagManualDateTime, true); updateChanged(); @@ -2210,7 +2223,7 @@ const wstring &oEvent::getTimeZoneString() const { wstring oEvent::getAbsDateTimeISO(DWORD time, bool includeDate, bool useGMT) const { - DWORD t = ZeroTime + time; + int t = ZeroTime + time; wstring dateS, timeS; if (int(t)<0) { dateS = L"2000-01-01"; @@ -2888,7 +2901,7 @@ void oEvent::generateInForestList(gdioutput &gdi, GUICALLBACK cb, GUICALLBACK cb // Get a set with unknown runner id:s set statUnknown; for (oRunnerList::const_iterator itr=Runners.begin(); itr != Runners.end(); ++itr) { - if (itr->tStatus == StatusUnknown && !(itr->skip() || itr->needNoCard())) { + if (!itr->hasFinished() && !(itr->skip() || itr->needNoCard())) { statUnknown.insert(itr->getId()); } } @@ -2959,7 +2972,7 @@ void oEvent::generateInForestList(gdioutput &gdi, GUICALLBACK cb, GUICALLBACK cb if (it->skip() || it->needNoCard()) continue; - if (it->tStatus == StatusUnknown) { + if (!it->hasFinished()) { if (id != it->getClassId(true)) { if (nr>0) { @@ -5181,7 +5194,7 @@ void oEvent::generateTestCompetition(int nClasses, int nRunners, nRunners-=nRInClass; if (k%5!=5) { vector spec; - spec.push_back(ClassDrawSpecification(cls->getId(), 0, getRelativeTime(start), 10, 3)); + spec.emplace_back(cls->getId(), 0, getRelativeTime(start), 10, 3, VacantPosition::Mixed); drawList(spec, DrawMethod::MeOS, 1, oEvent::DrawType::DrawAll); } else @@ -5203,7 +5216,7 @@ void oEvent::generateTestCompetition(int nClasses, int nRunners, if ( cls->getStartType(0)==STDrawn ) { vector spec; - spec.push_back(ClassDrawSpecification(cls->getId(), 0, getRelativeTime(start), 20, 3)); + spec.emplace_back(cls->getId(), 0, getRelativeTime(start), 20, 3, VacantPosition::Mixed); drawList(spec, DrawMethod::MeOS, 1, DrawType::DrawAll); } } diff --git a/code/oEvent.h b/code/oEvent.h index 8c90910..1608514 100644 --- a/code/oEvent.h +++ b/code/oEvent.h @@ -263,7 +263,9 @@ protected: oRunnerList Runners; intkeymap runnerById; - RunnerDB *runnerDB; + shared_ptr runnerDB; + shared_ptr runnerDBCopy; + MeOSFeatures *meosFeatures; oCardList Cards; @@ -296,35 +298,6 @@ protected: SqlUpdated sqlPunches; SqlUpdated sqlTeams; - /* - string sqlUpdateRunners; - string sqlUpdateClasses; - string sqlUpdateCourses; - string sqlUpdateControls; - string sqlUpdateClubs; - string sqlUpdateCards; - string sqlUpdatePunches; - string sqlUpdateTeams; - - int sqlCounterRunners; - int sqlCounterClasses; - int sqlCounterCourses; - int sqlCounterControls; - int sqlCounterClubs; - int sqlCounterCards; - int sqlCounterPunches; - int sqlCounterTeams; - - bool sqlChangedRunners; - bool sqlChangedClasses; - bool sqlChangedCourses; - bool sqlChangedControls; - bool sqlChangedClubs; - bool sqlChangedCards; - bool sqlChangedPunches; - bool sqlChangedTeams; - */ - bool needReEvaluate(); DirectSocket *directSocket; @@ -496,6 +469,13 @@ public: DrawAll, RemainingBefore, RemainingAfter, }; + /** Where to put vacancies */ + enum class VacantPosition { + Mixed = 0, + First = 1, + Last = 2, + }; + /** Drawing algorithm. */ enum class DrawMethod { NOMethod = -1, @@ -553,9 +533,11 @@ public: void getExtraLines(const char *attrib, vector > &lines) const; RunnerDB &getRunnerDatabase() const {return *runnerDB;} + void backupRunnerDatabase(); + void restoreRunnerDatabase(); MeOSFeatures &getMeOSFeatures() const {return *meosFeatures;} - void getDBRunnersInEvent(intkeymap &runners) const; + void getDBRunnersInEvent(intkeymap &runners) const; MetaListContainer &getListContainer() const; wstring getNameId(int id) const; const wstring &getFileNameFromId(int id) const; @@ -607,8 +589,10 @@ public: // Automatic draw of all classes void automaticDrawAll(gdioutput &gdi, const wstring &firstStart, - const wstring &minIntervall, const wstring &vacances, - bool lateBefore, bool allowNeighbourSameCourse, DrawMethod method, int pairSize); + const wstring &minIntervall, + const wstring &vacances, VacantPosition vp, + bool lateBefore, bool allowNeighbourSameCourse, + DrawMethod method, int pairSize); // Restore a backup by renamning the file to .meos void restoreBackup(); @@ -728,10 +712,10 @@ public: void generateList(gdioutput &gdi, bool reEvaluate, const oListInfo &li, bool updateScrollBars); - void generateListInfo(oListParam &par, int lineHeight, oListInfo &li); - void generateListInfo(vector &par, int lineHeight, oListInfo &li); + void generateListInfo(oListParam &par, oListInfo &li); + void generateListInfo(vector &par, oListInfo &li); void generateListInfo(EStdListType lt, const gdioutput &gdi, int classId, oListInfo &li); - void generateListInfoAux(oListParam &par, int lineHeight, oListInfo &li, const wstring &name); + void generateListInfoAux(oListParam &par, oListInfo &li, const wstring &name); /** Format a string for a list. Returns true of output is not empty*/ const wstring &formatListString(const oPrintPost &pp, const oListParam &par, diff --git a/code/oEventDraw.cpp b/code/oEventDraw.cpp index 1a9b914..b05c09d 100644 --- a/code/oEventDraw.cpp +++ b/code/oEventDraw.cpp @@ -212,6 +212,7 @@ namespace { void drawMeOSMethod(vector &runners) { if (runners.empty()) return; + map> runnersPerClub; for (pRunner r : runners) runnersPerClub[r->getClubId()].push_back(r); @@ -960,7 +961,7 @@ void oEvent::drawRemaining(DrawMethod method, bool placeAfter) for (oClassList::iterator it = Classes.begin(); it != Classes.end(); ++it) { vector spec; - spec.push_back(ClassDrawSpecification(it->getId(), 0, 0, 0, 0)); + spec.emplace_back(it->getId(), 0, 0, 0, 0, VacantPosition::Mixed); drawList(spec, method, 1, drawType); } @@ -1120,6 +1121,21 @@ void oEvent::drawList(const vector &spec, if (gdibase.isTest()) InitRanom(0,0); + + vector vacant; + VacantPosition vp = spec[0].vacantPosition; + if (vp != VacantPosition::Mixed) { + // Move vacants to a dedicated container + for (size_t k = 0; k < runners.size(); k++) { + if (runners[k]->isVacant()) { + vacant.push_back(runners[k]); + swap(runners[k], runners.back()); + runners.pop_back(); + k--; + } + } + } + switch (method) { case DrawMethod::SOFT: drawSOFTMethod(runners, true); @@ -1128,12 +1144,28 @@ void oEvent::drawList(const vector &spec, drawMeOSMethod(runners); break; case DrawMethod::Random: - permute(stimes); + { + vector pv(runners.size()); + for (size_t k = 0; k < pv.size(); k++) + pv[k] = k; + permute(pv); + vector r2(runners.size()); + for (size_t k = 0; k < pv.size(); k++) + r2[k] = runners[pv[k]]; + r2.swap(runners); + } break; default: throw 0; } + if (vp == VacantPosition::First) { + runners.insert(runners.begin(), vacant.begin(), vacant.end()); + } + else if (vp == VacantPosition::Last) { + runners.insert(runners.end(), vacant.begin(), vacant.end()); + } + int minStartNo = Runners.size(); vector> newStartNo; for(unsigned k=0;kisRemoved()) continue; vector spec; - spec.push_back(ClassDrawSpecification(it->getId(), 0, iFirstStart, 0, 0)); + spec.emplace_back(it->getId(), 0, iFirstStart, 0, 0, vp); oe->drawList(spec, DrawMethod::Random, 1, DrawType::DrawAll); } return; @@ -1505,9 +1542,9 @@ void oEvent::automaticDrawAll(gdioutput &gdi, const wstring &firstStart, gdi.addString("", 0, L"Lottar: X#" + getClass(ci.classId)->getName()); vector spec; - spec.push_back(ClassDrawSpecification(ci.classId, leg, - di.firstStart + di.baseInterval * ci.firstStart, - di.baseInterval * ci.interval, ci.nVacant)); + spec.emplace_back(ci.classId, leg, + di.firstStart + di.baseInterval * ci.firstStart, + di.baseInterval * ci.interval, ci.nVacant, vp); drawList(spec, method, pairSize, DrawType::DrawAll); gdi.scrollToBottom(); @@ -1526,7 +1563,7 @@ void oEvent::automaticDrawAll(gdioutput &gdi, const wstring &firstStart, gdi.addStringUT(0, lang.tl(L"Lottar efteranmälda: ") + it->getName()); vector spec; - spec.push_back(ClassDrawSpecification(it->getId(), leg, 0, 0, 0)); + spec.emplace_back(it->getId(), leg, 0, 0, 0, vp); drawList(spec, method, 1, lateBefore ? DrawType::RemainingBefore : DrawType::RemainingAfter); gdi.scrollToBottom(); diff --git a/code/oEventDraw.h b/code/oEventDraw.h index 3b84136..cb713d8 100644 --- a/code/oEventDraw.h +++ b/code/oEventDraw.h @@ -22,6 +22,8 @@ ************************************************************************/ +#include "oEvent.h" + struct ClassDrawSpecification { int classID; int leg; @@ -29,11 +31,11 @@ struct ClassDrawSpecification { mutable int interval; int vacances; mutable int ntimes; - - ClassDrawSpecification() : ntimes(0) {} - ClassDrawSpecification(int classID, int leg, int firstStart, int interval, int vacances) : + oEvent::VacantPosition vacantPosition; + ClassDrawSpecification() : ntimes(0), vacantPosition(oEvent::VacantPosition::Mixed) {} + ClassDrawSpecification(int classID, int leg, int firstStart, int interval, int vacances, oEvent::VacantPosition vp) : classID(classID), leg(leg), firstStart(firstStart), - interval(interval), vacances(vacances), ntimes(0) {} + interval(interval), vacances(vacances), ntimes(0), vacantPosition(vp) {} }; diff --git a/code/oEventResult.cpp b/code/oEventResult.cpp index 6a9dd6a..bdbdd67 100644 --- a/code/oEventResult.cpp +++ b/code/oEventResult.cpp @@ -69,7 +69,7 @@ template void calculatePlace(vectorgetClassRef(true); - useResults = cls ? cls->getNoTiming() == false : true; + useResults = true;// cls ? cls->getNoTiming() == false : true; invalidClass = cls ? cls->getClassStatus() != oClass::Normal : false; } diff --git a/code/oImportExport.cpp b/code/oImportExport.cpp index bda9c67..a755e6e 100644 --- a/code/oImportExport.cpp +++ b/code/oImportExport.cpp @@ -51,9 +51,17 @@ #include #include "localizer.h" #include "iof30interface.h" +#include "gdiconstants.h" #include "meosdb/sqltypes.h" +FlowOperation importFilterGUI(oEvent *oe, + gdioutput & gdi, + const set& stages, + const vector &idProviders, + set & filter, + string &preferredIdProvider); + string conv_is(int i) { char bf[256]; @@ -1276,6 +1284,22 @@ void oEvent::importXML_IOF_Data(const wstring &clubfile, if (xo && xo.getAttrib("iofVersion")) { IOF30Interface reader(this, false); + + vector idProviders; + reader.prescanCompetitorList(xo); + reader.getIdTypes(idProviders); + + if (idProviders.size() > 1) { + string preferredIdProvider; + set dmy; + FlowOperation op = importFilterGUI(oe, gdibase, + {}, idProviders, + dmy, preferredIdProvider); + if (op != FlowContinue) + return; + + reader.setPreferredIdType(preferredIdProvider); + } reader.readCompetitorList(gdibase, xo, personCount); } else { @@ -1306,8 +1330,6 @@ void oEvent::importXML_IOF_Data(const wstring &clubfile, gdibase.addString("", 0, "Uppdaterar serverns databas..."); gdibase.refresh(); - //msUploadRunnerDB(this); - OpFailStatus stat = (OpFailStatus)msUploadRunnerDB(this); if (stat == opStatusFail) { diff --git a/code/oListInfo.cpp b/code/oListInfo.cpp index 5d6ea47..0e7a884 100644 --- a/code/oListInfo.cpp +++ b/code/oListInfo.cpp @@ -471,7 +471,6 @@ int oListInfo::getMaxCharWidth(const oEvent *oe, } } - int width = minSize; vector row(pps.size(), 0); vector samples(pps.size()); wstring totWord = L""; @@ -527,11 +526,8 @@ int oListInfo::getMaxCharWidth(const oEvent *oe, wstring dummy; int w = totMeasure.measure(gdi, font, fontFace, dummy); - w = max(w, minSize); - if (large) - return w + 5; - else - return w + 15; + w = max(w, gdi.scaleLength(minSize)); + return int(0.5 + (w + (large ? 5 : 15))/gdi.getScale()); } const wstring & oEvent::formatListString(EPostType type, const pRunner r) const @@ -2416,7 +2412,6 @@ bool oEvent::formatPrintPost(const list &ppli, PrintPostInfo &ppi, } - if (pp.color != colorDefault) ti->setColor(pp.color); } @@ -3719,7 +3714,7 @@ void oEvent::generateListInfo(EStdListType lt, const gdioutput &gdi, int classId par.listCode=lt; - generateListInfo(par, gdi.getLineHeight(), li); + generateListInfo(par, li); } int openRunnerTeamCB(gdioutput *gdi, int type, void *data); @@ -3822,15 +3817,14 @@ void oListInfo::setCallback(GUICALLBACK cb) { } } -void oEvent::generateListInfo(oListParam &par, int lineHeight, oListInfo &li) { +void oEvent::generateListInfo(oListParam &par, oListInfo &li) { vector parV(1, par); - generateListInfo(parV, lineHeight, li); + generateListInfo(parV, li); } -void oEvent::generateListInfo(vector &par, int lineHeight, oListInfo &li) { +void oEvent::generateListInfo(vector &par, oListInfo &li) { li.getParam().sourceParam = -1;// Reset source loadGeneralResults(false, false); - lineHeight = 14; for (size_t k = 0; k < par.size(); k++) { par[k].cb = 0; } @@ -3839,7 +3833,7 @@ void oEvent::generateListInfo(vector &par, int lineHeight, oListInfo getListTypes(listMap, false); if (par.size() == 1) { - generateListInfoAux(par[0], lineHeight, li, listMap[par[0].listCode].Name); + generateListInfoAux(par[0], li, listMap[par[0].listCode].Name); set used; // Add linked lists oListParam *cPar = &par[0]; @@ -3851,7 +3845,7 @@ void oEvent::generateListInfo(vector &par, int lineHeight, oListInfo oListParam &nextPar = oe->getListContainer().getParam(cPar->nextList-1); li.next.push_back(oListInfo()); nextPar.cb = 0; - generateListInfoAux(nextPar, lineHeight, li.next.back(), L""); + generateListInfoAux(nextPar, li.next.back(), L""); cPar = &nextPar; } } @@ -3860,14 +3854,14 @@ void oEvent::generateListInfo(vector &par, int lineHeight, oListInfo if (k > 0) { li.next.push_back(oListInfo()); } - generateListInfoAux(par[k], lineHeight, k == 0 ? li : li.next.back(), + generateListInfoAux(par[k], k == 0 ? li : li.next.back(), li.Name = listMap[par[0].listCode].Name); } } } -void oEvent::generateListInfoAux(oListParam &par, int lineHeight, oListInfo &li, const wstring &name) { - const int lh=lineHeight; +void oEvent::generateListInfoAux(oListParam &par, oListInfo &li, const wstring &name) { + const int lh=14; const int vspace=lh/2; int bib; pair ln; @@ -3896,7 +3890,7 @@ void oEvent::generateListInfoAux(oListParam &par, int lineHeight, oListInfo &li, switch (lt) { case EStdStartList: { - li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Startlista - %s")), boldLarge, 0,0)); + li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Startlista - %s", true)), boldLarge, 0,0)); li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25)); int bib = 0; @@ -3926,7 +3920,7 @@ void oEvent::generateListInfoAux(oListParam &par, int lineHeight, oListInfo &li, } case EStdClubStartList: { - li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Klubbstartlista - %s")), boldLarge, 0,0)); + li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Klubbstartlista - %s", true)), boldLarge, 0,0)); li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25)); if (hasBib(true, true)) { @@ -3964,7 +3958,7 @@ void oEvent::generateListInfoAux(oListParam &par, int lineHeight, oListInfo &li, } case EStdClubResultList: { - li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Klubbresultatlista - %s")), boldLarge, 0,0)); + li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Klubbresultatlista - %s", true)), boldLarge, 0,0)); li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25)); pos.add("class", li.getMaxCharWidth(this, par.selection, lClassName, L"", normalText)); @@ -3994,7 +3988,7 @@ void oEvent::generateListInfoAux(oListParam &par, int lineHeight, oListInfo &li, case EStdRentedCard: { - li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Hyrbricksrapport - %s")), boldLarge, 0,0)); + li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Hyrbricksrapport - %s", true)), boldLarge, 0,0)); li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25)); li.addListPost(oPrintPost(lTotalCounter, L"%s", normalText, 0, 0)); @@ -4334,7 +4328,7 @@ void oEvent::generateListInfoAux(oListParam &par, int lineHeight, oListInfo &li, mList.addToSubList(lRunnerName); mList.addToSubList(lRunnerCard).align(lClassStartName); - mList.interpret(this, gdibase, par, lh, li); + mList.interpret(this, gdibase, par, li); } li.listType=li.EBaseTypeTeam; li.listSubType=li.EBaseTypeRunner; @@ -4583,7 +4577,7 @@ void oEvent::generateListInfoAux(oListParam &par, int lineHeight, oListInfo &li, mList.setListType(li.EBaseTypeTeam); mList.setSortOrder(ClassStartTime); mList.addFilter(EFilterExcludeDNS); - mList.interpret(this, gdibase, par, lh, li); + mList.interpret(this, gdibase, par, li); break; } case EStdPatrolResultList: @@ -4793,10 +4787,10 @@ void oEvent::generateListInfoAux(oListParam &par, int lineHeight, oListInfo &li, continue; par.setLegNumberCoded(out[k].second); if (k == 0) - generateListInfo(par, lineHeight, li); + generateListInfo(par, li); else { li.next.push_back(oListInfo()); - generateListInfo(par, lineHeight, li.next.back()); + generateListInfo(par, li.next.back()); } } } @@ -4814,7 +4808,7 @@ void oEvent::generateListInfoAux(oListParam &par, int lineHeight, oListInfo &li, break; default: - if (!getListContainer().interpret(this, gdibase, par, lineHeight, li)) + if (!getListContainer().interpret(this, gdibase, par, li)) throw std::exception("Not implemented"); } } diff --git a/code/oRunner.cpp b/code/oRunner.cpp index 9ca6774..9fe2730 100644 --- a/code/oRunner.cpp +++ b/code/oRunner.cpp @@ -1557,6 +1557,8 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, *refStatus = StatusOutOfCompetition; else if (hasFlag(TransferFlags::FlagNoTiming)) *refStatus = StatusNoTiming; + else if (clz && clz->getNoTiming()) + *refStatus = StatusNoTiming; } // Adjust times on course, including finish time doAdjustTimes(course); @@ -4128,6 +4130,11 @@ void oRunner::fillSpeakerObject(int leg, int courseControlId, int previousContro getSplitTime(courseControlId, spk.status, spk.runningTime.time); + if (getStatus() == StatusNoTiming || getStatus() == StatusOutOfCompetition) { + if (spk.status == StatusOK) + spk.status = getStatus(); + } + if (courseControlId == oPunch::PunchFinish) spk.timeSinceChange = oe->getComputerTime() - FinishTime; else @@ -4380,7 +4387,7 @@ void oEvent::analyseDNS(vector &unknown_dns, vector &known_dns for (oRunnerList::iterator it = Runners.begin(); it!=Runners.end();++it) { if (!it->isRemoved() && !it->needNoCard()) { - if (it->getStatus() == StatusUnknown) + if (!it->hasFinished()) stUnknown.push_back(&*it); else if (it->getStatus() == StatusDNS) { stDNS.push_back(&*it); @@ -5909,14 +5916,14 @@ void oRunner::setInputData(const oRunner &r) { } } -void oEvent::getDBRunnersInEvent(intkeymap &runners) const { +void oEvent::getDBRunnersInEvent(intkeymap &runners) const { runners.clear(); for (oRunnerList::const_iterator it = Runners.begin(); it != Runners.end(); ++it) { if (it->isRemoved()) continue; __int64 id = it->getExtIdentifier(); if (id != 0) - runners.insert(id, it->Class); + runners.insert(id, it->getId()); } } @@ -6338,6 +6345,7 @@ void oAbstractRunner::getInputResults(vector &st, // Add current result to input result. Only use when transferring to next stage void oAbstractRunner::addToInputResult(int thisStageNo, const oAbstractRunner *src) { + thisStageNo = max(thisStageNo, 0); int p = src->getPlace(); int rt = src->getRunningTime(true); RunnerStatus st = src->getStatusComputed(); @@ -6594,7 +6602,7 @@ int oRunner::getCheckTime() const { const pair oRunner::getRaceInfo() { pair res; RunnerStatus baseStatus = getStatus(); - if (baseStatus != StatusUnknown) { + if (hasFinished()) { int p = getPlace(); int rtComp = getRunningTime(true); int rtActual = getRunningTime(false); diff --git a/code/oRunner.h b/code/oRunner.h index 17b18f6..16c4e1c 100644 --- a/code/oRunner.h +++ b/code/oRunner.h @@ -408,6 +408,7 @@ public: else return true; } + /** Sets the status. If updatePermanent is true, the stored start time is updated, otherwise the value is considered deduced. */ @@ -646,7 +647,6 @@ protected: bool isHiredCard(int card) const; public: - static const shared_ptr
&getTable(oEvent *oe); // Get the leg defineing parallel results for this runner (in a team) @@ -655,6 +655,17 @@ public: // Returns true if there are radio control results, provided result calculation oEvent::ResultType::PreliminarySplitResults was invoked. bool hasOnCourseResult() const { return !tOnCourseResults.empty() || getFinishTime() > 0 || hasResult(); } + /** Return true if the race is completed (or definitely never will be started), e.g., not in forest*/ + bool hasFinished() const { + if (tStatus == StatusUnknown) + return false; + else if (isPossibleResultStatus(tStatus)) { + return Card || FinishTime > 0; + } + else + return true; + } + /** Returns a check time (or zero for no time). */ int getCheckTime() const; diff --git a/code/oTeam.cpp b/code/oTeam.cpp index 8592545..8551050 100644 --- a/code/oTeam.cpp +++ b/code/oTeam.cpp @@ -805,6 +805,7 @@ bool oTeam::compareSNO(const oTeam &a, const oTeam &b) { return a.Class->tSortIndex < b.Class->tSortIndex || (a.Class->tSortIndex == b.Class->tSortIndex && a.Class->Id < b.Class->Id); else return true; } + else return false; } return CompareString(LOCALE_USER_DEFAULT, 0, @@ -2382,8 +2383,9 @@ oTeam::TeamPlace &oTeam::getTeamPlace(int leg) const { if (size_t(leg) < tPlace.size()) return tPlace[leg]; - if (tComputedResults.empty()) + if (tComputedResults.empty() || tPlace.empty()) tPlace.resize(1); + return tPlace[0]; } diff --git a/code/pdfwriter.cpp b/code/pdfwriter.cpp index d0988b0..c9fc4a0 100644 --- a/code/pdfwriter.cpp +++ b/code/pdfwriter.cpp @@ -290,16 +290,18 @@ void pdfwriter::generatePDF(const gdioutput &gdi, string nt = gdi.toUTF8(info[k].ti.text); if (info[k].ti.format & textRight) { - float w = float(info[k].ti.xlimit) * scale*fontScale; + float w = float(info[k].ti.xlimit) * scale; float sw = HPDF_Page_TextWidth(page, nt.c_str()); - float space = info[k].ti.xlimit > 0 ? 2 * HPDF_Page_GetCharSpace(page) : 0; + //float space = info[k].ti.xlimit > 0 ? 2 * HPDF_Page_GetCharSpace(page) : 0; + float space = info[k].ti.xlimit > 0 ? HPDF_Page_TextWidth(page, "-") : 0; + HPDF_Page_TextOut (page, info[k].xp + w - sw - space, h - info[k].yp, nt.c_str()); } else if (info[k].ti.format & textCenter) { - float w = float(info[k].ti.xlimit) * scale*fontScale; + float w = float(info[k].ti.xlimit) * scale; float sw = HPDF_Page_TextWidth(page, nt.c_str()); - HPDF_Page_TextOut (page, info[k].xp + w - sw/2, h - info[k].yp, + HPDF_Page_TextOut (page, info[k].xp + (w - sw) *0.5, h - info[k].yp, nt.c_str()); } else { diff --git a/code/restserver.cpp b/code/restserver.cpp index 76e1940..ac445f4 100644 --- a/code/restserver.cpp +++ b/code/restserver.cpp @@ -21,15 +21,18 @@ Eksoppsvägen 16, SE-75646 UPPSALA, Sweden ************************************************************************/ #include "stdafx.h" + +#include +#include +#include + #include "oEvent.h" #include "xmlparser.h" -#include #include "restbed/restbed" #include "meosexception.h" #include "restserver.h" #include "infoserver.h" -#include #include "oListInfo.h" #include "TabList.h" @@ -376,6 +379,16 @@ void RestServer::computeInternal(oEvent &ref, shared_ptrparameters.find("lookup")->second; lookup(ref, what, rq->parameters, rq->answer); } + else if (rq->parameters.count("difference")) { + string what = rq->parameters.find("difference")->second; + int id = -2; + if (what == "zero") + id = -1; + else + id = atoi(what.c_str()); + + difference(ref, id, rq->answer); + } else if (rq->parameters.count("page") > 0) { string what = rq->parameters.find("page")->second; auto &writer = HTMLWriter::getWriter(HTMLWriter::TemplateType::Page, what); @@ -424,7 +437,7 @@ void RestServer::computeInternal(oEvent &ref, shared_ptrsecond.second) { res->second.second = make_shared(); - ref.generateListInfo(res->second.first, gdiPrint.getLineHeight(), *res->second.second); + ref.generateListInfo(res->second.first, *res->second.second); } ref.generateList(gdiPrint, true, *res->second.second, false); wstring exportFile = getTempFile(); @@ -1100,7 +1113,7 @@ void RestServer::lookup(oEvent &oe, const string &what, const multimapgetCardNo()); xml.write("Status", {make_pair("code", itow(r->getStatusComputed()))}, r->getStatusS(true, true)); xml.write("Start", r->getStartTimeS()); - if (r->getFinishTime() > 0) { + if (r->getFinishTime() > 0 && r->getStatusComputed() != StatusNoTiming) { xml.write("Finish", r->getFinishTimeS()); xml.write("RunningTime", r->getRunningTimeS(true)); xml.write("Place", r->getPlaceS()); @@ -1112,7 +1125,9 @@ void RestServer::lookup(oEvent &oe, const string &what, const multimapgetLegNumber(r->getLegNumber())); } - if ((r->getFinishTime() > 0 || r->getCard() != nullptr) && r->getCourse(false)) { + if ((r->getFinishTime() > 0 || r->getCard() != nullptr) && + r->getCourse(false) && + r->getStatusComputed() != StatusNoTiming) { auto &sd = r->getSplitTimes(false); vector after; r->getLegTimeAfter(after); @@ -1342,7 +1357,11 @@ void RestServer::newEntry(oEvent &oe, const multimap ¶m, str if (epType == EntryPermissionType::InDbExistingClub && clubId == 0) { error = L"Anmälan måste hanteras manuellt"; } - + + bool noTiming = false; + if (param.count("notiming")) + noTiming = true; + int cardNo = 0; if (param.count("card")) cardNo = atoi(param.find("card")->second.c_str()); @@ -1374,6 +1393,9 @@ void RestServer::newEntry(oEvent &oe, const multimap ¶m, str } r->setFlag(oRunner::FlagAddedViaAPI, true); r->addClassDefaultFee(true); + if (noTiming) + r->setStatus(StatusNoTiming, true, oBase::ChangeType::Update, false); + r->synchronize(); r->markClassChanged(-1); xml.write("Status", "OK"); @@ -1383,6 +1405,8 @@ void RestServer::newEntry(oEvent &oe, const multimap ¶m, str } xml.write("Fee", rentCard, itow(r->getDCI().getInt("Fee") + max(cf, 0))); xml.write("Info", r->getClass(true) + L", " + r->getCompleteIdentification(false)); + if (r->getStatus() == StatusNoTiming) + xml.write("NoTiming", "true"); } } } @@ -1413,3 +1437,131 @@ vector> RestServer::getPermissionsClass() { res.emplace_back(lang.tl("Med direktanmälan"), size_t(EntryPermissionClass::DirectEntry)); return res; } + + +//constexpr int largePrime = 5000011; +//constexpr int smallPrime = 101; + +constexpr int largePrime = 5000011; +constexpr int smallPrime = 101; +constexpr int idOffset = 100; + +int RestServer::InfoServerContainer::getNextInstanceId() { + + int newInstanceId = (thisInstanceId - idOffset + instanceIncrementor) % (largePrime * smallPrime); + return newInstanceId + idOffset; +} + +int RestServer::getNewInstanceId() { + int limit = 10; + if (isContainers.size() > 10) { + isContainers.pop_back(); + } + + set usedBaseId; + for (auto &c : isContainers) { + int baseId = (c.nextInstanceId - idOffset) % smallPrime; + usedBaseId.insert(baseId); + OutputDebugString((L"used: " + itow(baseId) + L"\n").c_str()); + } + if (!randGen) { + random_device r; + randGen = make_shared(r()); + } + int first = (*randGen)() % smallPrime; + int off = 1 + (*randGen)() % (smallPrime-1); + for (int i = 0; i < smallPrime; i++) { + int s = (first + i * off) % smallPrime; + if (!usedBaseId.count(s)) { + int id = s + smallPrime * ((*randGen)() % 32767) + idOffset; + int f = smallPrime * ((113 + (*randGen)() % 32767) % (largePrime - 1) + 1); + OutputDebugString((L"add: " + itow(s) + L", " + itow(f) + L"\n").c_str()); + + isContainers.emplace_front(id, f); + return id; + } + } + + throw meosException("No server instance found"); +} + +xmlbuffer * RestServer::getMOPXML(oEvent &oe, int id, int &nextId) { + for (auto it = isContainers.begin(); it != isContainers.end(); ++it) { + auto &c = *it; + if (c.thisInstanceId == id) { + nextId = c.nextInstanceId; + if (it != isContainers.begin()) + isContainers.splice(isContainers.begin(), isContainers, it); + + return c.lastData.get(); + } + else if (c.nextInstanceId == id) { + if (it != isContainers.begin()) + isContainers.splice(isContainers.begin(), isContainers, it); + + oe.autoSynchronizeLists(true); + if (!c.cmpModel) { + c.cmpModel = make_shared(id); + if (c.classes.empty()) { + vector classes; + oe.getClasses(classes, false); + for (pClass pc : classes) { + if (!pc->isQualificationFinalBaseClass()) + c.classes.insert(pc->getId()); + } + } + if (c.controls.empty()) { + vector ctrl; + vector ids; + oe.getControls(ctrl, true); + for (size_t k = 0; k < ctrl.size(); k++) { + if (ctrl[k]->isValidRadio()) { + ctrl[k]->getCourseControls(ids); + c.controls.insert(ids.begin(), ids.end()); + } + } + } + } + c.cmpModel->synchronize(oe, false, c.classes, c.controls, true); + c.lastData = make_shared(); + c.cmpModel->getDiffXML(*c.lastData); + c.cmpModel->commitComplete(); + + if (c.lastData->size() == 0) { + nextId = c.nextInstanceId; + return c.lastData.get(); + } + + c.thisInstanceId = id; + nextId = c.nextInstanceId = c.getNextInstanceId(); + return c.lastData.get(); + } + } + return nullptr; +} + +void RestServer::difference(oEvent &oe, int id, string &answer) { + string type; + if (id == -1) { + id = getNewInstanceId(); + } + int nextId; + xmlbuffer *bf = getMOPXML(oe, id, nextId); + if (bf) { + xmlparser mem; + mem.openMemoryOutput(false); + if (bf->isComplete()) + type = "MOPComplete"; + else + type = "MOPDiff"; + + mem.startTag(type.c_str(), { L"xmlns", L"http://www.melin.nu/mop", + L"nextdifference", itow(nextId)}); + bf->commitCopy(mem); + mem.endTag(); + mem.getMemoryOutput(answer); + } + else { + answer = "Error (MeOS): Unknown difference state. Use litteral 'zero' (?difference=zero) to get complete competition"; + } +} diff --git a/code/restserver.h b/code/restserver.h index cc8d8a6..ebd0531 100644 --- a/code/restserver.h +++ b/code/restserver.h @@ -31,6 +31,10 @@ Eksoppsvägen 16, SE-75646 UPPSALA, Sweden #include #include #include +#include + +class InfoCompetition; +class xmlbuffer; namespace restbed { class Service; @@ -111,6 +115,29 @@ private: string root; multimap rootMap; + + struct InfoServerContainer { + //static int currentInstanceId; + int getNextInstanceId(); + + const int instanceIncrementor; + int thisInstanceId = -1; + int nextInstanceId; + shared_ptr cmpModel; + shared_ptr lastData; + set classes; + set controls; + + InfoServerContainer(int s, int e) : nextInstanceId(s), instanceIncrementor(e) {} + }; + + shared_ptr randGen; + list isContainers; + int getNewInstanceId(); + xmlbuffer *getMOPXML(oEvent &oe, int id, int &nextId); + + void difference(oEvent &oe, int id, string &answer); + public: ~RestServer(); diff --git a/code/speakermonitor.cpp b/code/speakermonitor.cpp index c5de455..d25c199 100644 --- a/code/speakermonitor.cpp +++ b/code/speakermonitor.cpp @@ -53,7 +53,7 @@ void SpeakerMonitor::setClassFilter(const set &filter, const set &cfil classFilter = filter; controlIdFilter = cfilter; oListInfo li; - maxClassNameWidth = li.getMaxCharWidth(&oe, classFilter, lClassName, L"", normalText, false); + maxClassNameWidth = oe.gdiBase().scaleLength(li.getMaxCharWidth(&oe, classFilter, lClassName, L"", normalText, false)); } void SpeakerMonitor::setLimits(int place, int num) { diff --git a/code/swedish.lng b/code/swedish.lng index 926797a..87e33b0 100644 --- a/code/swedish.lng +++ b/code/swedish.lng @@ -2428,7 +2428,7 @@ prefsLastExportTarget = Senaste exportmål prefsServiceRootMap = Standardfunktion för webbserverns root prefsshowheader = Visa sidrubriker i listor Lagändringblankett = Lagändringblankett -Mappa rootadresssen (http:///localhost:port/) till funktion = Mappa rootadresssen (http:///localhost:port/) till funktion +Mappa rootadressen (http:///localhost:port/) till funktion = Mappa rootadressen (http:///localhost:port/) till funktion ClassAvailableMaps = Klassens lediga kartor ClassTotalMaps = Klassens antal kartor Patrol overtime = Patrulls tid övertid @@ -2482,4 +2482,21 @@ Status code for cancelled entry = Statuskod för återbud Age (on last day of current year) = Ålder vid årets slut Age above or equal implies senior/pensioner = Åldersgräns äldre/pensionär Age below or equal implies youth = Åldersgräns ungdom -Status code for not no timing = Statuskod för utan tidtagning +Det finns multiplia Id-nummer för personer = Det finns multiplia Id-nummer för personer +Välj vilken typ du vill importera = Välj vilken typ du vill importera +Status code for no timing = Statuskod för utan tidtagning +prefsVacantPercent = Andel vakanser +Före X = Före X +Efter X = Efter X +Slå ihop X = Slå ihop X +Slå ihop med = Slå ihop med +Mellan X och Y = Mellan X och Y +Ett okänt fel inträffade = Ett okänt fel inträffade +Antalet kolumner i urklippet är större än antalet kolumner i tabellen = Antalet kolumner i urklippet är större än antalet kolumner i tabellen +Antalet kolumner i urklipp får inte plats i selektionen = Fler kolumner i urklippet än i selektionen +prefsVacantPosition = Placering av vakanser +Vakansplacering = Vakansplacering +Först = Först +Lottat = Lottat +Sist = Sist +Fakturadatum = Fakturadatum diff --git a/code/testmeos.cpp b/code/testmeos.cpp index 6a90b3d..cb6a359 100644 --- a/code/testmeos.cpp +++ b/code/testmeos.cpp @@ -137,12 +137,11 @@ void TestMeOS::runProtected(bool protect) const { oe_main->setProperty("TestPath", tp); oe_main->getPropertyInt("UseEventor", 1); oe_main->setProperty("Interactive", 0); + oe_main->backupRunnerDatabase(); TabSI *tsi = dynamic_cast(gdi_main->getTabs().get(TabType::TSITab)); tsi->setMode(TabSI::ModeReadOut); tsi->clearQueue(); - //string pmOrig = oe_main->getPropertyString("PayModes", ""); - //oe_main->setProperty("PayModes", ""); - + OutputDebugString((L"Running test" + gdi_main->widen(test) + L"\n").c_str()); try { status = RUNNING; @@ -161,20 +160,20 @@ void TestMeOS::runProtected(bool protect) const { catch (const meosAssertionFailure & ex) { OutputDebugString(L"Test FAILED (assert)\n"); status = FAILED; + oe_main->restoreRunnerDatabase(); oe_main->useDefaultProperties(false); gdi_main->clearDialogAnswers(false); gdi_main->dbRegisterSubCommand(0, ""); gdi_main->isTestMode = false; subWindows.clear(); message = ex.message; - //oe_main->setProperty("PayModes", pmOrig); if (!protect) throw meosException(message); } catch (const std::exception &ex) { status = FAILED; OutputDebugString(L"Test FAILED (std)\n"); - + oe_main->restoreRunnerDatabase(); oe_main->useDefaultProperties(false); gdi_main->clearDialogAnswers(false); gdi_main->dbRegisterSubCommand(0, ""); @@ -189,18 +188,18 @@ void TestMeOS::runProtected(bool protect) const { OutputDebugString(L"Test FAILED (...)\n"); status = FAILED; + oe_main->restoreRunnerDatabase(); oe_main->useDefaultProperties(false); gdi_main->clearDialogAnswers(false); gdi_main->dbRegisterSubCommand(0, ""); gdi_main->isTestMode = false; subWindows.clear(); - //oe_main->setProperty("PayModes", pmOrig); message = L"Unknown Exception"; cleanup(); if (!protect) throw; } - //oe_main->setProperty("PayModes", pmOrig); + oe_main->restoreRunnerDatabase(); oe_main->useDefaultProperties(false); gdi_main->dbRegisterSubCommand(0, ""); for (size_t k = 0; k < tmpFiles.size(); k++)