MeOS version 3.6.1069

This commit is contained in:
Erik Melin 2019-05-11 21:11:36 +02:00
parent e8e5e499f2
commit 55d526c3e4
59 changed files with 2282 additions and 544 deletions

View File

@ -46,6 +46,8 @@ void RestService::save(oEvent &oe, gdioutput &gdi) {
int xport = gdi.getTextNo("Port");
if (xport > 0 && xport < 65536) {
oe.setProperty("ServicePort", xport);
port = xport;
server->startService(port);
}
@ -53,6 +55,12 @@ void RestService::save(oEvent &oe, gdioutput &gdi) {
throw meosException("Invalid port number");
}
if (gdi.isChecked("MapRoot")) {
rootMap = gdi.recodeToNarrow(gdi.getText("RootMap"));
server->setRootMap(rootMap);
oe.setProperty("ServiceRootMap", gdi.getText("RootMap"));
}
if (gdi.isChecked("AllowEntry")) {
RestServer::EntryPermissionType pt = (RestServer::EntryPermissionType)gdi.getSelectedItem("PermissionPerson").first;
RestServer::EntryPermissionClass pc = (RestServer::EntryPermissionClass)gdi.getSelectedItem("PermissionClass").first;
@ -67,9 +75,10 @@ void RestService::save(oEvent &oe, gdioutput &gdi) {
void ListIpAddresses(vector<string>& ip);
void RestService::settings(gdioutput &gdi, oEvent &oe, bool created) {
if (port == -1)
if (port == -1) {
port = oe.getPropertyInt("ServicePort", 2009);
rootMap = oe.getPropertyString("ServiceRootMap", "");
}
settingsTitle(gdi, "MeOS Informationsserver REST-API");
//gdi.fillRight();
@ -87,10 +96,15 @@ 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.addInput("RootMap", gdi.recodeToWide(rootMap));
gdi.setInputStatus("RootMap", !rootMap.empty());
startCancelInterval(gdi, "Save", created, IntervalNone, L"");
if (!server)
if (!server) {
gdi.addInput("Port", itow(port), 10, 0, L"Port:", L"#http://localhost:[PORT]/meos");
}
else {
gdi.addString("", 0, "Server startad på X#" + itos(port));
auto per = server->getEntryPermission();
@ -180,6 +194,9 @@ void RestService::handle(gdioutput &gdi, BaseInfo &info, GuiEventType type) {
gdi.setInputStatus("PermissionPerson", gdi.isChecked(bi.id));
gdi.setInputStatus("PermissionClass", gdi.isChecked(bi.id));
}
else if (bi.id == "MapRoot") {
gdi.setInputStatus("RootMap", gdi.isChecked(bi.id));
}
}
else if (type == GUI_LINK) {
wstring url = ((TextInfo &)info).text;

View File

@ -31,7 +31,7 @@ class RestService :
{
int port;
shared_ptr<RestServer> server;
string rootMap;
public:
void save(oEvent &oe, gdioutput &gdi) override;

View File

@ -1040,7 +1040,7 @@ void PunchMachine::process(gdioutput &gdi, oEvent *oe, AutoSyncType ast)
auto pc = r->getCourse(false);
if (radio < 10 || pc->hasControlCode(radio)) {
pp.clear();
oe->getPunchesForRunner(r->getId(), pp);
oe->getPunchesForRunner(r->getId(), false, pp);
bool hit = false;
for (auto p : pp) {
if (p->getTypeCode() == radio)

View File

@ -467,9 +467,11 @@ int TabClass::multiCB(gdioutput &gdi, int type, void *data)
oe->setupRelay(*pc, newType, nstages, st);
if (gdi.hasField("MAdd")) {
gdi.enableInput("MAdd");
gdi.enableInput("MCourses");
gdi.enableInput("MRemove");
for (const char *s : {"MCourses", "StageCourses", "MAdd", "MRemove", "MUp", "MDown"}) {
if (gdi.hasField(s)) {
gdi.enableInput(s);
}
}
}
pc->forceShowMultiDialog(true);
selectClass(gdi, pc->getId());
@ -506,11 +508,21 @@ int TabClass::multiCB(gdioutput &gdi, int type, void *data)
if (gdi.getSelectedItem("MCourses", lbi)) {
int courseid=lbi.data;
pc->addStageCourse(currentStage, courseid);
int ix = -1;
ListBoxInfo selS;
if (gdi.getSelectedItem("StageCourses", selS)) {
ix = selS.index+1;
}
pc->addStageCourse(currentStage, courseid, ix);
pc->fillStageCourses(gdi, currentStage, "StageCourses");
if (ix != -1)
gdi.selectItemByIndex("StageCourses", ix);
pc->synchronize();
oe->checkOrderIdMultipleCourses(cid);
setLockForkingState(gdi, *pc);
}
}
EditChanged=true;
@ -530,18 +542,67 @@ int TabClass::multiCB(gdioutput &gdi, int type, void *data)
ListBoxInfo lbi;
if (gdi.getSelectedItem("StageCourses", lbi)) {
int courseid=lbi.data;
pc->removeStageCourse(currentStage, courseid, lbi.index);
int ix = lbi.index;
pc->removeStageCourse(currentStage, courseid, ix);
pc->synchronize();
pc->fillStageCourses(gdi, currentStage, "StageCourses");
if (ix > 0)
gdi.selectItemByIndex("StageCourses", ix-1);
setLockForkingState(gdi, *pc);
}
}
}
else if (bi.id == "MUp" || bi.id == "MDown") {
DWORD cid = ClassId;
if (!checkClassSelected(gdi))
return false;
pClass pc = oe->getClass(cid);
if (!pc)
return false;
if (currentStage >= 0) {
int ix = -1;
ListBoxInfo selS;
if (gdi.getSelectedItem("StageCourses", selS)) {
ix = selS.index;
}
if (ix != -1) {
int off = bi.id == "MUp" ? -1 : 1;
pc->moveStageCourse(currentStage, ix, off);
pc->synchronize();
pc->fillStageCourses(gdi, currentStage, "StageCourses");
gdi.selectItemByIndex("StageCourses", ix + off);
}
}
setLockForkingState(gdi, *pc);
EditChanged = true;
}
EditChanged=true;
}
else if (type == GUI_LISTBOXSELECT) {
const ListBoxInfo &bi = *(ListBoxInfo *)data;
if (bi.id == "MCourses") {
gdi.sendCtrlMessage("MAdd");
}
else if (bi.id == "StageCourses") {
//gdi.sendCtrlMessage("MRemove");
}
}
else if (type==GUI_LISTBOX) {
ListBoxInfo bi=*(ListBoxInfo *)data;
if (bi.id.substr(0, 7)=="LegType") {
if (bi.id == "StageCourses") {
pClass pc = oe->getClass(ClassId);
if (!pc)
return false;
setLockForkingState(gdi, *pc);
}
else if (bi.id.substr(0, 7)=="LegType") {
LegTypes lt = LegTypes(bi.data);
int i=atoi(bi.id.substr(7).c_str());
@ -956,7 +1017,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
vector<pClass> cls;
oe->getClasses(cls, true);
for (size_t k = 0; k < cls.size(); k++) {
if (cls[k]->getStart() == start)
if (cls[k]->getStart() == start && !cls[k]->hasFreeStart())
lst.insert(cls[k]->getId());
}
gdi.setSelection("Classes", lst);
@ -1805,6 +1866,18 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
gdi.dropLine();
int tot, fin, dns;
oe->getNumClassRunners(pc->getId(), 0, tot, fin, dns);
if (pc->isQualificationFinalBaseClass()) {
set<int> base;
pc->getQualificationFinal()->getBaseClassInstances(base);
for (int i : base) {
if (pc->getVirtualClass(i)) {
int tot2 = 0;
oe->getNumClassRunners(pc->getVirtualClass(i)->getId(), 0, tot2, fin, dns);
tot += tot2;
}
}
}
gdi.addString("", fontMediumPlus, "Antal deltagare: X#" + itos(tot));
gdi.dropLine(1.2);
gdi.pushX();
@ -1826,9 +1899,6 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
for (int i : base) {
if (pc->getVirtualClass(i)) {
gdi.addStringUT(0, pc->getVirtualClass(i)->getName());
int tot2 = 0;
oe->getNumClassRunners(pc->getVirtualClass(i)->getId(), 0, tot2, fin, dns);
tot += tot2;
}
}
numSplitDef = base.size();
@ -1844,7 +1914,6 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
gdi.selectFirstItem("SplitInput");
gdi.dropLine(3);
gdi.popX();
}
updateSplitDistribution(gdi, numSplitDef, tot);
@ -2260,58 +2329,77 @@ void TabClass::showClassSettings(gdioutput &gdi)
int y = 0;
int xp = gdi.getCX();
const int width = 80;
const int width = gdi.scaleLength(80);
int classW = gdi.scaleLength(300);
vector<wstring> str(cInfo.size());
for (size_t k = 0; k < cInfo.size(); k++) {
auto &ci = cInfo[k];
wchar_t bf1[128];
wchar_t bf2[128];
int cstart = ci.firstStart + (ci.nRunners - 1) * ci.interval;
wstring first = oe->getAbsTime(ci.firstStart*drawInfo.baseInterval + drawInfo.firstStart);
wstring last = oe->getAbsTime((cstart)*drawInfo.baseInterval + drawInfo.firstStart);
pClass pc = oe->getClass(ci.classId);
swprintf_s(bf1, L"%s, %d", pc ? pc->getName().c_str() : L"-", ci.nRunners);
swprintf_s(bf2, L"%d-[%d]-%d (%s-%s)", ci.firstStart, ci.interval, cstart, first.c_str(), last.c_str());
str[k] = L"X platser. Startar Y#" + wstring(bf1) + L"#" + bf2;
TextInfo ti;
ti.xp = 0;
ti.yp = 0;
ti.format = 0;
ti.text = str[k];
gdi.calcStringSize(ti);
classW = max(classW, ti.realWidth + gdi.scaleLength(10));
}
if (!cInfo.empty()) {
gdi.dropLine();
y = gdi.getCY();
gdi.addString("", y, xp, 1, "Sammanställning, klasser:");
gdi.addString("", y, xp+300, 0, "Första start:");
gdi.addString("", y, xp+300+width, 0, "Intervall:");
gdi.addString("", y, xp+300+width*2, 0, "Vakanser:");
gdi.addString("", y, xp+300+width*3, 0, "Reserverade:");
gdi.addString("", y, xp + classW, 0, "Första start:");
gdi.addString("", y, xp + classW + width, 0, "Intervall:");
gdi.addString("", y, xp + classW + width * 2, 0, "Vakanser:");
gdi.addString("", y, xp + classW + width * 3, 0, "Reserverade:");
}
gdi.pushX();
for (size_t k=0;k<cInfo.size();k++) {
for (size_t k = 0; k < cInfo.size(); k++) {
const ClassInfo &ci = cInfo[k];
wchar_t bf1[128];
wchar_t bf2[128];
int cstart = ci.firstStart + (ci.nRunners-1) * ci.interval;
wstring first=oe->getAbsTime(ci.firstStart*drawInfo.baseInterval+drawInfo.firstStart);
wstring last=oe->getAbsTime((cstart)*drawInfo.baseInterval+drawInfo.firstStart);
pClass pc=oe->getClass(ci.classId);
swprintf_s(bf1, L"%s, %d", pc ? pc->getName().c_str() : L"-", ci.nRunners);
swprintf_s(bf2, L"%d-[%d]-%d (%s-%s)", ci.firstStart, ci.interval, cstart, first.c_str(), last.c_str());
int cstart = ci.firstStart + (ci.nRunners - 1) * ci.interval;
wstring first = oe->getAbsTime(ci.firstStart*drawInfo.baseInterval + drawInfo.firstStart);
wstring last = oe->getAbsTime((cstart)*drawInfo.baseInterval + drawInfo.firstStart);
pClass pc = oe->getClass(ci.classId);
gdi.fillRight();
int id = ci.classId;
GDICOLOR clr = ci.hasFixedTime || ci.nExtraSpecified || ci.nVacantSpecified ? colorDarkGreen : colorBlack;
gdi.addString("C" + itos(id), 0, L"X platser. Startar Y#" + wstring(bf1) + L"#" + bf2).setColor(clr).setExtra(clr);
gdi.addString("C" + itos(id), 0, str[k]).setColor(clr).setExtra(clr);
y = gdi.getCY();
InputInfo *ii;
GDICOLOR fixedColor = colorLightGreen;
ii = &gdi.addInput(xp+300, y, "S"+itos(id), first, 7, DrawClassesCB);
ii = &gdi.addInput(xp + classW, y, "S" + itos(id), first, 7, DrawClassesCB);
if (ci.hasFixedTime) {
ii->setBgColor(fixedColor).setExtra(fixedColor);
}
ii = &gdi.addInput(xp+300+width, y, "I"+itos(id), formatTime(ci.interval*drawInfo.baseInterval), 7, DrawClassesCB);
ii = &gdi.addInput(xp + classW + width, y, "I" + itos(id), formatTime(ci.interval*drawInfo.baseInterval), 7, DrawClassesCB);
if (ci.hasFixedTime) {
ii->setBgColor(fixedColor).setExtra(fixedColor);
}
ii = &gdi.addInput(xp+300+width*2, y, "V"+itos(id), itow(ci.nVacant), 7, DrawClassesCB);
ii = &gdi.addInput(xp + classW + width * 2, y, "V" + itos(id), itow(ci.nVacant), 7, DrawClassesCB);
if (ci.nVacantSpecified) {
ii->setBgColor(fixedColor).setExtra(fixedColor);
}
ii = &gdi.addInput(xp+300+width*3, y, "R"+itos(id), itow(ci.nExtra), 7, DrawClassesCB);
ii = &gdi.addInput(xp + classW + width * 3, y, "R" + itos(id), itow(ci.nExtra), 7, DrawClassesCB);
if (ci.nExtraSpecified) {
ii->setBgColor(fixedColor).setExtra(fixedColor);
}
if (k%5 == 4)
if (k % 5 == 4)
gdi.dropLine(1);
gdi.dropLine(1.6);
@ -2414,7 +2502,8 @@ void TabClass::selectClass(gdioutput &gdi, int cid)
gdi.check("LockStartList", false);
gdi.setInputStatus("LockStartList", false);
}
gdi.check("NoTiming", false);
if (gdi.hasField("NoTiming"))
gdi.check("NoTiming", false);
ClassId=cid;
EditChanged=false;
@ -2455,7 +2544,8 @@ void TabClass::selectClass(gdioutput &gdi, int cid)
}
gdi.check("AllowQuickEntry", pc->getAllowQuickEntry());
gdi.check("NoTiming", pc->getNoTiming());
if (gdi.hasField("NoTiming"))
gdi.check("NoTiming", pc->getNoTiming());
if (gdi.hasField("FreeStart"))
gdi.check("FreeStart", pc->hasFreeStart());
@ -2890,7 +2980,8 @@ void TabClass::save(gdioutput &gdi, bool skipReload)
pc->lockedForking(gdi.isChecked("LockForking"));
pc->setAllowQuickEntry(gdi.isChecked("AllowQuickEntry"));
pc->setNoTiming(gdi.isChecked("NoTiming"));
if (gdi.hasField("NoTiming"))
pc->setNoTiming(gdi.isChecked("NoTiming"));
if (gdi.hasField("FreeStart"))
pc->setFreeStart(gdi.isChecked("FreeStart"));
@ -2901,7 +2992,8 @@ void TabClass::save(gdioutput &gdi, bool skipReload)
if (gdi.hasField("DirectResult")) {
bool withDirect = gdi.isChecked("DirectResult");
if (withDirect && !pc->hasDirectResult() && !hasWarnedDirect) {
if (withDirect && !pc->hasDirectResult() && !hasWarnedDirect &&
!oe->getMeOSFeatures().hasFeature(MeOSFeatures::NoCourses)) {
if (gdi.ask(L"warning:direct_result"))
hasWarnedDirect = true;
else
@ -3135,20 +3227,24 @@ bool TabClass::loadPage(gdioutput &gdi)
gdi.popX();
gdi.dropLine(3.5);
gdi.addCheckbox("AllowQuickEntry", "Tillåt direktanmälan", 0);
gdi.addCheckbox("NoTiming", "Utan tidtagning", 0);
if (showAdvanced || !oe->getMeOSFeatures().hasFeature(MeOSFeatures::NoCourses)) {
gdi.addCheckbox("NoTiming", "Utan tidtagning", 0);
}
if (showAdvanced) {
gdi.dropLine(2);
gdi.popX();
gdi.addCheckbox("FreeStart", "Fri starttid", 0, false, "Klassen lottas inte, startstämpling");
gdi.addCheckbox("IgnoreStart", "Ignorera startstämpling", 0, false, "Uppdatera inte starttiden vid startstämpling");
gdi.dropLine(2);
gdi.popX();
}
if (showAdvanced || oe->getMeOSFeatures().hasFeature(MeOSFeatures::NoCourses)) {
gdi.addCheckbox("DirectResult", "Resultat vid målstämpling", 0, false,
"help:DirectResult");
}
gdi.dropLine(2);
gdi.popX();
@ -3794,7 +3890,7 @@ void TabClass::simultaneous(int classId, const wstring &time) {
pCourse crs = pc->getCourse();
pc->setNumStages(1);
if (crs)
pc->addStageCourse(0, crs->getId());
pc->addStageCourse(0, crs->getId(), -1);
}
pc->setStartType(0, STTime, false);
@ -3845,18 +3941,23 @@ void TabClass::selectCourses(gdioutput &gdi, int legNo) {
}
gdi.fillRight();
int x1=gdi.getCX();
gdi.addListBox("StageCourses", 180, 200, MultiCB, getCourseLabel(pc->hasCoursePool())).ignore(true);
gdi.addListBox("StageCourses", 240, 200, MultiCB, getCourseLabel(pc->hasCoursePool())).ignore(true);
pc->fillStageCourses(gdi, currentStage, "StageCourses");
int x2=gdi.getCX();
gdi.fillDown();
gdi.addListBox("MCourses", 180, 200, MultiCB, L"Banor:").ignore(true);
gdi.addListBox("MCourses", 240, 200, MultiCB, L"Banor:").ignore(true);
oe->fillCourses(gdi, "MCourses", true);
gdi.setCX(x1);
gdi.fillRight();
gdi.addButton("MRemove", "Ta bort markerad >>", MultiCB);
gdi.setCX(x2);
gdi.addButton(gdi.getCX(), gdi.getCY(), gdi.scaleLength(30), "MUp", L"#▲", MultiCB, L"Flytta upp", false, false);
gdi.addButton(gdi.getCX(), gdi.getCY(), gdi.scaleLength(30), "MDown", L"#▼", MultiCB, L"Flytta ner", false, false);
gdi.disableInput("MUp");
gdi.disableInput("MDown");
gdi.setCX(max(x2, gdi.getCY()));
gdi.fillDown();
gdi.addButton("MAdd", "<< Lägg till", MultiCB);
@ -4331,6 +4432,10 @@ void TabClass::writeDrawInfo(gdioutput &gdi, const DrawInfo &drawInfoIn) {
gdi.setText("FirstStart", oe->getAbsTime(drawInfoIn.firstStart));
}
void TabClass::setLockForkingState(gdioutput &gdi, const oClass &c) {
setLockForkingState(gdi, c.hasCoursePool(), c.lockedForking());
}
void TabClass::setLockForkingState(gdioutput &gdi, bool poolState, bool lockState) {
if (gdi.hasField("DefineForking"))
gdi.setInputStatus("DefineForking", !lockState && !poolState);
@ -4348,6 +4453,27 @@ void TabClass::setLockForkingState(gdioutput &gdi, bool poolState, bool lockStat
gdi.setInputStatus(s, !lockState || poolState);
}
}
bool moveUp = false;
bool moveDown = false;
if (gdi.hasField("MCourses")) {
ListBoxInfo lbi;
if (gdi.getSelectedItem("StageCourses", lbi)) {
if (lbi.index > 0)
moveUp = true;
int numItem = gdi.getNumItems("StageCourses");
if (lbi.index < numItem - 1)
moveDown = true;
}
}
if (gdi.hasField("MUp")) {
gdi.setInputStatus("MUp", (!lockState || poolState) && moveUp);
}
if (gdi.hasField("MDown")) {
gdi.setInputStatus("MDown", (!lockState || poolState) && moveDown);
}
}
bool TabClass::warnDrawStartTime(gdioutput &gdi, const wstring &firstStart) {

View File

@ -150,6 +150,7 @@ class TabClass :
static vector< pair<wstring, size_t> > getPairOptions();
void setLockForkingState(gdioutput &gdi, bool poolState, bool lockState);
void setLockForkingState(gdioutput &gdi, const oClass &c);
void loadBasicDrawSetup(gdioutput &gdi, int &bx, int &by, const wstring& firstStart,
int maxNumControl, const wstring& minInterval, const wstring& vacances, const set<int> &clsId);

View File

@ -383,6 +383,9 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
wstring url = ti.text;
ShellExecute(NULL, L"open", url.c_str(), NULL, NULL, SW_SHOWNORMAL);
}
else if (ti.id == "fnpath") {
ShellExecute(NULL, L"open", ti.text.c_str(), NULL, NULL, SW_SHOWNORMAL);
}
}
else if (type==GUI_BUTTON) {
ButtonInfo bi=*(ButtonInfo *)data;
@ -558,17 +561,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
gdi.refresh();
}
else if (bi.id=="Test") {
vector< pair<wstring, wstring> > cpp;
cpp.push_back(make_pair(L"Source", L"*.cpp"));
int ix = 0;
wstring fn = gdi.browseForSave(cpp, L".cpp", ix);
if (!fn.empty())
gdi.getRecorder().saveRecordings(gdi.narrow(fn));
else {
TestMeOS tm(oe, "base");
tm.runAll();
tm.publish(gdi);
}
checkRentCards(gdi);
}
else if (bi.id=="Report") {
gdi.clearPage(true);
@ -2076,7 +2069,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
// Construct runner from database
oRunner sRunner(oe, 0);
sRunner.init(*dbr);
sRunner.init(*dbr, false);
pRunner added = oe->addRunnerFromDB(&sRunner, classId, true);
if (added)
added->synchronize();
@ -2375,8 +2368,25 @@ void TabCompetition::copyrightLine(gdioutput &gdi) const
void TabCompetition::loadAboutPage(gdioutput &gdi) const
{
gdi.clearPage(false);
gdi.addString("", 2, makeDash(L"Om MeOS - ett Mycket Enkelt OrienteringsSystem")).setColor(colorDarkBlue);
gdi.dropLine(2);
gdi.addString("", textImage, "513");
gdi.addString("", fontMediumPlus, makeDash(L"Om MeOS - ett Mycket Enkelt OrienteringsSystem")).setColor(colorDarkBlue);
gdi.dropLine(1);
wchar_t FileNamePath[260];
getUserFile(FileNamePath, L"");
gdi.pushX();
gdi.fillRight();
gdi.addString("", 0, "MeOS lokala datakatalog är: ");
gdi.fillDown();
gdi.addString("fnpath", 0, FileNamePath, CompetitionCB);
gdi.popX();
gdi.dropLine(0.5);
RECT rc = { gdi.getCX(), gdi.getCY(), gdi.getPageX(), gdi.getCY() + 2 };
gdi.addRectangle(rc, colorBlack);
gdi.dropLine(1.5);
gdi.setCX(gdi.getCX() + gdi.scaleLength(20));
gdi.addStringUT(1, makeDash(L"Copyright © 2007-2019 Melin Software HB"));
gdi.dropLine();
gdi.addStringUT(10, "The database connection used is MySQL++\nCopyright "
@ -4115,3 +4125,34 @@ void TabCompetition::checkReadyForResultExport(gdioutput &gdi, const set<int> &c
}
}
void TabCompetition::checkRentCards(gdioutput &gdi) {
gdi.clearPage(false);
wstring fn = gdi.browseForOpen({ make_pair(L"csv", L"*.csv") }, L"csv");
if (!fn.empty()) {
csvparser csv;
list<vector<wstring>> data;
csv.parse(fn, data);
set<int> rentCards;
for (auto &c : data) {
if (c.size() > 0) {
int cn = _wtoi(c[0].c_str());
rentCards.insert(cn);
}
}
vector<pRunner> runners;
oe->getRunners(0, 0, runners);
int bcf = oe->getBaseCardFee();
for (pRunner r : runners) {
if (rentCards.count(r->getCardNo()) && r->getDCI().getInt("CardFee") == 0) {
gdi.addStringUT(0, r->getCompleteIdentification());
r->getDI().setInt("CardFee", bcf);
}
}
}
gdi.dropLine();
gdi.addButton("Cancel", "OK", CompetitionCB);
gdi.refresh();
}

View File

@ -139,6 +139,10 @@ class TabCompetition :
void createCompetition(gdioutput &gdi);
void listBackups(gdioutput &gdi);
void checkRentCards(gdioutput &gdi);
protected:
void clearCompetitionData();

View File

@ -914,7 +914,7 @@ void TabCourse::runCourseImport(gdioutput& gdi, const wstring &filename,
}
else {
for (size_t i = 0; i<cls[k]->getNumStages(); i++)
cls[k]->addStageCourse(i, res->second->getId());
cls[k]->addStageCourse(i, res->second->getId(), -1);
}
}
else {
@ -939,7 +939,7 @@ void TabCourse::runCourseImport(gdioutput& gdi, const wstring &filename,
}
else {
for (size_t i = 0; i<bestClass->getNumStages(); i++)
bestClass->addStageCourse(i, crs[k]->getId());
bestClass->addStageCourse(i, crs[k]->getId(), -1);
}
}
}

View File

@ -56,6 +56,13 @@
const static int CUSTOM_OFFSET = 10;
const static int NUMTEXTSAMPLE = 13;
const static int ForcePageBreak = 1024;
const static int IgnoreLimitPer = 512;
const static int AddTeamClasses = 4;
const static int AddPatrolClasses = 3;
const static int AddRogainingClasses = 2;
const static int AddAllClasses = 1;
TabList::TabList(oEvent *poe):TabBase(poe)
{
listEditor = 0;
@ -209,9 +216,12 @@ void TabList::generateList(gdioutput &gdi, bool forceUpdate)
gdi.getData("GeneralList", storedWidth);
gdi.restoreNoUpdate("GeneralList");
}
else
else {
gdi.clearPage(false);
if (currentList.getParam().filterMaxPer > 0 && !ownWindow && !gdi.isFullScreen()) {
gdi.addInfoBox("infofilter", L"Visar de X bästa#" + itow(currentList.getParam().filterMaxPer), 6000, 0);
}
}
gdi.setRestorePoint("GeneralList");
currentList.setCallback(ownWindow ? 0 : openRunnerTeamCB);
@ -519,10 +529,12 @@ int TabList::listCB(gdioutput &gdi, int type, void *data)
else if (bi.id == "RemoveSaved") {
ListBoxInfo lbi;
if (gdi.getSelectedItem("SavedInstance", lbi)) {
oe->synchronize(false);
oe->getListContainer().removeParam(lbi.data);
oe->synchronize(true);
loadPage(gdi);
if (gdi.ask(L"Vill du ta bort 'X'?#" + lbi.text)) {
oe->synchronize(false);
oe->getListContainer().removeParam(lbi.data);
oe->synchronize(true);
loadPage(gdi);
}
}
return 0;
}
@ -888,6 +900,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data)
par.back().listCode = EIndCourseList;
par.back().showInterTitle = false;
par.back().setLegNumberCoded(-1);
cnf.getIndividual(par.back().selection);
}
@ -938,13 +951,22 @@ int TabList::listCB(gdioutput &gdi, int type, void *data)
bi.id.substr(0, 7) == "StartL:" ||
bi.id.substr(0, 7) == "GenLst:") {
bool isReport = bi.id.substr(0, 7) == "GenLst:";
bool allClasses = bi.getExtraInt() == 1;
bool rogaining = bi.getExtraInt() == 2;
bool patrol = bi.getExtraInt() == 3;
int baseType = bi.getExtraInt() & 0xFF;
int flags = bi.getExtraInt() & 0xFF00;
bool isStartList = bi.id.substr(0, 7) == "StartL:";
bool allClasses = baseType == AddAllClasses;
bool rogaining = baseType == AddRogainingClasses;
bool patrol = baseType == AddPatrolClasses;
bool team = baseType == AddTeamClasses;
oe->sanityCheck(gdi, bi.id.substr(0, 7) == "Result:");
oListParam par;
par.listCode = oe->getListContainer().getType(bi.id.substr(7));
readSettings(gdi, par, true);
readSettings(gdi, par, !isStartList);
if ((flags & IgnoreLimitPer) == IgnoreLimitPer || isReport)
par.filterMaxPer = 0;
if ((flags & ForcePageBreak) == ForcePageBreak)
par.pageBreak = true;
par.setLegNumberCoded(-1);
if (patrol) {
@ -957,6 +979,11 @@ int TabList::listCB(gdioutput &gdi, int type, void *data)
oe->getClassConfigurationInfo(cnf);
cnf.getRogaining(par.selection);
}
else if (team) {
ClassConfigInfo cnf;
oe->getClassConfigurationInfo(cnf);
cnf.getRelay(par.selection);
}
else if (!isReport && !allClasses) {
ClassConfigInfo cnf;
oe->getClassConfigurationInfo(cnf);
@ -1872,20 +1899,20 @@ namespace {
gdi.addString("", 0, info);
gdi.dropLine(0.3);
gdi.fillRight();
gdi.addInput("Margin", itow(tmpSettingsParam.margin) + L" %", 5, 0, L"Marginal:");
gdi.addInput("Scale", itow(int(tmpSettingsParam.htmlScale*100)) + L" %", 5, 0, L"Skalfaktor:");
gdi.addInput("Margin", itow(tmpSettingsParam.margin) + L" %", 5, 0, L"Marginal:").setHandler(&htmlClass);
gdi.addInput("Scale", itow(int(tmpSettingsParam.htmlScale*100)) + L" %", 5, 0, L"Skalfaktor:").setHandler(&htmlClass);
if (tmpSettingsParam.nColumns <= 0)
tmpSettingsParam.nColumns = 1;
gdi.addInput("Columns", itow(tmpSettingsParam.nColumns), 5, 0, L"Kolumner:");
gdi.addInput("Time", itow(tmpSettingsParam.timePerPage) + L"ms", 5, 0, L"Visningstid:");
gdi.addInput("Columns", itow(tmpSettingsParam.nColumns), 5, 0, L"Kolumner:").setHandler(&htmlClass);
gdi.addInput("Time", itow(tmpSettingsParam.timePerPage) + L"ms", 5, 0, L"Visningstid:").setHandler(&htmlClass);
gdi.popX();
gdi.dropLine(3.4);
gdi.addCheckbox("UseRows", "Begränsa antal rader per sida", 0, tmpSettingsParam.htmlRows>0).setHandler(&htmlClass);
gdi.dropLine(-0.4);
gdi.addInput("Rows", itow(tmpSettingsParam.htmlRows), 5);
gdi.addInput("Rows", itow(tmpSettingsParam.htmlRows), 5).setHandler(&htmlClass);
gdi.setInputStatus("Rows", tmpSettingsParam.htmlRows > 0);
gdi.popX();
@ -1893,13 +1920,13 @@ namespace {
}
else {
gdi.fillRight();
gdi.addInput("Scale", itow(int(tmpSettingsParam.htmlScale * 100)) + L" %", 5, 0, L"Skalfaktor:");
gdi.addInput("Scale", itow(int(tmpSettingsParam.htmlScale * 100)) + L" %", 5, 0, L"Skalfaktor:").setHandler(&htmlClass);
gdi.popX();
gdi.dropLine(3.4);
gdi.addCheckbox("Reload", "Automatisk omladdning", 0, tmpSettingsParam.timePerPage>999).setHandler(&htmlClass);
gdi.dropLine(-0.4);
gdi.addInput("ReloadTime", itow(tmpSettingsParam.timePerPage/1000) + L" s", 5);
gdi.addInput("ReloadTime", itow(tmpSettingsParam.timePerPage/1000) + L" s", 5).setHandler(&htmlClass);
gdi.setInputStatus("Reload", tmpSettingsParam.timePerPage>999);
gdi.popX();
@ -1907,6 +1934,8 @@ namespace {
}
gdi.fillRight();
gdi.addButton("ApplyList", "Lagra inställningar").setHandler(&htmlClass);
if (tmpSettingsParam.sourceParam != -1)
gdi.disableInput("ApplyList");
gdi.addButton("Automatic", "Automatisera", 0, "Skriv ut eller exportera listan automatiskt.").setHandler(&htmlClass);
gdi.addButton("HTML", "Exportera").setHandler(&htmlClass);
}
@ -2021,6 +2050,7 @@ void TabList::handleHTMLSettings(gdioutput &gdi, BaseInfo &info, GuiEventType ty
else {
dest_gdi.sendCtrlMessage("Remember");
}
gdi.disableInput("ApplyList");
}
else {
if (lastHtmlTarget.empty()) {
@ -2055,18 +2085,24 @@ void TabList::handleHTMLSettings(gdioutput &gdi, BaseInfo &info, GuiEventType ty
}
else if (bi.id == "UseRows") {
gdi.setInputStatus("Rows", gdi.isChecked(bi.id));
gdi.enableInput("ApplyList");
}
else if (bi.id == "Reload") {
gdi.setInputStatus("ReloadTime", gdi.isChecked(bi.id));
gdi.enableInput("ApplyList");
}
}
else if (type == GUI_LISTBOX) {
ListBoxInfo lbi = dynamic_cast<ListBoxInfo &>(info);
if (lbi.id == "Format") {
htmlDetails(gdi, tmpSettingsParam, html2IdToInfo[lbi.data], lbi.data > 5);
gdi.enableInput("ApplyList");
gdi.refresh();
}
}
else if (type == GUI_INPUTCHANGE) {
gdi.enableInput("ApplyList");
}
}
void TabList::loadClassSettings(gdioutput &gdi, string targetTag) {
@ -2345,7 +2381,7 @@ bool TabList::loadPage(gdioutput &gdi)
}
if (cnf.hasPatrol()) {
checkWidth(gdi);
gdi.addButton("StartL:patrolstart", "Patrull", ListsCB).setExtra(3);
gdi.addButton("StartL:patrolstart", "Patrull", ListsCB).setExtra(AddPatrolClasses);
}
for (size_t k = 0; k < cnf.raceNStart.size(); k++) {
if (cnf.raceNStart[k].size() > 0) {
@ -2386,7 +2422,7 @@ bool TabList::loadPage(gdioutput &gdi)
}
if (cnf.hasPatrol()) {
gdi.addButton("Result:patrolresult", "Patrull", ListsCB).setExtra(3);
gdi.addButton("Result:patrolresult", "Patrull", ListsCB).setExtra(AddPatrolClasses);
checkWidth(gdi);
}
@ -2395,7 +2431,7 @@ bool TabList::loadPage(gdioutput &gdi)
gdi.addButton("Result:liveresultradio", "Liveresultat", ListsCB);
checkWidth(gdi);
gdi.addButton("Result:latestresult", "Latest Results", ListsCB).setExtra(1);
gdi.addButton("Result:latestresult", "Latest Results", ListsCB).setExtra(AddAllClasses);
checkWidth(gdi);
if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Clubs)) {
@ -2457,8 +2493,7 @@ bool TabList::loadPage(gdioutput &gdi)
if (cnf.hasRogaining()) {
checkWidth(gdi);
//gdi.addButton("RogainingResultList", "Rogaining", ListsCB);
gdi.addButton("Result:rogainingind", "Rogaining", ListsCB).setExtra(2);
gdi.addButton("Result:rogainingind", "Rogaining", ListsCB).setExtra(AddRogainingClasses);
}
checkWidth(gdi);
@ -2519,24 +2554,33 @@ bool TabList::loadPage(gdioutput &gdi)
gdi.fillRight();
gdi.pushX();
gdi.addButton("InForestList", "Kvar-i-skogen", ListsCB, "tooltip:inforest");
gdi.addButton("InForestList", "Kvar-i-skogen", ListsCB, "tooltip:inforest").setExtra(IgnoreLimitPer);
if (cnf.hasIndividual()) {
gdi.addButton("PriceList", "Prisutdelningslista", ListsCB);
}
gdi.addButton("PreReport", "Kör kontroll inför tävlingen...", ListsCB);
checkWidth(gdi);
if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Economy) && cnf.hasIndividual()) {
gdi.addButton("GenLst:unexpectedfee", "Unexpected Fee", ListsCB);
checkWidth(gdi);
}
if (cnf.hasMultiCourse) {
gdi.addButton("CourseReport", "Bantilldelning", ListsCB);
gdi.addButton("CourseReport", "Bantilldelning", ListsCB).setExtra(IgnoreLimitPer);
checkWidth(gdi);
if (cnf.hasTeamClass()) {
gdi.addButton("GenLst:courseteamtable", "Gafflingar i tabellformat", ListsCB,
"Från den här listan kan man skapa etiketter att klistra på kartor");
"Från den här listan kan man skapa etiketter att klistra på kartor");
checkWidth(gdi);
}
}
if (cnf.hasTeamClass()) {
gdi.addButton("GenLst:teamchanges", "Lagändringblankett", ListsCB).setExtra(AddTeamClasses | ForcePageBreak);
checkWidth(gdi);
}
bool hasVac = false;
bool hasAPIEntry = false;
{
@ -2579,7 +2623,7 @@ bool TabList::loadPage(gdioutput &gdi)
}
if (cnf.hasRentedCard)
gdi.addButton("HiredCards", "Hyrbricksrapport", ListsCB);
gdi.addButton("HiredCards", "Hyrbricksrapport", ListsCB).setExtra(IgnoreLimitPer);
gdi.popX();
@ -3055,9 +3099,6 @@ void TabList::getPublicLists(oEvent &oe, vector<oListParam> &lists) {
getResultPatrol(oe, lists.back(), cnf);
}
if (cnf.hasRogaining()) {
//gdi.addButton("Result:rogainingind", "Rogaining", ListsCB).setExtra(2);
}
}
MetaListContainer &lc = oe.getListContainer();

View File

@ -1089,6 +1089,10 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data)
pRunner r = oe->getRunner(runnerId, 0);
if (r) {
warnDuplicateCard(gdi, cardNo, r);
if (ii.changedInput() && oe->hasHiredCardData()) {
gdi.check("RentCard", oe->isHiredCard(cardNo));
}
}
}
}
@ -1369,7 +1373,6 @@ int TabRunner::vacancyCB(gdioutput &gdi, int type, void *data)
ButtonInfo bi=*(ButtonInfo *)data;
TabSI &tsi = dynamic_cast<TabSI &>(*gdi.getTabs().get(TSITab));
if (bi.id == "VacancyAdd") {
showVacancyList(gdi, "add");
}
@ -1509,9 +1512,12 @@ int TabRunner::vacancyCB(gdioutput &gdi, int type, void *data)
return 0;
}
void TabRunner::setCardNo(gdioutput &gdi, int cardNo)
{
pRunner db_r=oe->dbLookUpByCard(cardNo);
void TabRunner::setCardNo(gdioutput &gdi, int cardNo) {
pRunner db_r=oe->dbLookUpByCard(cardNo);
if (cardNo > 0 && oe->hasHiredCardData()) {
gdi.check("RentCard", oe->isHiredCard(cardNo));
}
if (db_r) {
gdi.setText("Name", db_r->getName());
@ -1810,7 +1816,7 @@ void TabRunner::runnerReport(oEvent &oe, gdioutput &gdi, int id, bool compact) {
}
else {
vector<pFreePunch> punches;
oe.getPunchesForRunner(r->getId(), punches);
oe.getPunchesForRunner(r->getId(), true, punches);
int lastT = r->getStartTime();
for (size_t k = 0; k < punches.size(); k++) {
@ -1953,7 +1959,7 @@ void TabRunner::showVacancyList(gdioutput &gdi, const string &method, int classI
lastFee = tsi.storedInfo.storedFee;
gdi.addCombo("Fee", 60, 150, 0, L"Avgift:");
oe->fillFees(gdi, "Fee", true);
oe->fillFees(gdi, "Fee", false, true);
gdi.autoGrow("Fee");
if (!lastFee.empty() && lastFee != L"@") {
@ -2097,11 +2103,12 @@ void TabRunner::listRunners(gdioutput &gdi, const vector<pRunner> &r, bool filte
for (size_t k=0; k<r.size(); k++) {
if (filterVacant && r[k]->isVacant())
continue;
out.clear();
sprintf_s(bf, "%d.", k+1);
gdi.addStringUT(yp, xp, 0, bf);
gdi.addStringUT(yp, xp+40, 0, r[k]->getNameAndRace(true), 190);
gdi.addStringUT(yp, xp+200, 0, r[k]->getClass(true), 140);
gdi.addStringUT(yp, xp+350, 0, r[k]->getClub(), 190);
gdi.addStringUT(yp, xp+gdi.scaleLength(40), 0, r[k]->getNameAndRace(true), gdi.scaleLength(190));
gdi.addStringUT(yp, xp+gdi.scaleLength(200), 0, r[k]->getClass(true), gdi.scaleLength(140));
gdi.addStringUT(yp, xp + gdi.scaleLength(350), 0, r[k]->getClub(), +gdi.scaleLength(190));
int c = r[k]->getCardNo();
if (c>0) {
{
@ -2113,10 +2120,10 @@ void TabRunner::listRunners(gdioutput &gdi, const vector<pRunner> &r, bool filte
}
}
if (out.size() <= 1) {
gdi.addStringUT(yp, xp+550, 0, "(" + itos(c) + ")", 190);
gdi.addStringUT(yp, xp+gdi.scaleLength(550), 0, "(" + itos(c) + ")", 190);
}
else {
TextInfo &ti = gdi.addStringUT(yp, xp+550, 0, L"(" + itow(c) + lang.tl(", reused card") + L")", 100);
TextInfo &ti = gdi.addStringUT(yp, xp+gdi.scaleLength(550), 0, L"(" + itow(c) + lang.tl(", reused card") + L")", gdi.scaleLength(100));
wstring tt;
for (size_t j = 0; j < out.size(); j++) {
if (out[j] == r[k]->getMultiRunner(0))
@ -2793,6 +2800,8 @@ void TabRunner::fillRunnerList(gdioutput &gdi) {
bool TabRunner::canSetStart(pRunner r) const {
pClass pc = r->getTeam() ? r->getTeam()->getClassRef(false) : r->getClassRef(true);
if (r->getTeam() && pc->isQualificationFinalBaseClass())
pc = r->getClassRef(true);
if (pc && pc->getNumStages() > 0) {
StartTypes st = pc->getStartType(r->getLegNumber());

View File

@ -933,12 +933,14 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data)
gdi.restore("EntryLine");
wchar_t bf[256];
wstring cno = r->getCardNo() > 0 ? L"(" + itow(r->getCardNo()) + L"), " : L"";
if (r->getClubId() != 0) {
swprintf_s(bf, L"(%d), %s, %s", r->getCardNo(), r->getClub().c_str(),
swprintf_s(bf, L"%s%s, %s", cno.c_str(), r->getClub().c_str(),
r->getClass(true).c_str());
}
else {
swprintf_s(bf, L"(%d), %s", r->getCardNo(), r->getClass(true).c_str());
swprintf_s(bf, L"%s%s", cno.c_str(), r->getClass(true).c_str());
}
wstring info(bf);
@ -1035,6 +1037,84 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data)
gdi.setInputStatus("StatusOK", !dnf);
gdi.check("StatusOK", !dnf);
}
else if (bi.id == "RHCClear") {
if (gdi.ask(L"Vill du tömma listan med hyrbrickor?")) {
oe->clearHiredCards();
loadPage(gdi);
}
}
else if (bi.id == "RHCImport") {
wstring fn = gdi.browseForOpen({ make_pair(L"Semikolonseparerad (csv)", L"*.csv") }, L"csv");
if (!fn.empty()) {
csvparser csv;
list<vector<wstring>> data;
csv.parse(fn, data);
set<int> rentCards;
for (auto &c : data) {
for (wstring wc : c) {
int cn = _wtoi(wc.c_str());
if (cn > 0) {
oe->setHiredCard(cn, true);
gdi.addStringUT(0, itos(cn)).setHandler(getResetHiredCardHandler());
}
}
}
gdi.scrollToBottom();
gdi.refresh();
vector<pRunner> runners;
oe->getRunners(0, 0, runners);
if (!runners.empty() && gdi.ask(L"Vill du sätta hyrbricka på befintliga löpare med dessa brickor?")) {
int bcf = oe->getBaseCardFee();
for (pRunner r : runners) {
if (rentCards.count(r->getCardNo()) && r->getDCI().getInt("CardFee") == 0) {
gdi.addStringUT(0, r->getCompleteIdentification());
r->getDI().setInt("CardFee", bcf);
}
}
}
loadPage(gdi);
}
}
else if (bi.id == "RHCExport") {
int ix = 0;
wstring fn = gdi.browseForSave({ make_pair(L"Semikolonseparerad (csv)", L"*.csv") }, L"csv", ix);
if (!fn.empty()) {
oe->synchronizeList(oListId::oLPunchId);
auto hc = oe->getHiredCards();
csvparser csv;
csv.openOutput(fn);
for (int c : hc)
csv.outputRow(itos(c));
csv.closeOutput();
}
}
else if (bi.id == "RHCPrint") {
gdioutput gdiPrint("print", gdi.getScale());
gdiPrint.clearPage(false);
gdiPrint.addString("", boldLarge, "Hyrbricksrapport");
oe->synchronizeList(oListId::oLPunchId);
auto hc = oe->getHiredCards();
int dc = gdiPrint.scaleLength(70);
int col = 0;
gdiPrint.dropLine(2);
int cx = gdiPrint.getCX();
int cy = gdiPrint.getCY();
for (int h : hc) {
if (col >= 8) {
col = 0;
cy += gdiPrint.getLineHeight() * 2;
}
gdiPrint.addStringUT(cy, cx + col * dc, 0, itow(h));
col++;
}
gdiPrint.refresh();
gdiPrint.print(oe);
}
else if (bi.id == "CCSClear") {
if (gdi.ask(L"Vill du göra om avbockningen från början igen?")) {
checkedCardFlags.clear();
@ -1138,6 +1218,9 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data)
else if (mode == ModeCheckCards) {
showCheckCardStatus(gdi, "init");
}
else if (mode == ModeRegisterCards) {
showRegisterHiredCards(gdi);
}
gdi.refresh();
}
else if (bi.id=="Fee") {
@ -1385,12 +1468,17 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data)
if (runnerMatchedId != -1 && gdi.isChecked("AutoTie") && cardNo>0)
gdi.addTimeoutMilli(50, "TieCard", SportIdentCB).setExtra(runnerMatchedId);
}
else if (cardNo>0 && gdi.getText("Name").empty()) {
SICard sic(ConvertedTimeStatus::Hour24);
sic.clear(0);
sic.CardNumber = cardNo;
else if (cardNo>0) {
if (ii.changedInput() && oe->hasHiredCardData())
gdi.check("RentCard", oe->isHiredCard(cardNo));
entryCard(gdi, sic);
if (gdi.getText("Name").empty()) {
SICard sic(ConvertedTimeStatus::Hour24);
sic.clear(0);
sic.CardNumber = cardNo;
entryCard(gdi, sic);
}
}
}
else if (ii.id[0]=='*') {
@ -1644,6 +1732,7 @@ bool TabSI::loadPage(gdioutput &gdi) {
gdi.addItem("ReadType", lang.tl("Avläsning/radiotider"), ModeReadOut);
gdi.addItem("ReadType", lang.tl("Tilldela hyrbrickor"), ModeAssignCards);
gdi.addItem("ReadType", lang.tl("Avstämning hyrbrickor"), ModeCheckCards);
gdi.addItem("ReadType", lang.tl("Registrera hyrbrickor"), ModeRegisterCards);
gdi.addItem("ReadType", lang.tl("Anmälningsläge"), ModeEntry);
gdi.addItem("ReadType", lang.tl("Print card data"), ModeCardData);
@ -1709,7 +1798,9 @@ bool TabSI::loadPage(gdioutput &gdi) {
else if (mode == ModeCheckCards) {
showCheckCardStatus(gdi, "init");
}
else if (mode == ModeRegisterCards) {
showRegisterHiredCards(gdi);
}
// Unconditional clear
activeSIC.clear(0);
@ -1783,7 +1874,7 @@ void TabSI::insertSICardAux(gdioutput &gdi, SICard &sic)
DWORD loaded;
bool pageLoaded=gdi.getData("SIPageLoaded", loaded);
if (pageLoaded && manualInput)
if (pageLoaded && manualInput && mode == ModeReadOut)
gdi.restore("ManualInput");
if (!pageLoaded && !insertCardNumberField.empty()) {
@ -1816,6 +1907,16 @@ void TabSI::insertSICardAux(gdioutput &gdi, SICard &sic)
checkCard(gdi, sic, true);
return;
}
else if (mode == ModeRegisterCards) {
if (!pageLoaded) {
CardQueue.push_back(sic);
gdi.addInfoBox("SIREAD", L"Inläst bricka ställd i kö");
}
else {
registerHiredCard(gdi, sic);
}
return;
}
else if (mode == ModeCardData) {
if (sic.convertedTime == ConvertedTimeStatus::Hour12) {
int locTime = getLocalAbsTime();
@ -2549,6 +2650,9 @@ void TabSI::entryCard(gdioutput &gdi, const SICard &sic)
{
gdi.setText("CardNo", sic.CardNumber);
if (oe->hasHiredCardData())
gdi.check("RentCard", oe->isHiredCard(sic.CardNumber));
wstring name;
wstring club;
int age = 0;
@ -2582,6 +2686,9 @@ void TabSI::entryCard(gdioutput &gdi, const SICard &sic)
if (cls && age > 0) {
directEntryGUI.updateFees(gdi, cls, age);
}
else {
updateEntryInfo(gdi);
}
}
void TabSI::assignCard(gdioutput &gdi, const SICard &sic)
@ -2690,7 +2797,7 @@ void TabSI::generateEntryLine(gdioutput &gdi, pRunner r) {
if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Economy)) {
gdi.addCombo("Fee", 60, 150, SportIdentCB, L"Anm. avgift:");
oe->fillFees(gdi, "Fee", false);
oe->fillFees(gdi, "Fee", true, false);
if (!storedInfo.storedFee.empty() && storedInfo.storedFee != L"@")
gdi.setText("Fee", storedInfo.storedFee);
@ -3503,6 +3610,35 @@ wstring TabSI::getCardInfo(bool param, vector<int> &count) const {
return msg;
}
void TabSI::showRegisterHiredCards(gdioutput &gdi) {
gdi.disableInput("Interactive");
gdi.disableInput("Database");
gdi.disableInput("PrintSplits");
gdi.disableInput("UseManualInput");
gdi.fillDown();
gdi.addString("", 10, "help:registerhiredcards");
gdi.dropLine();
gdi.fillRight();
gdi.pushX();
gdi.addButton("RHCClear", "Nollställ", SportIdentCB);
gdi.addButton("RHCImport", "Importera...", SportIdentCB);
gdi.addButton("RHCExport", "Exportera...", SportIdentCB);
gdi.addButton("RHCPrint", "Skriv ut...", SportIdentCB);
gdi.popX();
gdi.dropLine(3);
gdi.fillDown();
oe->synchronizeList(oListId::oLPunchId);
auto &hiredCards = oe->getHiredCards();
for (int i : hiredCards) {
gdi.addStringUT(0, itos(i)).setExtra(i).setHandler(getResetHiredCardHandler());
}
gdi.refresh();
}
void TabSI::showCheckCardStatus(gdioutput &gdi, const string &cmd) {
vector<pRunner> r;
const int cx = gdi.getCX();
@ -3514,6 +3650,7 @@ void TabSI::showCheckCardStatus(gdioutput &gdi, const string &cmd) {
gdi.disableInput("Database");
gdi.disableInput("PrintSplits");
gdi.disableInput("UseManualInput");
gdi.fillDown();
gdi.addString("", 10, "help:checkcards");
@ -3640,6 +3777,42 @@ void TabSI::showCheckCardStatus(gdioutput &gdi, const string &cmd) {
gdi.dropLine();
}
class ResetHiredCard : public GuiHandler {
oEvent *oe;
public:
void handle(gdioutput &gdi, BaseInfo &info, GuiEventType type) {
if (type == GuiEventType::GUI_LINK) {
TextInfo &ti = dynamic_cast<TextInfo &>(info);
int c = _wtoi(ti.text.c_str());
if (gdi.ask(L"Vill du ta bort brickan från hyrbrickslistan?")) {
oe->setHiredCard(c, false);
ti.text = L"-";
ti.setHandler(nullptr);
gdi.refreshFast();
}
}
}
ResetHiredCard(oEvent *oe) : oe(oe) {}
};
GuiHandler *TabSI::getResetHiredCardHandler() {
if (!resetHiredCardHandler)
resetHiredCardHandler = make_shared<ResetHiredCard>(oe);
return resetHiredCardHandler.get();
}
void TabSI::registerHiredCard(gdioutput &gdi, const SICard &sic) {
if (!oe->isHiredCard(sic.CardNumber))
oe->setHiredCard(sic.CardNumber, true);
gdi.addStringUT(0, itos(sic.CardNumber)).setHandler(getResetHiredCardHandler());
gdi.scrollToBottom();
gdi.refresh();
}
void TabSI::checkCard(gdioutput &gdi, const SICard &card, bool updateAll) {
bool wasChecked = (checkedCardFlags[card.CardNumber] & CNFChecked) != 0 && updateAll;

View File

@ -37,7 +37,8 @@ public:
ModeAssignCards,
ModeCheckCards,
ModeEntry,
ModeCardData
ModeCardData,
ModeRegisterCards,
};
void setMode(SIMode m) { mode = m; }
@ -73,6 +74,9 @@ private:
set<int> warnedClassOutOfMaps;
shared_ptr<GuiHandler> resetHiredCardHandler;
GuiHandler *getResetHiredCardHandler();
int runnerMatchedId;
bool printErrorShown;
void printProtected(gdioutput &gdi, gdioutput &gdiprint);
@ -94,6 +98,7 @@ private:
int inputId;
void showCheckCardStatus(gdioutput &gdi, const string &cmd);
void showRegisterHiredCards(gdioutput &gdi);
wstring getCardInfo(bool param, vector<int> &count) const;
// Formatting for card tick off
@ -119,6 +124,7 @@ private:
map<int, CardNumberFlags> checkedCardFlags;
void checkCard(gdioutput &gdi, const SICard &sic, bool updateAll);
void registerHiredCard(gdioutput &gdi, const SICard &sic);
void showReadPunches(gdioutput &gdi, vector<PunchInfo> &punches, set<string> &dates);
void showReadCards(gdioutput &gdi, vector<SICard> &cards);

View File

@ -979,15 +979,15 @@ bool TabSpeaker::loadPage(gdioutput &gdi) {
int h,w;
gdi.getTargetDimension(w, h);
int bw=gdi.scaleLength(100);
int nbtn=max((w-80)/bw, 1);
bw=(w-80)/nbtn;
int bw = gdi.scaleLength(100);
int numBtn = max((w - gdi.scaleLength(80)) / bw, 1);
bw = (w - 80) / numBtn;
int basex = SPEAKER_BASE_X;
int basey=gdi.getCY();
int cx=basex;
int cy=basey;
int cb=1;
vector<pClass> clsToWatch;
for (int cid : classesToWatch) {
pClass pc = oe->getClass(cid);
@ -998,18 +998,29 @@ bool TabSpeaker::loadPage(gdioutput &gdi) {
sort(clsToWatch.begin(), clsToWatch.end(), [](const pClass &a, const pClass &b) {return *a < *b; });
int bwCls = bw;
TextInfo ti;
for (auto pc : clsToWatch) {
ti.xp = 0;
ti.yp = 0;
ti.format = 0;
ti.text = pc->getName();
gdi.calcStringSize(ti);
bwCls = max(bwCls, ti.realWidth+ gdi.scaleLength(10));
}
int limitX = w - bw / 3;
for (auto pc : clsToWatch) {
char classid[32];
sprintf_s(classid, "cid%d", pc->getId());
gdi.addButton(cx, cy, bw, classid, L"#" + pc->getName(), tabSpeakerCB, L"", false, false);
cx+=bw;
cb++;
if (cb>nbtn) {
cb=1;
cx=basex;
cy+=gdi.getButtonHeight()+4;
if (cx > basex && (cx + bwCls) >= limitX) {
cx = basex;
cy += gdi.getButtonHeight() + 4;
}
gdi.addButton(cx, cy, bwCls-2, classid, L"#" + pc->getName(), tabSpeakerCB, L"", false, false);
cx += bwCls;
}
bool pm = false;
@ -1021,20 +1032,21 @@ bool TabSpeaker::loadPage(gdioutput &gdi) {
cx=gdi.getCX();
}
else {
if ((cx + db) > basex && (cx + db + bw) >= limitX) {
cx = basex; db = 0;
cy += gdi.getButtonHeight() + 4;
}
gdi.addButton(cx+db, cy, bw-2, "Events", "Händelser", tabSpeakerCB, "Löpande information om viktiga händelser i tävlingen", false, false);
if (++cb>nbtn) {
cb = 1, cx = basex, db = 0;
cy += gdi.getButtonHeight()+4;
} else db += bw;
db += bw;
pm = true;
}
gdi.addButton(cx + db, cy, bw - 2, "Report", "Rapportläge", tabSpeakerCB, "Visa detaljerad rapport för viss deltagare", false, false);
if (++cb>nbtn) {
cb = 1, cx = basex, db = 0;
if ((cx + db) > basex && (cx + db + bw) >= limitX) {
cx = basex; db = 0;
cy += gdi.getButtonHeight() + 4;
}
else db += bw;
gdi.addButton(cx + db, cy, bw - 2, "Report", "Rapportläge", tabSpeakerCB, "Visa detaljerad rapport för viss deltagare", false, false);
db += bw;
if (pm) {
gdi.addButton(cx + db, cy, bw / 5, "ZoomIn", "+", tabSpeakerCB, "Zooma in (Ctrl + '+')", false, false);
@ -1043,62 +1055,69 @@ bool TabSpeaker::loadPage(gdioutput &gdi) {
db += bw / 5 + 2;
}
if ((cx + db) > basex && (cx + db + bw) >= limitX) {
cx = basex; db = 0;
cy += gdi.getButtonHeight() + 4;
}
gdi.addButton(cx+db, cy, bw-2, "Settings", "Inställningar...", tabSpeakerCB, "Välj vilka klasser och kontroller som bevakas", false, false);
if (++cb>nbtn) {
cb = 1, cx = basex, db = 0;
cy += gdi.getButtonHeight()+4;
} else db += bw;
db += bw;
if ((cx + db) > basex && (cx + db + bw) >= limitX) {
cx = basex; db = 0;
cy += gdi.getButtonHeight() + 4;
}
gdi.addButton(cx+db, cy, bw-2, "Manual", "Tidsinmatning", tabSpeakerCB, "Mata in radiotider manuellt", false, false);
if (++cb>nbtn) {
cb = 1, cx = basex, db = 0;
cy += gdi.getButtonHeight()+4;
} else db += bw;
db += bw;
if ((cx + db) > basex && (cx + db + bw) >= limitX) {
cx = basex; db = 0;
cy += gdi.getButtonHeight() + 4;
}
gdi.addButton(cx+db, cy, bw-2, "PunchTable", "Stämplingar", tabSpeakerCB, "Visa en tabell över alla stämplingar", false, false);
if (++cb>nbtn) {
cb = 1, cx = basex, db = 0;
cy += gdi.getButtonHeight()+4;
} else db += bw;
db += bw;
if ((cx + db) > basex && (cx + db + bw) >= limitX) {
cx = basex; db = 0;
cy += gdi.getButtonHeight() + 4;
}
gdi.addButton(cx+db, cy, bw-2, "LiveResult", "Direkt tidtagning", tabSpeakerCB, "Visa rullande tider mellan kontroller i helskärmsläge", false, false);
if (++cb>nbtn) {
cb = 1, cx = basex, db = 0;
cy += gdi.getButtonHeight()+4;
} else db += bw;
db += bw;
if (!ownWindow) {
if ((cx + db) > basex && (cx + db + bw) >= limitX) {
cx = basex; db = 0;
cy += gdi.getButtonHeight() + 4;
}
gdi.addButton(cx+db, cy, bw-2, "Priority", "Prioritering", tabSpeakerCB, "Välj löpare att prioritera bevakning för", false, false);
if (++cb>nbtn) {
cb = 1, cx = basex, db = 0;
cy += gdi.getButtonHeight()+4;
} else db += bw;
db += bw;
if ((cx + db) > basex && (cx + db + bw) >= limitX) {
cx = basex; db = 0;
cy += gdi.getButtonHeight() + 4;
}
gdi.addButton(cx+db, cy, bw-2, "Window", "Nytt fönster", tabSpeakerCB, "", false, false);
if (++cb>nbtn) {
cb = 1, cx = basex, db = 0;
cy += gdi.getButtonHeight()+4;
} else db += bw;
db += bw;
if (getExtraWindows().size() == 1) {
wstring sf = getSpeakerSettingsFile();
if (fileExist(sf.c_str())) {
gdi.addButton(cx + db, cy, bw - 2, "LoadWindows", "Återskapa", tabSpeakerCB, "Återskapa tidigare sparade fönster- och speakerinställningar", false, false);
if (++cb > nbtn) {
cb = 1, cx = basex, db = 0;
if ((cx + db) > basex && (cx + db + bw) >= limitX) {
cx = basex; db = 0;
cy += gdi.getButtonHeight() + 4;
}
else db += bw;
gdi.addButton(cx + db, cy, bw - 2, "LoadWindows", "Återskapa", tabSpeakerCB, "Återskapa tidigare sparade fönster- och speakerinställningar", false, false);
db += bw;
}
}
else {
gdi.addButton(cx + db, cy, bw - 2, "SaveWindows", "Spara", tabSpeakerCB, "Spara fönster- och speakerinställningar på datorn", false, false);
if (++cb > nbtn) {
cb = 1, cx = basex, db = 0;
if ((cx + db) > basex && (cx + db + bw) >= limitX) {
cx = basex; db = 0;
cy += gdi.getButtonHeight() + 4;
}
else db += bw;
gdi.addButton(cx + db, cy, bw - 2, "SaveWindows", "Spara", tabSpeakerCB, "Spara fönster- och speakerinställningar på datorn", false, false);
db += bw;
}
}

View File

@ -442,7 +442,6 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) {
r->setName(name, true);
}
r->setCardNo(cardNo, true);
if (gdi.isChecked("RENT" + itos(i)))
r->getDI().setInt("CardFee", oe->getBaseCardFee());
else
@ -461,10 +460,15 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) {
}
}
else
r=oe->addRunner(name, t->getClubId(), t->getClassId(false), cardNo, 0, false);
r = oe->addRunner(name, t->getClubId(), t->getClassId(false), cardNo, 0, false);
r->setName(name, true);
r->setCardNo(cardNo, true);
if (gdi.isChecked("RENT" + itos(i)))
r->getDI().setInt("CardFee", oe->getBaseCardFee());
else
r->getDI().setInt("CardFee", 0);
r->synchronize();
t->setRunner(i, r, true);
}
@ -1065,8 +1069,11 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data)
}
if (ii.id == "DirName" || ii.id == "DirCard") {
gdi.setInputStatus("DirOK", !gdi.getText("DirName").empty() &&
gdi.getTextNo("DirCard") > 0);
int cno = gdi.getTextNo("DirCard");
if (cno > 0 && oe->hasHiredCardData()) {
gdi.check("DirRent", oe->isHiredCard(cno));
}
gdi.setInputStatus("DirOK", !gdi.getText("DirName").empty() && cno > 0);
}
@ -1135,12 +1142,16 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data)
}
pClass pc=oe->getClass(classId);
if (pc) {
for(unsigned i=0;i<pc->getNumStages();i++){
for (unsigned i = 0; i < pc->getNumStages(); i++) {
if (ii.id == "SI" + itos(i)) {
int cardNo = _wtoi(ii.text.c_str());
pTeam t = oe->getTeam(teamId);
if (t) {
warnDuplicateCard(gdi, ii.id, cardNo, t->getRunner(i));
pRunner r = t->getRunner(i);
if (ii.changedInput() && oe->hasHiredCardData()) {
gdi.check("RENT" + itos(i), oe->isHiredCard(cardNo));
}
warnDuplicateCard(gdi, ii.id, cardNo, r);
}
break;
}

View File

@ -209,11 +209,11 @@ void Table::addRow(int rowId, oBase *object)
Data[0].cells[i].canEdit=false;
Data[0].cells[i].type=cellEdit;
Data[0].cells[i].owner=0;
Data[0].cells[i].ownerRef.reset();
Data[1].cells[i].canEdit=false;
Data[1].cells[i].type=cellEdit;
Data[1].cells[i].owner=0;
Data[1].cells[i].ownerRef.reset();
}
}
else {
@ -236,7 +236,7 @@ void Table::set(int column, oBase &owner, int id, const wstring &data, bool canE
TableRow &row=Data[dataPointer];
TableCell &cell=row.cells[column];
cell.contents=data;
cell.owner=&owner;
cell.ownerRef = owner.getReference();
cell.id=id;
cell.canEdit=canEdit;
cell.type=type;
@ -725,7 +725,8 @@ void Table::selection(gdioutput &gdi, const wstring &text, int data) {
TableCell &cell = Data[selectionRow].cells[selectionCol];
int id = Data[selectionRow].id;
cell.owner->inputData(cell.id, text, data, cell.contents, false);
if (cell.hasOwner())
cell.getOwner()->inputData(cell.id, text, data, cell.contents, false);
reloadRow(id);
RECT rc;
getRowRect(selectionRow, rc);
@ -913,7 +914,7 @@ bool Table::editCell(gdioutput &gdi, int row, int col) {
if (cell.type == cellAction) {
ReleaseCapture();
gdi.makeEvent("CellAction", internalName, cell.id, cell.owner ? cell.owner->getId() : 0, false);
gdi.makeEvent("CellAction", internalName, cell.id, cell.hasOwner() ? cell.getOwner()->getId() : 0, false);
return true;
}
@ -934,7 +935,8 @@ bool Table::editCell(gdioutput &gdi, int row, int col) {
vector< pair<wstring, size_t> > out;
size_t selected = 0;
cell.owner->fillInput(cell.id, out, selected);
if (cell.hasOwner())
cell.getOwner()->fillInput(cell.id, out, selected);
int width = 40;
for (size_t k = 0; k<out.size(); k++)
@ -1593,7 +1595,8 @@ void Table::setTableText(gdioutput &gdi, int editRow, int editCol, const wstring
wstring output;
TableCell &cell=Data[editRow].cells[editCol];
cell.owner->inputData(cell.id, bf, 0, output, false);
if (cell.hasOwner())
cell.getOwner()->inputData(cell.id, bf, 0, output, false);
cell.contents = output;
if (hEdit != 0)
DestroyWindow(hEdit);
@ -2108,7 +2111,8 @@ void Table::importClipboard(gdioutput &gdi)
if (cell.type==cellSelection || cell.type==cellCombo) {
vector< pair<wstring, size_t> > out;
size_t selected = 0;
cell.owner->fillInput(cell.id, out, selected);
if (cell.hasOwner())
cell.getOwner()->fillInput(cell.id, out, selected);
index = -1;
for (size_t i = 0; i<out.size() && index == -1; i++) {
if (_wcsicmp(out[i].first.c_str(), table[k][j].c_str()) == 0)
@ -2117,11 +2121,13 @@ void Table::importClipboard(gdioutput &gdi)
}
try {
if (index != -1) {
cell.owner->inputData(cell.id, table[k][j], index, output, false);
if (cell.hasOwner())
cell.getOwner()->inputData(cell.id, table[k][j], index, output, false);
cell.contents = output;
}
else if (cell.type == cellCombo) {
cell.owner->inputData(cell.id, table[k][j], index, output, false);
if (cell.hasOwner())
cell.getOwner()->inputData(cell.id, table[k][j], index, output, false);
cell.contents = output;
}
}
@ -2158,13 +2164,13 @@ int Table::deleteRows(int row1, int row2)
if ( k >= sortIndex.size())
throw std::exception("Index out of range");
const TableRow &tr = Data[sortIndex[k].index];
oBase *ob = tr.cells[0].owner;
if (!ob)
throw std::exception("Null pointer exception");
if (ob->canRemove())
ob->remove();
else
failed++;
oBase *ob = tr.cells[0].getOwner();
if (ob) {
if (ob->canRemove())
ob->remove();
else
failed++;
}
}
clearCellSelection(0);
@ -2323,8 +2329,8 @@ int Table::getNumDataRows() const {
void TableRow::setObject(oBase &obj) {
ob = &obj;
for (size_t k = 0; k < cells.size(); k++) {
if (cells[k].owner != 0)
cells[k].owner = &obj;
if (cells[k].hasOwner())
cells[k].ownerRef = obj.getReference();
}
}

View File

@ -52,7 +52,10 @@ class TableCell
RECT absPos;
DWORD id;
oBase *owner;
bool hasOwner() const { return ownerRef && ownerRef->get() != nullptr; };
oBase *getOwner() const { return ownerRef ? ownerRef->get() : nullptr; }
shared_ptr<oBase::oBaseReference> ownerRef;
bool canEdit;
CellType type;

View File

@ -642,7 +642,7 @@ bool csvparser::importOCAD_CSV(oEvent &event, const wstring &file, bool addClass
}
else {
for (size_t i = 0; i<cls->getNumStages(); i++)
cls->addStageCourse(i, pc->getId());
cls->addStageCourse(i, pc->getId(), -1);
}
cls->synchronize();

View File

@ -2315,3 +2315,97 @@ xml-data = XML data
Year of birth = Førdselsår
Zooma in (Ctrl + '+') = Zoom ind (Ctrl + '+')
Zooma ut (Ctrl + '-') = Zoom ud (Ctrl + '-')
AllPunches = Alle stemplinger
Anmäl andra = Tilmeld andre
Anmälan mottagen = Tilmelding modtaget
Anmälan måste hanteras manuellt = Tilmelding skal håndteres manuelt.
Anyone = Enhver
Automatisk omladdning = Automatisk opdatering
Begränsa antal rader per sida = Begræns antal rækker per side
Bricknummer = Si brik nummer
ClassKnockoutTotalResult = Klasse, knock-out, samlet resultat
ClassLiveResult = Live resultater (radio tider), klassevis
Classes together = Klasser samlet
CoursePunches = Stemplinger (for bane)
Database is used and cannot be deleted = Databasen er i brug og kan ikke slettes
Direkt tidtagning = Direkte tidtagning
EFilterAPIEntry = Indtastning via API
Ej omstart = Ej omstart
Endast grundläggande (enklast möjligt) = Kund grundlæggende(så simpelt som muligt)
Endast tidtagning = Kun tidtagning
Endast tidtagning (utan banor) = Kun tidtagning (uden baner)
Endast tidtagning (utan banor), stafett = Kun tidtagning (uden baner), stafet
Externa adresser = Eksterne adresser
Felaktigt datum 'X' (Använd YYYY-MM-DD) = Forkert dato format 'X' (Brug YYYY-MM-DD)
Felaktigt rankingformat i X. Förväntat: Y = Forkert format på ranking i X \nForventet format: Y
FilterAnyResult = Med radiotider/resultat
FilterNamedControl = Med angivne poster
FilterNotFinish = Udeluk ikke gennemført
Finish order = Målrækkefølge
First to finish = Først gennemført
Från löpardatabasen = Fra løberdatabase
Från löpardatabasen i befintliga klubbar = Fra løber database fra klubber der er kendt
Färre slingor = Færre sløjfer
Förhindra att laget deltar i någon omstart = Udeluk at holdet deltager i omstart
Förhindra omstart = Udeluk omstart
HTML Export = HTML Export
HTML Export för 'X' = HTML export af 'X'
HTML formaterad genom listinställningar = HTML formateret ud fra liste indstillinger
Hittar inte klass X = Kan ikke finde klasse X
Importerar RAID patrull csv-fil = Importerer RAID patrulje .csv data
Importerar ranking = Importer ranking
Individual result by finish time = Individuelle resultater ud fra måltid
Individuellt = Individuelt
Klart. X värden tilldelade = Klar, X værdier er tildelt.
Klassen X är listad flera gånger = Klassen X er angivet flere gange
Klassval för 'X' = Valg af klasse for 'X'
Knockout total = Knock-out samlet
Kolumner = Kolonner
Lag och stafett = Hold og stafet
Lagra inställningar = Gem indstillinger
LineBreak = Linieskift
Liveresultat, radiotider = Live resultater med radiotider
MeOS utvecklinsstöd = Støtte til MeOS udvikling
Med direktanmälan = Med tilmelding på stævneplads
Ogiltig starttid X = Ugyldig starttid X
Ogiltigt startintervall X = Ugyldigt start interval X
Pages with columns = Sider med kolonner
Pages with columns, no header = Sider med kolonner, uden overskrift
PunchAbsTime = Stempling rigtig tid
PunchName = Stempling, post benævnelse
PunchNamedSplit = Tid siden seneste nævnte post
PunchSplitTime = Tid siden seneste post (mellemtid)
PunchTimeSinceLast = Tid mellem poster
PunchTotalTime = Samlet tid ved post
PunchTotalTimeAfter = Samlet efter ved post
Rad X är ogiltig = Række X er ugyldig
Rader = Rækker
RunnerCheck = Tidspunkt for check stempling
RunnerGrossTime = Løbers tid før korrektion
RunnerId = Løbers eksterne ID
Slå ihop med befintlig lista = Slå sammen med eksisterende liste
StartTimeClass = Start tid, klasse
Support intermediate legs = Understøt angivne stafet stræk
TeamGrossTime = Holdets tid før korrektion
Till vilka klasser = Til hvilke klasser
Tillåt anmälan = Tillad tilmelding
Timekeeping = Tidtagning
Varning: Följande deltagare har ett osäkert resultat = Advarsel: tildeling er uklar for følgende løbere.
Varning: Kartorna är slut = Advarsel: Der er ikke flere kort
Varning: deltagare med blankt namn påträffad. MeOS kräver att alla deltagare har ett namn, och tilldelar namnet 'N.N.' = Advarsel: Der er fundet en løber uden tildelt navn. MeOS kræver at alle løbere har et navn. MeOS har tildelt navnet 'N.N.'
Varning: lag utan namn påträffat. MeOS kräver att alla lag har ett namn, och tilldelar namnet 'N.N.' = Advarsel: Der er fundet et hold uden tildelt navn. MeOS kræver at hold har et navn og har tildelt navnet 'N.N.'
Varvräkning = Tæl omgange
Varvräkning med mellantid = Tæl omgange med mellemtider
Vem får anmäla sig = Hvem kan tilmelde sig
Vill du ta bort schemat? = Vil du fjerne skemaet?
Visa detaljerad rapport för viss deltagare = Vis detaljeret rapport for en specifik deltager
Visa rubrik = Vis overskrift
Visa rubrik mellan listorna = Vis overskrift imellem listerne
Without courses = Uden baner
X går vidare, klass enligt ranking = X går videre, klasse ifølge ranking
ask:outofmaps = Der er ikke flere kort. Vil du alligevel tilføje denne løber?
ask:removescheme = Resultater slettes hvis du sletter dette skema. Vil du fortsætte?
help:custom_text_lines = Du kan indsætte egne data ved at skrive [Symbol Name]. De mulige tegn kan ses i listen til højre.\n\nExample: Well done [Løbernavn]!
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 <Store Settings> 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

View File

@ -2392,7 +2392,7 @@ Bricknummer = Card number
Anmäl andra = New entry
Anmälan mottagen = Accepted entry
Automatisk omladdning = Automatic update
Till vilka klasser = To which classes
Till vilka klasser = To what classes
Vem får anmäla sig = Who may enter
Anmälan måste hanteras manuellt = Your entry requires manual processing.
EFilterAPIEntry = Entries via API
@ -2407,3 +2407,24 @@ info:pageswithcolumns = Show the list one page at the time, with the specified n
Pages with columns = Pages with columns
Pages with columns, no header = Pages with columns, no header
Externa adresser = External links
info:advanceinfo = Starting the service for instant result transfer failed. Results will be recieved with a few seconds delay. This is expected behaviour if more than one MeOS process is started on this computer.
Klassen är full = The class is full
Flytta upp = Move up
Flytta ner = Move down
EFilterWrongFee = Unexpected fee
RunnerExpectedFee = Competitors expected fee
Unexpected Fee = Unexpected entry fees
Anmälningsdatum = Entry date
Förväntad = Expected
Registrera hyrbrickor = Register rental cards
Vill du sätta hyrbricka på befintliga löpare med dessa brickor? = Do you want to apply rental card data on existing runners?
Vill du ta bort brickan från hyrbrickslistan? = Do you want to remove the card from the rental card list?
Vill du tömma listan med hyrbrickor? = Do you want to clear the rental card list?
prefsLastExportTarget = Last export target
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
ClassAvailableMaps = Available maps for class
ClassTotalMaps = Total number of maps for class

View File

@ -1016,14 +1016,11 @@ ButtonInfo &gdioutput::addButton(int x, int y, const string &id, const wstring &
HANDLE bm = 0;
int width = 0;
if (text[0] == '@') {
HINSTANCE hInst = GetModuleHandle(0);//(HINSTANCE)GetWindowLong(hWndTarget, GWL_HINSTANCE);
int ir = _wtoi(text.c_str() + 1);
// bm = LoadImage(hInst, MAKEINTRESOURCE(ir), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
bm = LoadBitmap(hInst, MAKEINTRESOURCE(ir));// , IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
HINSTANCE hInst = GetModuleHandle(0); int ir = _wtoi(text.c_str() + 1);
bm = LoadBitmap(hInst, MAKEINTRESOURCE(ir));
SIZE size;
size.cx = 24;
//GetBitmapDimensionEx(bm, &size);
width = size.cx+4;
}
else {
@ -1031,6 +1028,13 @@ ButtonInfo &gdioutput::addButton(int x, int y, const string &id, const wstring &
HDC hDC = GetDC(hWndTarget);
SelectObject(hDC, getGUIFont());
wstring ttext = lang.tl(text);
int tts = ttext.size();
if (tts > 2 && ttext[0] == '<' && ttext[1] == '<') {
ttext = L"" + ttext.substr(2);
}
else if (tts > 2 && ttext[tts-1] == '>' && ttext[tts-2] == '>') {
ttext = ttext.substr(0, tts-2) + L"";
}
if (lang.capitalizeWords())
capitalizeWords(ttext);
GetTextExtentPoint32(hDC, ttext.c_str(), ttext.length(), &size);
@ -1100,6 +1104,13 @@ ButtonInfo &gdioutput::addButton(int x, int y, int w, const string &id,
ButtonInfo bi;
wstring ttext = lang.tl(text);
int tts = ttext.size();
if (tts > 2 && ttext[0] == '<' && ttext[1] == '<') {
ttext = L"" + ttext.substr(2);
}
else if (tts > 2 && ttext[tts - 1] == '>' && ttext[tts - 2] == '>') {
ttext = ttext.substr(0, tts - 2) + L"";
}
if (lang.capitalizeWords())
capitalizeWords(ttext);
int height = getButtonHeight();
@ -1779,6 +1790,26 @@ bool gdioutput::getSelectedItem(ListBoxInfo &lbi) {
return true;
}
int gdioutput::getNumItems(const char *id) {
for (auto &lbi : LBI) {
if (lbi.id == id) {
if (lbi.IsCombo) {
return SendMessage(lbi.hWnd, CB_GETCOUNT, 0, 0);
}
else {
return SendMessage(lbi.hWnd, LB_GETCOUNT, 0, 0);
}
}
}
#ifdef _DEBUG
string err = string("Internal Error, identifier not found: X#") + id;
throw std::exception(err.c_str());
#endif
return 0;
}
int gdioutput::getItemDataByName(const char *id, const char *name) const{
wstring wname = recodeToWide(name);
list<ListBoxInfo>::const_iterator it;
@ -1872,6 +1903,61 @@ bool gdioutput::selectItemByData(const char *id, int data)
return false;
}
bool gdioutput::selectItemByIndex(const char *id, int index) {
for (auto it = LBI.begin(); it != LBI.end(); ++it) {
if (it->id == id) {
if (it->IsCombo) {
if (index == -1) {
SendMessage(it->hWnd, CB_SETCURSEL, -1, 0);
it->data = 0;
it->text = L"";
it->original = L"";
it->originalIdx = -1;
return true;
}
else {
SendMessage(it->hWnd, CB_SETCURSEL, index, 0);
int data = SendMessage(it->hWnd, CB_GETITEMDATA, index, 0);
it->data = data;
it->originalIdx = data;
TCHAR bf[1024];
if (SendMessage(it->hWnd, CB_GETLBTEXT, index, LPARAM(bf)) != CB_ERR) {
it->text = bf;
it->original = bf;
}
return true;
}
return false;
}
else {
if (index == -1) {
SendMessage(it->hWnd, LB_SETCURSEL, -1, 0);
it->data = 0;
it->text = L"";
it->original = L"";
it->originalIdx = -1;
return true;
}
else {
SendMessage(it->hWnd, LB_SETCURSEL, index, 0);
int data = SendMessage(it->hWnd, LB_GETITEMDATA, index, 0);
it->data = data;
it->originalIdx = data;
TCHAR bf[1024];
if (SendMessage(it->hWnd, LB_GETTEXT, index, LPARAM(bf)) != LB_ERR) {
it->text = bf;
it->original = bf;
}
return true;
}
return false;
}
}
}
return false;
}
bool gdioutput::autoGrow(const char *id) {
list<ListBoxInfo>::iterator it;
int size = 0;

View File

@ -549,6 +549,8 @@ public:
int getItemDataByName(const char *id, const char *name) const;
bool selectItemByData(const char *id, int data);
bool selectItemByIndex(const char *id, int index);
void removeSelected(const char *id);
bool selectItemByData(const string &id, int data) {
@ -579,6 +581,9 @@ public:
/** Return a selected item*/
bool getSelectedItem(const string &id, ListBoxInfo &lbi);
/** Get number of items in a list box.'*/
int getNumItems(const char *id);
/** Return the selected data in first, second indicates if data was available*/
pair<int, bool> getSelectedItem(const string &id);
pair<int, bool> getSelectedItem(const char *id);

View File

@ -251,7 +251,9 @@ public:
InputInfo &setFont(gdioutput &gdi, gdiFonts font);
GDICOLOR getBgColor() const {return bgColor;}
GDICOLOR getFgColor() const {return fgColor;}
/** Return the previously stored text */
const wstring &getPreviousText() const { return focusText; }
bool changedInput() const { return text != focusText; }
InputInfo &setPassword(bool pwd);
HWND getControlWindow() const {return hWnd;}
@ -276,7 +278,7 @@ private:
bool isEditControl;
bool writeLock;
wstring original;
wstring focusText; // Test when got focus
wstring focusText; // Text when got focus
bool ignoreCheck; // True if changed-state should be ignored
friend class gdioutput;
};

View File

@ -272,6 +272,196 @@ When and only when the type is <b>CourseIndividual</b>, the attribute <i>course<
*/person>
</pre>
<h2>Status</h2>
<b>Syntax:</b>
<pre><a href="/meos?get=status">/meos?get=status</a></pre>
<b>Returns:</b>
<p>MeOS Status Data. Includes MeOS version and competition name id, which also is the name of the database used, if any.</p>
<b>Example:</b>
<pre>
*MOPComplete>
*status version="3.6.1029" eventNameId="meos_20190223_212818_2FD" onDatabase="1"/>
*/MOPComplete>
</pre>
<h2>Entry Classes</h2>
<b>Syntax:</b>
<pre><a href="/meos?get=entryclass">/meos?get=entryclass</a></pre>
<b>Returns:</b>
<p>Classes where entry is allowed via the API.</p>
<pre>
*EntryClasses>
*Class id="5">
*Name>U1*Name>
*Fee>70 kr*/Fee>
*MaxAge>16*/MaxAge>
*Start>Start 1*/Start>
*AvailableStarts>30*/AvailableStarts>
*/Class>
*/EntryClasses>
</pre>
<h2>Lookup competitor</h2>
<b>Syntax:</b>
<pre><a href="/meos?lookup=competitor&id=%runnerid%">/meos?lookup=competitor&id=*id></a></pre>
<pre><a href="/meos?lookup=competitor&card=%card%">/meos?lookup=competitor&card=*card></a></pre>
<pre><a href="/meos?lookup=competitor&bib=%bib%">/meos?lookup=competitor&bib=*bib></a></pre>
<pre><a href="/meos?lookup=competitor&name=%name%&club=%club%">/meos?lookup=competitor&name=*name>&club=*club></a></pre>
<b>Returns:</b>
<p>Competitor including individual result.</p>
<b>Arguments:</b>
<ul>
<li><i>id</i> Competitor id. MeOS internal id.</li>
<li><i>card</i> Card number.</li>
<li><i>bib</i> Bib or start number.</li>
<li><i>name</i> Name of competitor.</li>
<li><i>club</i> Name of club.</li>
</ul>
<pre>
*Competitors>
*Competitor id="85">
*Name>Nils Bor*/Name>
*ExternalId>1234*/ExternalId>
*Club id="84">OK Linn&eacute;*/Club>
*Class id="204">Men*/Class>
*Card>16733*/Card>
*Status code="1">OK*/Status>
*Start>17:38:00*/Start>
*Finish>18:22:21*/Finish>
*RunningTime>44:21*/RunningTime>
*Place>7*/Place>
*TimeAfter>13:04*/TimeAfter>
*Team id="26">OK Linn&eacute; 2*/Team>
*Leg>1*/Leg>
*Splits>
*Control number="1">
*Name>[31]*/Name>
*Time>6:25*/Time>
*Analysis lost="1:11" behind="1:11" mistake="" leg="5" total="3"/>
*/Control>
*Control number="2">
*Name>Radio*/Name>
*Time>12:50*/Time>
*Analysis lost="1:10" behind="2:21" mistake="1:20" leg="3" total="3"/>
*/Control>
...
*/Splits>
*/Competitor>
*/Competitors>
</pre>
<h2>Lookup Database Competitor</h2>
<b>Syntax:</b>
<pre><a href="/meos?lookup=dbcompetitor&id=%runnerextid%">/meos?lookup=dbcompetitor&id=*id></a></pre>
<pre><a href="/meos?lookup=dbcompetitor&card=%card%">/meos?lookup=dbcompetitor&card=*card></a></pre>
<pre><a href="/meos?lookup=dbcompetitor&name=%dbname%&club=%dbclub%">/meos?lookup=dbcompetitor&name=*name>&club=*club></a></pre>
<b>Returns:</b>
<p>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.</p>
<b>Arguments:</b>
<ul>
<li><i>id</i> External id from runner database.</li>
<li><i>card</i> Card number.</li>
<li><i>name</i> Name of competitor. Possibly a partial name.</li>
<li><i>club</i> Name of club.</li>
</ul>
<pre>
*DatabaseCompetitors>
*Competitor id="15393">
*Name>Anskar Dahl*/Name>
*Club id="575">IFK Mora OK*/Club>
*Card>79709*/Card>
*Nationality>SWE*/Nationality>
*Sex>M*/Sex>
*BirthYear>1957*/BirthYear>
*/Competitor>
*/DatabaseCompetitors>
</pre>
<h2>Lookup Database Club</h2>
<b>Syntax:</b>
<pre><a href="/meos?lookup=dbclub&id=%clubid%">/meos?lookup=dbclub&id=*id></a></pre>
<pre><a href="/meos?lookup=dbclub&name=%club%">/meos?lookup=dbclub&name=*name></a></pre>
<b>Returns:</b>
<p>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.</p>
<b>Arguments:</b>
<ul>
<li><i>id</i> External id from club database.</li>
<li><i>name</i> Name of club. Possibly a partial name.</li>
</ul>
<pre>
*DatabaseClubs>
*Club id="134">
*Name>OK Enen*/Name>
*/Club>
*/DatabaseClubs>
</pre>
<h2>API New Entry</h2>
<b>Syntax:</b>
<pre>/meos?entry&id=*id>&class=*classid>&card=*card></pre>
<pre>/meos?entry&name=*name>&club=*club>&class=*classid>&card=*card></pre>
<b>Arguments:</b>
<ul>
<li><i>id</i> External id of runner from runner database.</li>
<li><i>name</i> Name of runner.</li>
<li><i>club</i> Name of runner's club.</li>
<li><i>class</i> Id of class.</li>
<li><i>card</i> Card number.</li>
</ul>
<b>Returns:</b>
Status.
<pre>
*Answer>
*Status>OK*/Status>
*Fee>130*/Fee>
*Info>Open Long, Rudolf Minowski (OK Tisaren)*/Info>
*/Answer>
</pre>
<pre>
*Answer>
*Status>Failed*/Status>
*Info>Out of maps.*/Info>
*/Answer>
</pre>
<h2>Page template</h2>
<b>Syntax:</b>
<pre>/meos?page=*page></pre>
<b>Returns:</b>
Installed template file with the specified tag.
<h2>Image</h2>
<b>Syntax:</b>
<pre>/meos?image=*image></pre>
<b>Returns:</b>
Image, *image>.png, if installed in MeOS datafolder. MeOS logo if *image> is meos.
<h2>IOF XML Results</h2>
<b>Syntax:</b>

View File

@ -269,7 +269,7 @@ void IOF30Interface::classCourseAssignment(gdioutput &gdi, xmlList &xAssignment,
if (pc->hasMultiCourse()) {
for (size_t k = 0; k < pc->getNumStages(); k++) {
for (size_t j = 0; j < pCrs.size(); j++)
pc->addStageCourse(k, pCrs[j]);
pc->addStageCourse(k, pCrs[j], -1);
}
}
else
@ -277,7 +277,7 @@ void IOF30Interface::classCourseAssignment(gdioutput &gdi, xmlList &xAssignment,
}
else if (leg == 0 && pCrs.size() == 1) {
if (pc->hasMultiCourse())
pc->addStageCourse(0, pCrs[0]);
pc->addStageCourse(0, pCrs[0], -1);
else
pc->setCourse(pCrs[0]);
}
@ -286,7 +286,7 @@ void IOF30Interface::classCourseAssignment(gdioutput &gdi, xmlList &xAssignment,
pc->setNumStages(leg+1);
for (size_t j = 0; j < pCrs.size(); j++)
pc->addStageCourse(leg, pCrs[j]);
pc->addStageCourse(leg, pCrs[j], -1);
}
}
}
@ -620,7 +620,7 @@ void IOF30Interface::classAssignmentObsolete(gdioutput &gdi, xmlList &xAssignmen
c[k]->setNumStages(1);
c[k]->clearStageCourses(0);
for (size_t j = 0; j < crsFam.size(); j++)
c[k]->addStageCourse(0, crsFam[j]->getId());
c[k]->addStageCourse(0, crsFam[j]->getId(), -1);
}
}
else {
@ -628,7 +628,7 @@ void IOF30Interface::classAssignmentObsolete(gdioutput &gdi, xmlList &xAssignmen
for (int i = 0; i < nl; i++) {
c[k]->clearStageCourses(i);
for (int j = 0; j < nFam; j++)
c[k]->addStageCourse(i, crsFam[(j + i)%nFam]->getId());
c[k]->addStageCourse(i, crsFam[(j + i)%nFam]->getId(), -1);
}
}
assigned = true;
@ -648,7 +648,7 @@ void IOF30Interface::classAssignmentObsolete(gdioutput &gdi, xmlList &xAssignmen
const vector<pCourse> &crsFam = coursesFamilies.find(*fit)->second;
int nFam = crsFam.size();
for (int j = 0; j < nFam; j++)
c[k]->addStageCourse(i, crsFam[j]->getId());
c[k]->addStageCourse(i, crsFam[j]->getId(), -1);
}
assigned = true;
@ -667,7 +667,7 @@ void IOF30Interface::classAssignmentObsolete(gdioutput &gdi, xmlList &xAssignmen
for (int i = 0; i < nl; i++) {
c[k]->clearStageCourses(i);
for (int j = 0; j < nCrs; j++)
c[k]->addStageCourse(i, crs[(j + i)%nCrs]->getId());
c[k]->addStageCourse(i, crs[(j + i)%nCrs]->getId(), -1);
}
}
}
@ -739,6 +739,8 @@ void IOF30Interface::readEntryList(gdioutput &gdi, xmlobject &xo, bool removeNon
int &entRead, int &entFail, int &entRemoved) {
string ver;
entRemoved = 0;
bool wasEmpty = oe.getNumRunners() == 0;
xo.getObjectString("iofVersion", ver);
if (!ver.empty() && ver > "3.0")
gdi.addString("", 0, "Varning, okänd XML-version X#" + ver);
@ -962,8 +964,95 @@ void IOF30Interface::readEntryList(gdioutput &gdi, xmlobject &xo, bool removeNon
if (!rids.empty())
oe.removeRunner(rids);
}
if (wasEmpty) {
vector<pClass> allCls;
oe.getClasses(allCls, false);
set<int> fees;
set<int> redFees;
set<double> factor;
for (pClass cls : allCls) {
int cf = cls->getDCI().getInt("ClassFee");
int cfRed = cls->getDCI().getInt("ClassFeeRed");
if (cf > 0)
fees.insert(cf);
if (cfRed != 0 && cfRed != cf)
redFees.insert(cfRed);
int cfLate = cls->getDCI().getInt("HighClassFee");
if (cfLate > cf && cf > 0) {
factor.insert(double(cfLate) / double(cf));
}
}
int youthFee = numeric_limits<int>::max();
if (!fees.empty())
youthFee = min(youthFee, *fees.begin());
if (!redFees.empty())
youthFee = min(youthFee, *redFees.begin());
int eliteFee = numeric_limits<int>::max();
if (!fees.empty())
eliteFee = *fees.rbegin();
int normalFee = eliteFee;
for (int f : fees) {
if (f != youthFee && f != eliteFee) {
normalFee = f;
}
}
if (youthFee != numeric_limits<int>::max()) {
oe.getDI().setInt("YouthFee", youthFee);
}
if (eliteFee != numeric_limits<int>::max()) {
oe.getDI().setInt("EliteFee", eliteFee);
}
if (normalFee != numeric_limits<int>::max()) {
oe.getDI().setInt("EntryFee", normalFee);
}
if (factor.size() > 0) {
double f = *factor.rbegin();
wstring fs = std::to_wstring(int((f - 1.0) * 100.0)) + L" %";
oe.getDI().setString("LateEntryFactor", fs);
}
}
}
void IOF30Interface::readServiceRequestList(gdioutput &gdi, xmlobject &xo, int &entRead, int &entFail) {
string ver;
xo.getObjectString("iofVersion", ver);
if (!ver.empty() && ver > "3.0")
gdi.addString("", 0, "Varning, okänd XML-version X#" + ver);
xmlList req;
xo.getObjects("PersonServiceRequest", req);
entrySourceId = 0;
for (auto &rx : req) {
xmlobject xPers = rx.getObject("Person");
pRunner r = 0;
if (xPers)
r = readPerson(gdi, xPers);
if (r) {
auto xreq = rx.getObject("ServiceRequest");
if (xreq) {
auto xServ = xreq.getObject("Service");
string type;
if (xServ && xServ.getObjectString("type", type)=="StartGroup") {
int id = xServ.getObjectInt("Id");
r->getDI().setInt("Heat", id);
}
}
}
}
}
void IOF30Interface::readStartList(gdioutput &gdi, xmlobject &xo, int &entRead, int &entFail) {
string ver;
@ -1149,10 +1238,13 @@ void IOF30Interface::readEvent(gdioutput &gdi, const xmlobject &xo,
if (date) {
wstring dateStr;
date.getObjectString("Date", dateStr);
oe.setDate(dateStr);
wstring timeStr;
date.getObjectString("Time", timeStr);
if (!timeStr.empty()) {
wstring tDate, tTime;
getLocalDateTime(dateStr, timeStr, tDate, tTime);
dateStr.swap(tDate);
timeStr.swap(tTime);
int t = convertAbsoluteTimeISO(timeStr);
if (t >= 0 && oe.getNumRunners() == 0) {
int zt = t - 3600;
@ -1161,6 +1253,9 @@ void IOF30Interface::readEvent(gdioutput &gdi, const xmlobject &xo,
oe.setZeroTime(formatTimeHMS(zt));
}
}
oe.setDate(dateStr);
//oe.setZeroTime(...);
}
@ -1346,12 +1441,12 @@ pTeam IOF30Interface::readTeamEntry(gdioutput &gdi, xmlobject &xTeam,
if (newTeam) {
wstring entryTime;
xTeam.getObjectString("EntryTime", entryTime);
di.setDate("EntryDate", entryTime);
size_t tpos = entryTime.find_first_of(L"T");
if (tpos != -1) {
wstring timeString = entryTime.substr(tpos+1);
int t = convertAbsoluteTimeISO(timeString);
wstring date, time;
getLocalDateTime(entryTime, date, time);
di.setDate("EntryDate", date);
if (time.length() > 0) {
int t = convertAbsoluteTimeISO(time);
if (t >= 0)
di.setInt("EntryTime", t);
}
@ -1450,7 +1545,8 @@ pTeam IOF30Interface::getCreateTeam(gdioutput &gdi, const xmlobject &xTeam, bool
if (!t)
return 0;
t->setEntrySource(entrySourceId);
if (entrySourceId > 0)
t->setEntrySource(entrySourceId);
t->flagEntryTouched(true);
if (t->getName().empty() || !t->hasFlag(oAbstractRunner::FlagUpdateName))
t->setName(name, false);
@ -1608,16 +1704,16 @@ pRunner IOF30Interface::readPersonEntry(gdioutput &gdi, xmlobject &xo, pTeam tea
wstring entryTime;
xo.getObjectString("EntryTime", entryTime);
di.setDate("EntryDate", entryTime);
size_t tpos = entryTime.find_first_of(L"T");
if (tpos != -1) {
wstring timeString = entryTime.substr(tpos+1);
int t = convertAbsoluteTimeISO(timeString);
wstring date, time;
getLocalDateTime(entryTime, date, time);
di.setDate("EntryDate", date);
if (time.length() > 0) {
int t = convertAbsoluteTimeISO(time);
if (t >= 0)
di.setInt("EntryTime", t);
}
double fee = 0, paid = 0, taxable = 0, percentage = 0;
wstring currency;
xmlList xAssigned;
@ -1836,7 +1932,8 @@ pRunner IOF30Interface::readPerson(gdioutput &gdi, const xmlobject &person) {
}
}
r->setEntrySource(entrySourceId);
if (entrySourceId > 0)
r->setEntrySource(entrySourceId);
r->flagEntryTouched(true);
if (!r->hasFlag(oAbstractRunner::FlagUpdateName)) {
@ -2115,16 +2212,20 @@ void IOF30Interface::getAgeLevels(const vector<FeeInfo> &fees, const vector<int>
redIx = ix[k];
if (fees[normalIx] < fees[ix[k]])
normalIx = ix[k];
}
for (size_t k = 0; k < ix.size(); k++) {
const wstring &to = fees[ix[k]].toBirthDate;
const wstring &from = fees[ix[k]].fromBirthDate;
if (!from.empty() && (youthLimit.empty() || youthLimit > from))
if (!from.empty() && (youthLimit.empty() || youthLimit > from) && fees[ix[k]].fee == fees[redIx].fee)
youthLimit = from;
if (!to.empty() && (seniorLimit.empty() || seniorLimit > to))
if (!to.empty() && (seniorLimit.empty() || seniorLimit > to) && fees[ix[k]].fee == fees[redIx].fee)
seniorLimit = to;
}
}
}
@ -2395,6 +2496,123 @@ int IOF30Interface::parseISO8601Time(const xmlobject &xo) {
return oe.getRelativeTime(date, time, zone);
}
void IOF30Interface::getLocalDateTime(const string &date, const string &time,
string &dateOut, string &timeOut) {
int zIx = -1;
for (size_t k = 0; k < time.length(); k++) {
if (time[k] == '+' || time[k] == '-' || time[k] == 'Z') {
if (zIx == -1)
zIx = k;
else;
// Bad format
}
}
if (zIx == -1) {
dateOut = date;
timeOut = time;
return;
}
string timePart = time.substr(0, zIx);
string zone = time.substr(zIx);
wstring wTime(timePart.begin(), timePart.end());
SYSTEMTIME st;
memset(&st, 0, sizeof(SYSTEMTIME));
int atime = convertAbsoluteTimeISO(wTime);
int idate = convertDateYMS(date, st, true);
if (idate != -1) {
if (zone == "Z" || zone == "z") {
st.wHour = atime / 3600;
st.wMinute = (atime / 60) % 60;
st.wSecond = atime % 60;
SYSTEMTIME localTime;
memset(&localTime, 0, sizeof(SYSTEMTIME));
SystemTimeToTzSpecificLocalTime(0, &st, &localTime);
char bf[64];
sprintf(bf, "%02d:%02d:%02d", localTime.wHour, localTime.wMinute, localTime.wSecond);
timeOut = bf;
sprintf(bf, "%d-%02d-%02d", localTime.wYear, localTime.wMonth, localTime.wDay);
dateOut = bf;
}
else {
dateOut = date;
timeOut = time;
}
}
}
void IOF30Interface::getLocalDateTime(const wstring &datetime, wstring &dateOut, wstring &timeOut) {
size_t t = datetime.find_first_of('T');
if (t != dateOut.npos) {
wstring date = datetime.substr(0, t);
wstring time = datetime.substr(t + 1);
getLocalDateTime(date, time, dateOut, timeOut);
}
else {
dateOut = datetime;
timeOut = L"";
}
}
void IOF30Interface::getLocalDateTime(const wstring &date, const wstring &time,
wstring &dateOut, wstring &timeOut) {
int zIx = -1;
for (size_t k = 0; k < time.length(); k++) {
if (time[k] == '+' || time[k] == '-' || time[k] == 'Z') {
if (zIx == -1)
zIx = k;
else;
// Bad format
}
}
if (zIx == -1) {
dateOut = date;
timeOut = time;
return;
}
wstring timePart = time.substr(0, zIx);
wstring zone = time.substr(zIx);
wstring wTime(timePart.begin(), timePart.end());
SYSTEMTIME st;
memset(&st, 0, sizeof(SYSTEMTIME));
int atime = convertAbsoluteTimeISO(wTime);
int idate = convertDateYMS(date, st, true);
if (idate != -1) {
if (zone == L"Z" || zone == L"z") {
st.wHour = atime / 3600;
st.wMinute = (atime / 60) % 60;
st.wSecond = atime % 60;
SYSTEMTIME localTime;
memset(&localTime, 0, sizeof(SYSTEMTIME));
SystemTimeToTzSpecificLocalTime(0, &st, &localTime);
atime = localTime.wHour * 3600 + localTime.wMinute * 60 + localTime.wSecond;
wchar_t bf[64];
wsprintf(bf, L"%02d:%02d:%02d", localTime.wHour, localTime.wMinute, localTime.wSecond);
timeOut = bf;
wsprintf(bf, L"%d-%02d-%02d", localTime.wYear, localTime.wMonth, localTime.wDay);
dateOut = bf;
//dateOut = itow(localTime.wYear) + L"-" + itow(localTime.wMonth) + L"-" + itow(localTime.wDay);
//timeOut = itow(localTime.wHour) + L":" + itow(localTime.wMinute) + L":" + itow(localTime.wSecond);
}
else {
dateOut = date;
timeOut = time;
}
}
}
void IOF30Interface::getProps(vector<wstring> &props) const {
props.push_back(L"xmlns");
props.push_back(L"http://www.orienteering.org/datastandard/3.0");
@ -3601,7 +3819,7 @@ void IOF30Interface::bindClassCourse(oClass &pc, const vector< vector<pCourse> >
for (size_t k = 0; k < crs.size(); k++) {
pc.clearStageCourses(k);
for (size_t j = 0; j < crs[k].size(); j++) {
pc.addStageCourse(k, crs[k][j]->getId());
pc.addStageCourse(k, crs[k][j]->getId(), -1);
}
}
}

View File

@ -274,6 +274,13 @@ public:
IOF30Interface(oEvent *oe, bool forceSplitFee);
virtual ~IOF30Interface() {}
static void getLocalDateTime(const wstring &datetime, wstring &dateOut, wstring &timeOut);
static void getLocalDateTime(const wstring &date, const wstring &time,
wstring &dateOut, wstring &timeOut);
static void getLocalDateTime(const string &date, const string &time,
string &dateOut, string &timeOut);
void getIdTypes(vector<string> &types);
void setPreferredIdType(const string &type);
@ -287,6 +294,8 @@ public:
void readStartList(gdioutput &gdi, xmlobject &xo, int &entRead, int &entFail);
void readServiceRequestList(gdioutput &gdi, xmlobject &xo, int &entRead, int &entFail);
void readClassList(gdioutput &gdi, xmlobject &xo, int &entRead, int &entFail);
void readCompetitorList(gdioutput &gdi, const xmlobject &xo, int &personCount);

View File

@ -1214,6 +1214,13 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
if (autoTask)
autoTask->synchronize(gdi_extra);
break;
case WM_USER + 5:
if (gdi_main)
gdi_main->addInfoBox("ainfo", L"info:advanceinfo", 10000);
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);

View File

@ -30,7 +30,7 @@
//V35: abcdef
//V36: abcdef
int getMeosBuild() {
string revision("$Rev: 859 $");
string revision("$Rev: 895 $");
return 174 + atoi(revision.substr(5, string::npos).c_str());
}
@ -42,12 +42,12 @@ int getMeosBuild() {
//V33: abcdefghij
//V34: abcdfge
wstring getMeosDate() {
wstring date(L"$Date: 2019-03-19 21:52:12 +0100 (ti, 19 mar 2019) $");
wstring date(L"$Date: 2019-05-11 07:26:35 +0200 (lö, 11 maj 2019) $");
return date.substr(7,10);
}
wstring getBuildType() {
return L"RC1"; // No parantheses (...)
return L""; // No parantheses (...)
}
wstring getMajorVersion() {
@ -128,5 +128,7 @@ void getSupporters(vector<wstring> &supp, vector<wstring> &developSupp)
supp.emplace_back(L"Ligue PACA");
supp.emplace_back(L"SC vebr-sport");
supp.emplace_back(L"IP Skogen Göteborg");
supp.emplace_back(L"Smedjebackens Orientering");
supp.emplace_back(L"Gudhems IF");
reverse(supp.begin(), supp.end());
}

View File

@ -1654,6 +1654,8 @@ void MetaList::initSymbols() {
typeToSymbol[lClassStartTimeRange] = L"StartTimeForClassRange";
typeToSymbol[lClassLength] = L"ClassLength";
typeToSymbol[lClassResultFraction] = L"ClassResultFraction";
typeToSymbol[lClassAvailableMaps] = L"ClassAvailableMaps";
typeToSymbol[lClassTotalMaps] = L"ClassTotalMaps";
typeToSymbol[lCourseLength] = L"CourseLength";
typeToSymbol[lCourseName] = L"CourseName";
typeToSymbol[lCourseClimb] = L"CourseClimb";
@ -1712,6 +1714,7 @@ void MetaList::initSymbols() {
typeToSymbol[lRunnerNationality] = L"RunnerNationality";
typeToSymbol[lRunnerPhone] = L"RunnerPhone";
typeToSymbol[lRunnerFee] = L"RunnerFee";
typeToSymbol[lRunnerExpectedFee] = L"RunnerExpectedFee";
typeToSymbol[lRunnerPaid] = L"RunnerPaid";
typeToSymbol[lRunnerPayMethod] = L"RunnerPayMethod";
typeToSymbol[lRunnerEntryDate] = L"RunnerEntryDate";
@ -1881,6 +1884,7 @@ void MetaList::initSymbols() {
filterToSymbol[EFilterHasNoCard] = "FilterNoCard";
filterToSymbol[EFilterAnyResult] = "FilterAnyResult";
filterToSymbol[EFilterAPIEntry] = "EFilterAPIEntry";
filterToSymbol[EFilterWrongFee] = "EFilterWrongFee";
for (map<EFilterList, string>::iterator it = filterToSymbol.begin();
it != filterToSymbol.end(); ++it) {

View File

@ -536,7 +536,7 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) {
wstring str;
try {
st = currentResult->deduceStatus(*rr[k]);
str = oe->formatStatus(st);
str = oe->formatStatus(st, false);
}
catch (meosException &ex) {
err = ex.wwhat();
@ -638,7 +638,7 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) {
wstring str;
try {
st = currentResult->deduceStatus(*tr[k]);
str = oe->formatStatus(st);
str = oe->formatStatus(st, false);
}
catch (meosException &ex) {
err = ex.wwhat();
@ -964,7 +964,7 @@ void MethodEditor::debug(gdioutput &gdi_in, int id, bool isTeam) {
st = currentResult->deduceStatus(r);
currentResult->debugDumpVariables(gdi, true);
gdi.addStringUT(1, L"ComputedStatus: " + oe->formatStatus(st)).setColor(colorGreen);
gdi.addStringUT(1, L"ComputedStatus: " + oe->formatStatus(st, false)).setColor(colorGreen);
}
catch (meosException &ex) {
currentResult->debugDumpVariables(gdi, true);
@ -1018,7 +1018,7 @@ void MethodEditor::debug(gdioutput &gdi_in, int id, bool isTeam) {
st = currentResult->deduceStatus(t);
currentResult->debugDumpVariables(gdi, true);
gdi.addStringUT(1, L"ComputedStatus: " + oe->formatStatus(st)).setColor(colorGreen);
gdi.addStringUT(1, L"ComputedStatus: " + oe->formatStatus(st, false)).setColor(colorGreen);
}
catch (meosException &ex) {
currentResult->debugDumpVariables(gdi, true);

View File

@ -44,8 +44,7 @@ static char THIS_FILE[]=__FILE__;
char RunnerStatusOrderMap[100];
oBase::oBase(oEvent *poe)
{
oBase::oBase(oEvent *poe) {
Removed = false;
oe = poe;
Id = 0;
@ -57,8 +56,14 @@ oBase::oBase(oEvent *poe)
localObject = false;
}
oBase::~oBase()
{
oBase::~oBase(){
if (myReference)
myReference->ref = nullptr;
}
void oBase::remove() {
if (myReference)
myReference->ref = nullptr;
}
bool oBase::synchronize(bool writeOnly)

View File

@ -34,6 +34,7 @@
#include "TimeStamp.h"
#include "stdafx.h"
#include <vector>
#include <memory>
class oEvent;
class gdioutput;
@ -72,8 +73,18 @@ enum SortOrder {ClassStartTime,
Custom,
SortEnumLastItem};
class oBase
{
class oBase {
public:
class oBaseReference {
private:
oBase * ref = nullptr;
public:
oBase * get() {
return ref;
}
friend class oBase;
};
private:
void storeChangeStatus() {reChanged = changed;}
void resetChangeStatus() {changed &= reChanged;}
@ -83,6 +94,7 @@ 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<oBaseReference> myReference;
protected:
int Id;
@ -118,6 +130,15 @@ protected:
public:
// Get a safe reference to this object
const shared_ptr<oBaseReference> &getReference() {
if (!myReference) {
myReference = make_shared<oBaseReference>();
myReference->ref = this;
}
return myReference;
}
// Returns true if the object is local, not stored in DB/On disc
bool isLocalObject() { return localObject; }

View File

@ -706,16 +706,10 @@ void oCard::getPunches(vector<pPunch> &punchesOut) const {
}
}
bool oCard::comparePunchTime(oPunch *p1, oPunch *p2) {
return p1->Time < p2->Time;
}
void oCard::setupFromRadioPunches(oRunner &r) {
oe->synchronizeList(oListId::oLPunchId);
vector<pFreePunch> p;
oe->getPunchesForRunner(r.getId(), p);
sort(p.begin(), p.end(), comparePunchTime);
oe->getPunchesForRunner(r.getId(), true, p);
for (size_t k = 0; k < p.size(); k++)
addPunch(p[k]->Type, p[k]->Time, 0);

View File

@ -644,21 +644,44 @@ bool oClass::fillStageCourses(gdioutput &gdi, int stage,
return true;
}
bool oClass::addStageCourse(int iStage, int courseId)
bool oClass::addStageCourse(int iStage, int courseId, int index)
{
return addStageCourse(iStage, oe->getCourse(courseId));
return addStageCourse(iStage, oe->getCourse(courseId), index);
}
bool oClass::addStageCourse(int iStage, pCourse pc)
bool oClass::addStageCourse(int iStage, pCourse pc, int index)
{
if (unsigned(iStage)>=MultiCourse.size())
return false;
vector<pCourse> &Stage=MultiCourse[iStage];
vector<pCourse> &stage=MultiCourse[iStage];
if (pc) {
tCoursesChanged = true;
Stage.push_back(pc);
if (index == -1 || size_t(index) >= stage.size())
stage.push_back(pc);
else {
stage.insert(stage.begin() + index, pc);
}
updateChanged();
return true;
}
return false;
}
bool oClass::moveStageCourse(int stage, int index, int offset) {
if (unsigned(stage) >= MultiCourse.size())
return false;
vector<pCourse> &stages = MultiCourse[stage];
if (offset == -1 && size_t(index) < stages.size() && index > 0) {
swap(stages[index - 1], stages[index]);
updateChanged();
return true;
}
else if (offset == 1 && size_t(index + 1) < stages.size() && index >= 0) {
swap(stages[index + 1], stages[index]);
updateChanged();
return true;
}
@ -2622,11 +2645,8 @@ int oClass::getNumRemainingMaps(bool forceRecalculate) const {
numMaps = Course->tMapsRemaining;
else
numMaps = min(numMaps, Course->tMapsRemaining);
return numMaps;
}
else
return numMaps;
return numMaps;
}
void oClass::setNumberMaps(int nm) {
@ -2648,39 +2668,19 @@ int oClass::getNumberMaps(bool rawAttribute) const {
void oEvent::getStartBlocks(vector<int> &blocks, vector<wstring> &starts) const
{
oClassList::const_iterator it;
map<int, wstring> bs;
set<pair<wstring, int>> bs;
for (it = Classes.begin(); it != Classes.end(); ++it) {
if (it->isRemoved())
continue;
map<int, wstring>::iterator v = bs.find(it->getBlock());
if (v!=bs.end() && v->first!=0 && v->second!=it->getStart()) {
wstring msg = L"Ett startblock spänner över flera starter: X/Y#" + it->getStart() + L"#" + v->second;
throw meosException(msg.c_str());
}
bs[it->getBlock()] = it->getStart();
bs.emplace(it->getStart(), it->getBlock());
}
blocks.clear();
starts.clear();
if (bs.size() > 1) {
for (map<int, wstring>::iterator v = bs.begin(); v != bs.end(); ++v) {
blocks.push_back(v->first);
starts.push_back(v->second);
}
}
else if (bs.size() == 1) {
set<wstring> s;
for (it = Classes.begin(); it != Classes.end(); ++it) {
if (it->isRemoved())
continue;
s.insert(it->getStart());
}
for (set<wstring>::iterator v = s.begin(); v != s.end(); ++v) {
blocks.push_back(bs.begin()->first);
starts.push_back(*v);
}
for (auto &v : bs) {
blocks.push_back(v.second);
starts.push_back(v.first);
}
}
@ -2861,20 +2861,24 @@ void oClass::addClassDefaultFee(bool resetFee) {
}
}
void oClass::reinitialize() {
int ix = getDI().getInt("SortIndex");
void oClass::reinitialize(bool force) const {
if (!force && isInitialized)
return;
isInitialized = true; // Prevent recursion
int ix = getDCI().getInt("SortIndex");
if (ix == 0) {
ix = getSortIndex(getId()*10);
getDI().setInt("SortIndex", ix);
const_cast<oClass*>(this)->getDI().setInt("SortIndex", ix);
}
tSortIndex = ix;
tMaxTime = getDI().getInt("MaxTime");
tMaxTime = getDCI().getInt("MaxTime");
if (tMaxTime == 0 && oe) {
tMaxTime = oe->getMaximalTime();
}
wstring wInfo = getDI().getString("Qualification");
wstring wInfo = getDCI().getString("Qualification");
if (!wInfo.empty()) {
if (qualificatonFinal && !qualificatonFinal->matchSerialization(wInfo))
clearQualificationFinal();
@ -2897,7 +2901,7 @@ void oClass::reinitialize() {
tIgnoreStartPunch = -1;
}
void oClass::clearQualificationFinal() {
void oClass::clearQualificationFinal() const {
if (!qualificatonFinal)
return;
@ -2911,15 +2915,12 @@ void oClass::clearQualificationFinal() {
qualificatonFinal.reset();
}
void oEvent::reinitializeClasses()
{
for (oClassList::iterator it = Classes.begin(); it != Classes.end(); ++it)
it->reinitialize();
void oEvent::reinitializeClasses() const {
for (auto &c : Classes)
c.reinitialize(true);
}
int oClass::getSortIndex(int candidate)
{
int oClass::getSortIndex(int candidate) const {
int major = numeric_limits<int>::max();
int minor = 0;
@ -3340,6 +3341,7 @@ bool oClass::canRemove() const
}
int oClass::getMaximumRunnerTime() const {
reinitialize(false);
return tMaxTime;
}
@ -3821,10 +3823,10 @@ pair<int, int> oClass::autoForking(const vector< vector<int> > &inputCourses) {
lastSet = j;
for (size_t k = 0; k < courseMatrix[j].size(); k++) {
if (k < fperm.size()) {
addStageCourse(j, courseMatrix[j][fperm[k]]);
addStageCourse(j, courseMatrix[j][fperm[k]], -1);
}
else {
addStageCourse(j, courseMatrix[j][k]);
addStageCourse(j, courseMatrix[j][k], -1);
}
}
}
@ -4402,6 +4404,7 @@ void oClass::configureInstance(int instance, bool allowCreation) const {
}
int oClass::getNumQualificationFinalClasses() const {
reinitialize(false);
if (qualificatonFinal)
return qualificatonFinal->getNumClasses()+1;
return 0;

View File

@ -200,8 +200,10 @@ protected:
mutable int tIgnoreStartPunch;
// Sort classes for this index
int tSortIndex;
int tMaxTime;
mutable int tSortIndex;
mutable int tMaxTime;
mutable bool isInitialized = false;
// True when courses was changed on this client. Used to update course pool bindings
bool tCoursesChanged;
@ -244,7 +246,7 @@ protected:
void exportIOFStart(xmlparser &xml);
/** Setup transient data */
void reinitialize();
void reinitialize(bool force) const;
/** Recalculate derived data */
void apply();
@ -264,7 +266,7 @@ protected:
mutable vector<ClassResultInfo> tResultInfo;
/** Get/calculate sort index from candidate */
int getSortIndex(int candidate);
int getSortIndex(int candidate) const;
/** Get internal data buffers for DI */
oDataContainer &getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const;
@ -276,7 +278,7 @@ protected:
mutable vector<pClass> virtualClasses;
pClass parentClass;
shared_ptr<QualificationFinal> qualificatonFinal;
mutable shared_ptr<QualificationFinal> qualificatonFinal;
int tMapsRemaining;
mutable int tMapsUsed;
@ -288,9 +290,12 @@ public:
/** The master class in a qualification/final scheme. */
const pClass getParentClass() const { return parentClass; }
const QualificationFinal *getQualificationFinal() const { return qualificatonFinal.get(); }
const QualificationFinal *getQualificationFinal() const {
reinitialize(false);
return qualificatonFinal.get();
}
void clearQualificationFinal();
void clearQualificationFinal() const;
bool isQualificationFinalClass() const {
return parentClass && parentClass->isQualificationFinalBaseClass();
@ -565,9 +570,10 @@ public:
int getCourseId() const {if (Course) return Course->getId(); else return 0;}
void setCourse(pCourse c);
bool addStageCourse(int stage, int courseId);
bool addStageCourse(int stage, pCourse pc);
bool addStageCourse(int stage, int courseId, int index);
bool addStageCourse(int stage, pCourse pc, int index);
void clearStageCourses(int stage);
bool moveStageCourse(int stage, int index, int offset);
bool removeStageCourse(int stage, int courseId, int position);

View File

@ -317,12 +317,13 @@ Table *oEvent::getClubsTB()//Table mode
void oEvent::generateClubTableData(Table &table, oClub *addClub)
{
oe->setupClubInfoData();
if (addClub) {
addClub->addTableRow(table);
return;
}
synchronizeList(oListId::oLClubId);
oe->setupClubInfoData();
oClubList::iterator it;
for (it=Clubs.begin(); it != Clubs.end(); ++it){
@ -919,7 +920,9 @@ void oEvent::printInvoices(gdioutput &gdi, InvoicePrintType type,
if (!it->isRemoved() && clubId.count(it->getId()) > 0) {
gdi.addStringUT(yp, 50, fontMedium, itos(it->getDCI().getInt("InvoiceNo")));
gdi.addStringUT(yp, 240, textRight|fontMedium, itos(it->getId()));
if (it->getExtIdentifier() != 0)
gdi.addStringUT(yp, 240, textRight|fontMedium, it->getExtIdentifierString());
gdi.addStringUT(yp, 250, fontMedium, it->getName());
gdi.addStringUT(yp, 550, fontMedium|textRight, oe->formatCurrency(fees[k]));
gdi.addStringUT(yp, 620, fontMedium|textRight, oe->formatCurrency(vpaid[k]));

View File

@ -904,6 +904,7 @@ void oCourse::clearCache() const {
cachedHasRogaining = 0;
cachedControlOrdinal.clear();
cacheDataRevision = oe->dataRevision;
oe->tCalcNumMapsDataRevision = -1;
tMapsUsed = -1;
tMapsUsedNoVacant = -1;
}

View File

@ -68,8 +68,8 @@ protected:
vector<int> legLengths;
int tMapsRemaining;
mutable int tMapsUsed;
mutable int tMapsUsedNoVacant;
mutable int tMapsUsed = -1;
mutable int tMapsUsedNoVacant = -1;
// Get an identity sum based on controls
int getIdSum(int nControls);

View File

@ -650,16 +650,24 @@ pControl oEvent::getControl(int Id) const {
return const_cast<oEvent *>(this)->getControl(Id, false);
}
pControl oEvent::getControlByType(int type) const {
for (auto &c : Controls) {
if (!c.isRemoved() && c.getFirstNumber() == type)
return pControl(&c);
}
return nullptr;
}
pControl oEvent::getControl(int Id, bool create) {
oControlList::const_iterator it;
for (it=Controls.begin(); it != Controls.end(); ++it) {
if (it->Id==Id)
if (it->Id==Id && !it->isRemoved())
return pControl(&*it);
}
if (!create || Id<=0)
return 0;
return nullptr;
//Not found. Auto add...
return addControl(Id, Id, L"");
@ -1420,14 +1428,7 @@ pRunner oEvent::dbLookUpById(__int64 extId) const
sRunner.setTemporary();
RunnerWDBEntry *dbr = runnerDB->getRunnerById(int(extId));
if (dbr != 0) {
sRunner.init(*dbr);
/*dbr->getName(sRunner.Name);
sRunner.CardNo = dbr->cardNo;
sRunner.Club = runnerDB->getClub(dbr->clubNo);
sRunner.getDI().setString("Nationality", dbr->getNationality());
sRunner.getDI().setInt("BirthYear", dbr->getBirthYear());
sRunner.getDI().setString("Sex", dbr->getSex());
sRunner.setExtIdentifier(dbr->getExtId());*/
sRunner.init(*dbr, false);
return &sRunner;
}
else
@ -1446,7 +1447,7 @@ pRunner oEvent::dbLookUpByCard(int cardNo) const
if (dbr != 0) {
dbr->getName(sRunner.sName);
oRunner::getRealName(sRunner.sName, sRunner.tRealName);
sRunner.init(*dbr);
sRunner.init(*dbr, false);
sRunner.cardNumber = cardNo;
return &sRunner;
}
@ -1482,14 +1483,7 @@ pRunner oEvent::dbLookUpByName(const wstring &name, int clubId, int classId, int
RunnerWDBEntry *dbr = runnerDB->getRunnerByName(name, clubId, birthYear);
if (dbr) {
sRunner.init(*dbr);
/*
dbr->getName(sRunner.Name);
sRunner.CardNo = dbr->cardNo;
sRunner.Club = runnerDB->getClub(dbr->clubNo);
sRunner.getDI().setString("Nationality", dbr->getNationality());
sRunner.getDI().setInt("BirthYear", dbr->getBirthYear());
sRunner.getDI().setString("Sex", dbr->getSex());*/
sRunner.init(*dbr, false);
sRunner.setExtIdentifier(int(dbr->getExtId()));
return &sRunner;
}
@ -2050,6 +2044,15 @@ bool oEvent::sortRunners(SortOrder so) {
return true;
}
bool oEvent::sortRunners(SortOrder so, vector<const oRunner *> &runners) const {
reinitializeClasses();
auto oldSortOrder = CurrentSortOrder;
CurrentSortOrder = so;
sort(runners.begin(), runners.end(), [](const oRunner * &a, const oRunner * &b)->bool {return *a < *b; });
CurrentSortOrder = oldSortOrder;
return true;
}
bool oEvent::sortTeams(SortOrder so, int leg, bool linearLeg) {
reinitializeClasses();
oTeamList::iterator it;
@ -2165,6 +2168,120 @@ bool oEvent::sortTeams(SortOrder so, int leg, bool linearLeg) {
return true;
}
bool oEvent::sortTeams(SortOrder so, int leg, bool linearLeg, vector<const oTeam *> &teams) const {
reinitializeClasses();
map<int, int> classId2Linear;
if (so == ClassResult || so == ClassTotalResult || so == ClassTeamLegResult) {
bool totalResult = so == ClassTotalResult;
bool legResult = so == ClassTeamLegResult;
bool hasRunner = (leg == -1);
for (auto it : teams) {
int lg = leg;
int clsId = it->Class->getId();
if (leg >= 0 && !linearLeg && it->Class) {
map<int, int>::iterator res = classId2Linear.find(it->Class->getId());
if (res == classId2Linear.end()) {
unsigned linLegBase = it->Class->getLegNumberLinear(leg, 0);
while (linLegBase + 1 < it->Class->getNumStages()) {
if (it->Class->isParallel(linLegBase + 1) || it->Class->isOptional(linLegBase + 1))
linLegBase++;
else
break;
}
lg = linLegBase;
//lg = linLegBase + it->Class->getNumParallel(linLegBase) - 1;
classId2Linear[clsId] = lg;
}
else {
lg = res->second;
}
}
const int lastIndex = it->Class ? it->Class->getLastStageIndex() : 0;
lg = min<unsigned>(lg, lastIndex);
if (lg >= leg)
hasRunner = true;
if (legResult) {
pRunner r = it->getRunner(lg);
if (r) {
it->_sortTime = r->getRunningTime();
it->_cachedStatus = r->getStatus();
}
else {
it->_sortTime = 0;
it->_cachedStatus = StatusUnknown;
}
}
else {
it->_sortTime = it->getLegRunningTime(lg, totalResult) + it->getNumShortening(lg) * 3600 * 24 * 10;
it->_cachedStatus = it->getLegStatus(lg, totalResult);
// Ensure number of restarts has effect on final result
if (lg == lastIndex)
it->_sortTime += it->tNumRestarts * 24 * 3600;
}
unsigned rawStatus = it->_cachedStatus;
it->_sortStatus = RunnerStatusOrderMap[rawStatus < 100u ? rawStatus : 0];
}
if (!hasRunner)
return false;
sort(teams.begin(), teams.end(), [](const oTeam * &a, const oTeam * &b)->bool {return oTeam::compareResult(*a, *b); });
}
else if (so == ClassStartTime || so == ClassStartTimeClub) {
if (leg == -1)
leg = 0;
for (auto it : teams) {
it->_cachedStatus = StatusUnknown;
it->_sortStatus = 0;
it->_sortTime = it->getLegStartTime(leg);
if (it->_sortTime <= 0)
it->_sortStatus = 1;
}
if (so == ClassStartTime)
sort(teams.begin(), teams.end(), [](const oTeam * &a, const oTeam * &b)->bool {return oTeam::compareResult(*a, *b); });
else
sort(teams.begin(), teams.end(), [](const oTeam * &a, const oTeam * &b)->bool {return oTeam::compareResultNoSno(*a, *b); });
}
else if (so == ClassKnockoutTotalResult) {
for (auto it : teams) {
it->_cachedStatus = StatusUnknown;
it->_sortStatus = 0;
it->_sortTime = 0;
// Count number of races with results
int numResult = 0;
int lastClassHeat = 0;
for (pRunner r : it->Runners) {
if (r && (r->prelStatusOK() ||
(r->tStatus != StatusUnknown && r->tStatus != StatusDNS && r->tStatus != StatusCANCEL))) {
if (r->Class && r->tLeg > 0 && r->Class->isQualificationFinalBaseClass() && r->getClassRef(true) == r->Class)
continue; // Skip if not qualified.
numResult++;
lastClassHeat = r->getDCI().getInt("Heat");
it->_cachedStatus = r->tStatus;
it->_sortTime = r->getRunningTime();
}
}
if (lastClassHeat > 50 || lastClassHeat < 0)
lastClassHeat = 0;
unsigned rawStatus = it->_cachedStatus;
it->_sortStatus = RunnerStatusOrderMap[rawStatus < 100u ? rawStatus : 0] - (numResult * 100 + lastClassHeat) * 1000;
}
sort(teams.begin(), teams.end(), [](const oTeam * &a, const oTeam * &b)->bool {return oTeam::compareResult(*a, *b); });
}
return true;
}
wstring oEvent::getZeroTime() const
{
return getAbsTime(0);
@ -2896,6 +3013,8 @@ void oEvent::generateInForestList(gdioutput &gdi, GUICALLBACK cb, GUICALLBACK cb
typedef multimap<int, pFreePunch>::const_iterator TPunchIter;
for (oFreePunchList::iterator it = punches.begin(); it != punches.end(); ++it) {
if (it->isRemoved() || it->isHiredCard())
continue;
punchHash.insert(make_pair(it->getCardNo(), &*it));
}
@ -3087,7 +3206,7 @@ void oEvent::generateMinuteStartlist(gdioutput &gdi) {
for (size_t k=0;k<blocks.size();k++) {
gdi.dropLine();
if (k>0)
gdi.addStringUT(gdi.getCY()-1, 0, pageNewPage, "");
gdi.addStringUT(gdi.getCY()-1, 0, pageNewChapter, "");
gdi.addStringUT(boldLarge|Capitalize, lang.tl(L"Minutstartlista", true) + makeDash(L" - ") + getName());
if (!starts[k].empty()) {
@ -3103,9 +3222,9 @@ void oEvent::generateMinuteStartlist(gdioutput &gdi) {
sb.reserve(Runners.size());
int LastStartTime=-1;
for (oRunnerList::iterator it=Runners.begin(); it != Runners.end(); ++it) {
if (it->Class && it->Class->getBlock()!=blocks[k])
if (it->Class && it->Class->getBlock() != blocks[k])
continue;
if (it->Class && it->Class->getStart() != starts[k] )
if (it->Class && it->Class->getStart() != starts[k])
continue;
if (!it->Class && blocks[k]!=0)
continue;
@ -3626,6 +3745,7 @@ void oEvent::clear()
punchIndex.clear();
punches.clear();
cachedFirstStart.clear();
hiredCardHash.clear();
updateFreeId();
@ -3778,7 +3898,7 @@ void oEvent::reEvaluateAll(const set<int> &cls, bool doSync)
if (cls.empty() || cls.count(it->Id)) {
it->clearSplitAnalysis();
it->resetLeaderTime();
it->reinitialize();
it->reinitialize(true);
}
}
@ -3877,7 +3997,7 @@ void oEvent::reEvaluateChanged()
if (it->wasSQLChanged(-1, oPunch::PunchFinish)) {
it->clearSplitAnalysis();
it->resetLeaderTime();
it->reinitialize();
it->reinitialize(true);
resetClasses[it->getId()] = it->hasClassGlobalDependence();
}
}
@ -4281,11 +4401,13 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) {
if (cls == 0)
throw meosException("Class not found");
if (cls->getParentClass())
if (cls->getParentClass()) {
cls->getParentClass()->setBibMode(BibFree);
cls->getParentClass()->synchronize(true);
}
if (!firstNumber.empty()) {
cls->setBibMode(BibFree);
cls->synchronize(true);
wchar_t pattern[32];
int num = oClass::extractBibPattern(firstNumber, pattern);
@ -4536,6 +4658,8 @@ void oEvent::addAutoBib() {
// Switch to free mode if bib set for subclass
cls->getParentClass()->setBibMode(BibFree);
cls->setBibMode(BibFree);
cls->getParentClass()->synchronize(true);
cls->synchronize(true);
}
for (size_t k = 0; k < rl.size(); k++) {
if (pattern[0]) {
@ -4553,27 +4677,40 @@ void oEvent::addAutoBib() {
}
}
void oEvent::checkOrderIdMultipleCourses(int ClassId)
{
void oEvent::checkOrderIdMultipleCourses(int ClassId) {
sortRunners(ClassStartTime);
int order=1;
oRunnerList::iterator it;
//Find first free order
for(it=Runners.begin(); it != Runners.end(); ++it){
if (ClassId==0 || it->getClassId(false)==ClassId){
for (it = Runners.begin(); it != Runners.end(); ++it) {
if (it->isRemoved())
continue;
if (ClassId == 0 || it->getClassId(false) == ClassId) {
it->synchronize();//Ensure we are up-to-date
order=max(order, it->StartNo);
order = max(order, it->StartNo);
}
}
//Assign orders
for(it=Runners.begin(); it != Runners.end(); ++it){
if (ClassId==0 || it->getClassId(false)==ClassId)
if (it->StartNo==0){
it->StartNo=++order;
it->updateChanged(); //Mark as changed.
it->synchronize(); //Sync!
for (it = Runners.begin(); it != Runners.end(); ++it) {
if (it->isRemoved())
continue;
if (it->getClassRef(true) && it->getClassRef(true)->lockedForking())
continue;
if (ClassId == 0 || it->getClassId(false) == ClassId)
if (it->StartNo == 0) {
if (it->getTeam()) {
if (it->getTeam()->getStartNo() == 0) {
it->updateStartNo(++order);
}
else {
it->setStartNo(it->getTeam()->getStartNo(), false);
}
}
else {
it->updateStartNo(++order);
}
}
}
}
@ -4817,7 +4954,7 @@ void oEvent::calcUseStartSeconds()
}
}
const wstring &oEvent::formatStatus(RunnerStatus status)
const wstring &oEvent::formatStatus(RunnerStatus status, bool forPrint)
{
const static wstring stats[9]={L"?", L"Godkänd", L"Ej start", L"Felst.", L"Utg.", L"Disk.",
L"Maxtid", L"Deltar ej", L"Återbud[status]"};
@ -4838,6 +4975,12 @@ const wstring &oEvent::formatStatus(RunnerStatus status)
return lang.tl(stats[6]);
case StatusNotCompetiting:
return lang.tl(stats[7]);
case StatusUnknown: {
if (forPrint)
return formatTime(-1);
else
return stats[0];
}
default:
return stats[0];
}
@ -5081,8 +5224,8 @@ pClass oEvent::generateTestClass(int nlegs, int nrunners,
setupRelay(*cls, PPatrol, 2, start);
int nCtrl=rand()%15+10;
pCourse pc=generateTestCourse(nCtrl);
cls->addStageCourse(0, pc->getId());
cls->addStageCourse(1, pc->getId());
cls->addStageCourse(0, pc->getId(), -1);
cls->addStageCourse(1, pc->getId(), -1);
}
else if (nlegs>1 && nrunners==2) {
setupRelay(*cls, PTwinRelay, nlegs, start);
@ -5093,7 +5236,7 @@ pClass oEvent::generateTestClass(int nlegs, int nrunners,
for (int k=0;k<nlegs;k++)
for (int j=0;j<nlegs;j++)
cls->addStageCourse(k, cid[(k+j)%nlegs]);
cls->addStageCourse(k, cid[(k+j)%nlegs], -1);
}
else if (nlegs>1 && nrunners==nlegs) {
setupRelay(*cls, PRelay, nlegs, start);
@ -5104,12 +5247,12 @@ pClass oEvent::generateTestClass(int nlegs, int nrunners,
for (int k=0;k<nlegs;k++)
for (int j=0;j<nlegs;j++)
cls->addStageCourse(k, cid[(k+j)%nlegs]);
cls->addStageCourse(k, cid[(k+j)%nlegs], -1);
}
else if (nlegs>1 && nrunners==1) {
setupRelay(*cls, PHunting, 2, start);
cls->addStageCourse(0, generateTestCourse(rand()%8+10)->getId());
cls->addStageCourse(1, generateTestCourse(rand()%8+10)->getId());
cls->addStageCourse(0, generateTestCourse(rand()%8+10)->getId(), -1);
cls->addStageCourse(1, generateTestCourse(rand()%8+10)->getId(), -1);
}
return cls;
}
@ -5256,7 +5399,7 @@ void oEvent::getFreeImporter(oFreeImport &fi)
}
void oEvent::fillFees(gdioutput &gdi, const string &name, bool withAuto) const {
void oEvent::fillFees(gdioutput &gdi, const string &name, bool onlyDirect, bool withAuto) const {
gdi.clearList(name);
set<int> fees;
@ -5265,6 +5408,8 @@ void oEvent::fillFees(gdioutput &gdi, const string &name, bool withAuto) const {
for (oClassList::const_iterator it = Classes.begin(); it != Classes.end(); ++it) {
if (it->isRemoved())
continue;
if (onlyDirect && !it->getAllowQuickEntry())
continue;
f = it->getDCI().getInt("ClassFee");
if (f > 0)
@ -5285,18 +5430,21 @@ void oEvent::fillFees(gdioutput &gdi, const string &name, bool withAuto) const {
}
}
f = getDCI().getInt("EliteFee");
if (f > 0)
fees.insert(f);
if (fees.empty()) {
if (!onlyDirect) {
f = getDCI().getInt("EliteFee");
if (f > 0)
fees.insert(f);
}
f = getDCI().getInt("EntryFee");
if (f > 0)
fees.insert(f);
f = getDCI().getInt("YouthFee");
if (f > 0)
fees.insert(f);
f = getDCI().getInt("EntryFee");
if (f > 0)
fees.insert(f);
f = getDCI().getInt("YouthFee");
if (f > 0)
fees.insert(f);
}
vector< pair<wstring, size_t> > ff;
if (withAuto)
ff.push_back(make_pair(lang.tl(L"Från klassen"), -1));

View File

@ -351,7 +351,7 @@ protected:
void updateFreeId();
void updateFreeId(oBase *ob);
SortOrder CurrentSortOrder;
mutable SortOrder CurrentSortOrder;
list<CompetitionInfo> cinfo;
list<BackupInfo> backupInfo;
@ -433,7 +433,7 @@ protected:
void exportTeamSplits(xmlparser &xml, const set<int> &classes, bool oldStylePatrol);
/** Set up transient data in classes */
void reinitializeClasses();
void reinitializeClasses() const;
/** Analyze the result status of each class*/
void analyzeClassResultStatus() const;
@ -656,7 +656,7 @@ public:
pTeam findTeam(const wstring &s, int lastId, unordered_set<int> &filter) const;
pRunner findRunner(const wstring &s, int lastId, const unordered_set<int> &inputFilter, unordered_set<int> &filter) const;
static const wstring &formatStatus(RunnerStatus status);
static const wstring &formatStatus(RunnerStatus status, bool forPrint);
inline bool useStartSeconds() const {return tUseStartSeconds;}
void calcUseStartSeconds();
@ -808,7 +808,14 @@ public:
void getResultEvents(const set<int> &classFilter, const set<int> &controlFilter, vector<ResultEvent> &results) const;
/** Compute results for split times while runners are on course.*/
void computePreliminarySplitResults(const set<int> &classes);
void computePreliminarySplitResults(const set<int> &classes) const;
/** Synchronizes to server and checks if there are hired card data*/
bool hasHiredCardData();
bool isHiredCard(int cardNo) const;
void setHiredCard(int cardNo, bool flag);
vector<int> getHiredCards() const;
void clearHiredCards();
protected:
// Returns hash key for punch based on control id, and leg. Class is marked as changed if oldHashKey != newHashKey.
@ -821,6 +828,9 @@ protected:
mutable shared_ptr<unordered_multimap<int, pRunner>> cardToRunnerHash;
unordered_multimap<int, pRunner> &getCardToRunner() const;
mutable set<int> hiredCardHash;
mutable int tHiredCardHashDataRevision = -1;
int tClubDataRevision;
int tCalcNumMapsDataRevision = -1;
@ -872,7 +882,7 @@ public:
void removeFreePunch(int id);
pFreePunch getPunch(int id) const;
pFreePunch getPunch(int runnerId, int courseControlId, int card) const;
void getPunchesForRunner(int runnerId, vector<pFreePunch> &punches) const;
void getPunchesForRunner(int runnerId, bool sort, vector<pFreePunch> &punches) const;
//Returns true if data is changed.
bool autoSynchronizeLists(bool syncPunches);
@ -1011,8 +1021,8 @@ public:
enum class ResultType {ClassResult, TotalResult, CourseResult,
ClassCourseResult, PreliminarySplitResults};
void calculateResults(const set<int> &classes, ResultType result, bool includePreliminary = false);
void calculateRogainingResults(const set<int> &classSelection);
void calculateResults(const set<int> &classes, ResultType result, bool includePreliminary = false) const;
void calculateRogainingResults(const set<int> &classSelection) const;
void calculateResults(list<oSpeakerObject> &rl);
void calculateTeamResults(bool totalMultiday);
@ -1021,9 +1031,15 @@ public:
void calculateTeamResultAtControl(const set<int> &classId, int leg, int controlId, bool totalResults);
bool sortRunners(SortOrder so);
bool sortRunners(SortOrder so, vector<const oRunner *> &runners) const;
/** If linear leg is true, leg is interpreted as actual leg numer, otherwise w.r.t to parallel legs. */
bool sortTeams(SortOrder so, int leg, bool linearLeg);
bool sortTeams(SortOrder so, int leg, bool linearLeg, vector<const oTeam *> &teams) const;
pCard allocateCard(pRunner owner);
/** Optimize the start order based on drawInfo. Result in cInfo */
@ -1153,7 +1169,7 @@ public:
void updateClubsFromDB();
void updateRunnersFromDB();
void fillFees(gdioutput &gdi, const string &name, bool withAuto) const;
void fillFees(gdioutput &gdi, const string &name, bool onlyDirect, bool withAuto) const;
wstring getAutoClassName() const;
pClass addClass(const wstring &pname, int CourseId = 0, int classId = 0);
pClass addClass(oClass &c);
@ -1214,6 +1230,7 @@ public:
void calculateNumRemainingMaps(bool forceRecalculate);
pControl getControl(int Id) const;
pControl getControlByType(int type) const;
pControl getControl(int Id, bool create);
enum ControlType {CTAll, CTRealControl, CTCourseControl};

View File

@ -938,12 +938,13 @@ void oEvent::drawList(const vector<ClassDrawSpecification> &spec,
throw std::exception("Det går endast att sätta in vakanser på sträcka 1.");
if (size_t(spec[k].leg) < pc->legInfo.size()) {
pc->legInfo[spec[k].leg].startMethod = STDrawn; //Automatically change start method
pc->setStartType(spec[k].leg, STDrawn, true); //Automatically change start method
}
else if (spec[k].leg == -1) {
for (size_t j = 0; j < pc->legInfo.size(); j++)
pc->legInfo[j].startMethod = STDrawn; //Automatically change start method
pc->setStartType(j, STDrawn, true); //Automatically change start method
}
pc->synchronize(true);
clsId2Ix[spec[k].classID] = k;
if (!multiDay && spec[k].leg == 0 && pc->getParentClass() == 0)
clsIdClearVac.insert(spec[k].classID);
@ -1080,20 +1081,26 @@ void oEvent::drawList(const vector<ClassDrawSpecification> &spec,
}
int minStartNo = Runners.size();
vector<pair<int, int>> newStartNo;
for(unsigned k=0;k<stimes.size(); k++) {
runners[k]->setStartTime(stimes[k], true, false, false);
runners[k]->synchronize();
minStartNo = min(minStartNo, runners[k]->getStartNo());
newStartNo.emplace_back(stimes[k], k);
}
CurrentSortOrder = SortByStartTime;
sort(runners.begin(), runners.end());
sort(newStartNo.begin(), newStartNo.end());
//CurrentSortOrder = SortByStartTime;
//sort(runners.begin(), runners.end());
if (minStartNo == 0)
minStartNo = nextFreeStartNo + 1;
for(size_t k=0; k<runners.size(); k++) {
runners[k]->setStartNo(k+minStartNo, false);
runners[k]->synchronize();
pClass pCls = runners[k]->getClassRef(true);
if (pCls && pCls->lockedForking() || runners[k]->getLegNumber() > 0)
continue;
runners[k]->updateStartNo(newStartNo[k].second + minStartNo);
}
nextFreeStartNo = max<int>(nextFreeStartNo, minStartNo + stimes.size());

View File

@ -41,8 +41,7 @@
#include "listeditor.h"
void oEvent::calculateSplitResults(int controlIdFrom, int controlIdTo)
{
void oEvent::calculateSplitResults(int controlIdFrom, int controlIdTo) {
oRunnerList::iterator it;
for (it=Runners.begin(); it!=Runners.end(); ++it) {
@ -98,14 +97,14 @@ void oEvent::calculateSplitResults(int controlIdFrom, int controlIdTo)
cTime=it->tempRT;
it->tPlace=vPlace;
it->tPlace.update(*this, vPlace); // XXX User other result container
}
else
it->tPlace=99000+it->tStatus;
it->tPlace.update(*this, 99000+it->tStatus);
}
}
void oEvent::calculateResults(const set<int> &classes, ResultType resultType, bool includePreliminary) {
void oEvent::calculateResults(const set<int> &classes, ResultType resultType, bool includePreliminary) const {
if (resultType == ResultType::PreliminarySplitResults) {
computePreliminarySplitResults(classes);
return;
@ -114,14 +113,26 @@ void oEvent::calculateResults(const set<int> &classes, ResultType resultType, bo
const bool courseResults = resultType == ResultType::CourseResult;
const bool classCourseResults = resultType == ResultType::ClassCourseResult;
bool all = classes.empty();
vector<const oRunner *> runners;
runners.reserve(Runners.size());
for (auto &r : Runners) {
if (r.isRemoved())
continue;
if (!all && !classes.count(r.getClassId(true)))
continue;
runners.push_back(&r);
}
if (classCourseResults)
sortRunners(ClassCourseResult);
sortRunners(ClassCourseResult, runners);
else if (courseResults)
sortRunners(CourseResult);
sortRunners(CourseResult, runners);
else if (!totalResults)
sortRunners(ClassResult);
sortRunners(ClassResult, runners);
else
sortRunners(ClassTotalResult);
sortRunners(ClassTotalResult, runners);
oRunnerList::iterator it;
@ -133,9 +144,8 @@ void oEvent::calculateResults(const set<int> &classes, ResultType resultType, bo
int cLegEquClass = 0;
bool invalidClass = false;
bool useResults = false;
for (it=Runners.begin(); it != Runners.end(); ++it) {
if (it->isRemoved())
continue;
for (auto it : runners) {
// Start new "class"
if (classCourseResults) {
const pCourse crs = it->getCourse(false);
@ -175,7 +185,7 @@ void oEvent::calculateResults(const set<int> &classes, ResultType resultType, bo
// Calculate results
if (invalidClass) {
it->tTotalPlace = 0;
it->tPlace = 0;
it->tPlace.update(*this, 0);
}
else if (!totalResults) {
int tPlace = 0;
@ -197,7 +207,7 @@ void oEvent::calculateResults(const set<int> &classes, ResultType resultType, bo
tPlace = 99000 + it->tStatus;
if (!classCourseResults)
it->tPlace = tPlace;
it->tPlace.update(*this, tPlace);
else
it->tCoursePlace = tPlace;
}
@ -224,10 +234,22 @@ void oEvent::calculateResults(const set<int> &classes, ResultType resultType, bo
}
}
void oEvent::calculateRogainingResults(const set<int> &classSelection) {
void oEvent::calculateRogainingResults(const set<int> &classSelection) const {
const bool all = classSelection.empty();
sortRunners(ClassPoints);
oRunnerList::iterator it;
vector<const oRunner *> runners;
runners.reserve(Runners.size());
for (auto &r : Runners) {
if (r.isRemoved())
continue;
if (!all && !classSelection.count(r.getClassId(true)))
continue;
if (r.Class && r.Class->isRogaining()) {
runners.push_back(&r);
}
}
sortRunners(ClassPoints, runners);
int cClassId=-1;
int cPlace = 0;
@ -238,13 +260,7 @@ void oEvent::calculateRogainingResults(const set<int> &classSelection) {
bool isRogaining = false;
bool invalidClass = false;
for (it=Runners.begin(); it != Runners.end(); ++it) {
if (it->isRemoved())
continue;
if (!all && !classSelection.count(it->getClassId(false)))
continue;
for (auto it : runners) {
if (it->getClassId(true)!=cClassId || it->tDuplicateLeg!=cDuplicateLeg) {
cClassId = it->getClassId(true);
useResults = it->Class ? !it->Class->getNoTiming() : false;
@ -252,16 +268,12 @@ void oEvent::calculateRogainingResults(const set<int> &classSelection) {
vPlace = 0;
cTime = numeric_limits<int>::min();
cDuplicateLeg = it->tDuplicateLeg;
isRogaining = it->Class ? it->Class->isRogaining() : false;
invalidClass = it->Class ? it->Class->getClassStatus() != oClass::Normal : false;
}
if (!isRogaining)
continue;
if (invalidClass) {
it->tTotalPlace = 0;
it->tPlace = 0;
it->tPlace.update(*this, 0);
}
else if (it->tStatus==StatusOK) {
cPlace++;
@ -274,12 +286,12 @@ void oEvent::calculateRogainingResults(const set<int> &classSelection) {
cTime = cmpRes;
if (useResults)
it->tPlace = vPlace;
it->tPlace.update(*this, vPlace);
else
it->tPlace = 0;
it->tPlace.update(*this, 0);
}
else
it->tPlace = 99000 + it->tStatus;
it->tPlace.update(*this, 99000 + it->tStatus);
}
}
@ -603,9 +615,9 @@ void oEvent::calculateTeamResultAtControl(const set<int> &classId, int leg, int
}
}
void oEvent::computePreliminarySplitResults(const set<int> &classes) {
void oEvent::computePreliminarySplitResults(const set<int> &classes) const {
bool allClasses = classes.empty();
map<pair<int, int>, vector<pRunner>> runnerByClassLeg;
map<pair<int, int>, vector<const oRunner *>> runnerByClassLeg;
for (auto &r : Runners) {
r.tOnCourseResults.clear();
r.currentControlTime.first = 1;
@ -637,11 +649,14 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) {
map<pair<int, int>, set<int>> classLeg2ExistingCCId;
for (auto &p : punches) {
if (p.isRemoved())
if (p.isRemoved() || p.isHiredCard())
continue;
pRunner r = p.getTiedRunner();
if (!r)
continue;
if (!p.isCheck() && r->getCourse(false) == nullptr)
r->tOnCourseResults.hasAnyRes = true; // Register all punches for runners without course
pClass cls = r->getClassRef(false);
if (r->getCourse(false) && cls) {
int ccId = p.getCourseControlId();
@ -669,7 +684,7 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) {
const set<int> &expectedCCid = classLeg2ExistingCCId[make_pair(clsId, leg)];
size_t nRT = 0;
for (auto &radioTimes : r.tOnCourseResults) {
for (auto &radioTimes : r.tOnCourseResults.res) {
if (expectedCCid.count(radioTimes.courseControlId))
nRT++;
}
@ -681,7 +696,7 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) {
int ccId = crs->getCourseControlId(p.tIndex);
if (expectedCCid.count(ccId)) {
bool added = false;
for (auto &stored : r.tOnCourseResults) {
for (auto &stored : r.tOnCourseResults.res) {
if (stored.courseControlId == ccId) {
added = true;
break;
@ -714,7 +729,7 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) {
if (ccId == oPunch::PunchFinish) {
negLeg = -1000; //Finish, smallest number
for (int j = 0; j < nRun; j++) {
pRunner r = rr[j];
auto r = rr[j];
if (r->prelStatusOK()) {
int time;
if (!r->tInTeam || !totRes)
@ -723,15 +738,15 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) {
time = r->tInTeam->getLegRunningTime(r->tLeg, false);
}
int ix = -1;
int nr = r->tOnCourseResults.size();
int nr = r->tOnCourseResults.res.size();
for (int i = 0; i < nr; i++) {
if (r->tOnCourseResults[i].courseControlId == ccId) {
if (r->tOnCourseResults.res[i].courseControlId == ccId) {
ix = i;
break;
}
}
if (ix == -1) {
ix = r->tOnCourseResults.size();
ix = r->tOnCourseResults.res.size();
int nc = 0;
pCourse crs = r->getCourse(false);
if (crs)
@ -744,12 +759,12 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) {
}
else {
for (int j = 0; j < nRun; j++) {
pRunner r = rr[j];
int nr = r->tOnCourseResults.size();
auto r = rr[j];
int nr = r->tOnCourseResults.res.size();
for (int i = 0; i < nr; i++) {
if (r->tOnCourseResults[i].courseControlId == ccId) {
timeRunnerIx.emplace_back(r->tOnCourseResults[i].time, j, i);
negLeg = min(negLeg, -r->tOnCourseResults[i].controlIx);
if (r->tOnCourseResults.res[i].courseControlId == ccId) {
timeRunnerIx.emplace_back(r->tOnCourseResults.res[i].time, j, i);
negLeg = min(negLeg, -r->tOnCourseResults.res[i].controlIx);
break;
}
}
@ -769,10 +784,10 @@ void oEvent::computePreliminarySplitResults(const set<int> &classes) {
if (leadTime == 0)
leadTime = time;
}
pRunner r = rr[get<1>(timeRunnerIx[i])];
auto r = rr[get<1>(timeRunnerIx[i])];
int locIx = get<2>(timeRunnerIx[i]);
r->tOnCourseResults[locIx].place = place;
r->tOnCourseResults[locIx].after = time - leadTime;
r->tOnCourseResults.res[locIx].place = place;
r->tOnCourseResults.res[locIx].after = time - leadTime;
int &legWithTimeIndexNeg = r->currentControlTime.first;
if (negLeg < legWithTimeIndexNeg) {

View File

@ -344,7 +344,7 @@ void renderRowSpeakerList(const oSpeakerObject &r, const oSpeakerObject *next_r,
if (r.finishStatus<=1 || r.finishStatus==r.status)
row.push_back(SpeakerString(normalText, names));
else
row.push_back(SpeakerString(normalText, names + L" ("+ oEvent::formatStatus(r.finishStatus) +L")"));
row.push_back(SpeakerString(normalText, names + L" ("+ oEvent::formatStatus(r.finishStatus, true) +L")"));
row.push_back(SpeakerString(normalText, r.club));
@ -410,7 +410,7 @@ void renderRowSpeakerList(const oSpeakerObject &r, const oSpeakerObject *next_r,
else{
//gdi.addStringUT(y, x+dx[4], textRight, oEvent::formatStatus(r.status)).setColor(colorDarkRed);
row.push_back(SpeakerString());
row.push_back(SpeakerString(textRight, oEvent::formatStatus(r.status)));
row.push_back(SpeakerString(textRight, oEvent::formatStatus(r.status, true)));
row.back().color = colorDarkRed;
row.push_back(SpeakerString());
}
@ -1742,7 +1742,7 @@ void oEvent::getResultEvents(const set<int> &classFilter, const set<int> &punchF
for (oFreePunchList::const_iterator it = punches.begin(); it != punches.end(); ++it) {
const oFreePunch &fp = *it;
if (fp.isRemoved() || fp.tRunnerId == 0 || fp.Type == oPunch::PunchCheck || fp.Type == oPunch::PunchStart)
if (fp.isRemoved() || fp.tRunnerId == 0 || fp.Type == oPunch::PunchCheck || fp.Type == oPunch::PunchStart || fp.Type == oPunch::HiredCard)
continue;
pRunner r = getRunner(fp.tRunnerId, 0);

View File

@ -178,7 +178,7 @@ void oEvent::generatePunchTableData(Table &table, oFreePunch *addPunch)
oFreePunchList::iterator it;
table.reserve(punches.size());
for (it = punches.begin(); it != punches.end(); ++it){
if (!it->isRemoved()){
if (!it->isRemoved() && !it->isHiredCard()){
it->addTableRow(table);
}
}
@ -292,7 +292,7 @@ void oFreePunch::rehashPunches(oEvent &oe, int cardNo, pFreePunch newPunch) {
// Rehash all punches. Ignore cardNo and newPunch (will be included automatically)
fp.reserve(oe.punches.size());
for (oFreePunchList::iterator pit = oe.punches.begin(); pit != oe.punches.end(); ++pit) {
if (pit->isRemoved())
if (pit->isRemoved() || pit->isHiredCard())
continue;
fp.push_back(&(*pit));
}
@ -335,7 +335,7 @@ void oFreePunch::rehashPunches(oEvent &oe, int cardNo, pFreePunch newPunch) {
it->second.erase(res.first, res.second);
}
if (newPunch)
if (newPunch && !newPunch->isHiredCard())
fp.push_back(newPunch);
sort(fp.begin(), fp.end(), FreePunchComp());
@ -614,12 +614,12 @@ pFreePunch oEvent::getPunch(int runnerId, int courseControlId, int card) const
return 0;
}
void oEvent::getPunchesForRunner(int runnerId, vector<pFreePunch> &runnerPunches) const {
void oEvent::getPunchesForRunner(int runnerId, bool doSort, vector<pFreePunch> &runnerPunches) const {
runnerPunches.clear();
pRunner r = getRunner(runnerId, 0);
if (r == 0)
return;
/*
// Get times for when other runners used this card
vector< pair<int, int> > times;
int refCno = r->getCardNo();
@ -641,6 +641,9 @@ void oEvent::getPunchesForRunner(int runnerId, vector<pFreePunch> &runnerPunches
for (oFreePunchList::const_iterator it = punches.begin(); it != punches.end(); ++it) {
if (it->CardNo == refCno) {
if (it->isRemoved() || it->isHiredCard())
continue;
bool other = false;
int t = it->Time;
for (size_t k = 0; k<times.size(); k++) {
@ -652,8 +655,33 @@ void oEvent::getPunchesForRunner(int runnerId, vector<pFreePunch> &runnerPunches
runnerPunches.push_back(pFreePunch(&*it));
}
}
*/
// XXX Advance punches...
//Lazy setup
oFreePunch::rehashPunches(*oe, 0, 0);
int card = r->getCardNo();
if (card == 0)
return;
for (auto &it1 :punchIndex) {
const oEvent::PunchIndexType &cIndex = it1.second;
pair<oEvent::PunchConstIterator, oEvent::PunchConstIterator> res = cIndex.equal_range(card);
oEvent::PunchConstIterator pIter = res.first;
while (pIter != res.second) {
pFreePunch punch = pIter->second;
if (!punch->isRemoved()) {
assert(punch && punch->CardNo == card);
if (punch->tRunnerId == runnerId || runnerId == 0)
runnerPunches.push_back(punch);
++pIter;
}
}
}
if (doSort) {
sort(runnerPunches.begin(), runnerPunches.end(), [](const oPunch *p1, const oPunch *p2)->bool {return p1->Time < p2->Time; });
}
}
@ -732,3 +760,75 @@ void oFreePunch::changedObject() {
if (r && tMatchControlId>0)
r->markClassChanged(tMatchControlId);
}
bool oEvent::hasHiredCardData() {
synchronizeList(oListId::oLPunchId);
isHiredCard(0);
return !hiredCardHash.empty();
}
bool oEvent::isHiredCard(int cardNo) const {
if (tHiredCardHashDataRevision != dataRevision) {
hiredCardHash.clear();
for (auto &p : punches) {
if (!p.isRemoved() && p.isHiredCard())
hiredCardHash.insert(p.getCardNo());
}
tHiredCardHashDataRevision = dataRevision;
}
return hiredCardHash.count(cardNo) > 0;
}
void oEvent::setHiredCard(int cardNo, bool flag) {
if (cardNo <= 0)
return;
if (isHiredCard(cardNo) != flag) {
if (flag) {
addFreePunch(0, oPunch::HiredCard, cardNo, false);
hiredCardHash.insert(cardNo);
tHiredCardHashDataRevision = dataRevision;
}
else {
hiredCardHash.erase(cardNo);
for (auto it = punches.begin(); it != punches.end();) {
if (!it->isRemoved() && it->isHiredCard() && it->CardNo == cardNo) {
if (HasDBConnection)
msRemove(&*it);
auto toErase = it;
++it;
punches.erase(toErase);
}
else {
++it;
}
}
tHiredCardHashDataRevision = dataRevision;
}
}
}
vector<int> oEvent::getHiredCards() const {
isHiredCard(0); // Update hash
vector<int> r(hiredCardHash.begin(), hiredCardHash.end());
return r;
}
void oEvent::clearHiredCards() {
vector<int> toRemove;
for (auto it = punches.begin(); it != punches.end();) {
if (!it->isRemoved() && it->isHiredCard()) {
if (HasDBConnection)
msRemove(&*it);
auto toErase = it;
++it;
punches.erase(toErase);
}
else {
++it;
}
}
hiredCardHash.clear();
}

View File

@ -118,7 +118,7 @@ bool oEvent::exportOECSV(const wchar_t *file, int languageTypeIndex, bool includ
return false;
calculateResults({}, ResultType::ClassResult);
sortRunners(SortOrder::ClassResult);
oRunnerList::iterator it;
string maleString;
string femaleString;
@ -269,7 +269,7 @@ bool oEvent::exportOECSV(const wchar_t *file, int languageTypeIndex, bool includ
// Extra punches
vector<pFreePunch> punches;
oe->getPunchesForRunner(it->getId(), punches);
oe->getPunchesForRunner(it->getId(), true, punches);
for (vector<pFreePunch>::iterator punchIt = punches.begin(); punchIt != punches.end(); ++punchIt) {
pPunch punch = *punchIt;
if (!punch->isUsed && !(punch->isFinish() && !pc->useLastAsFinish()) && !(punch->isStart() && !pc->useFirstAsStart()) && !punch->isCheck())
@ -321,6 +321,10 @@ void oEvent::importXML_EntryData(gdioutput &gdi, const wstring &file,
IOF30Interface reader(this, false);
reader.setPreferredIdType(preferredIdType);
reader.readEntryList(gdi, xo, removeNonexisting, filter, ent, fail, removed);
for (auto &c : Clubs) {
c.updateFromDB();
}
}
else {
xmlList xl;
@ -604,6 +608,24 @@ void oEvent::importXML_EntryData(gdioutput &gdi, const wstring &file,
}
}
xo = xml.getObject("ServiceRequestList");
if (xo) {
gdi.addString("", 0, "Importerar tävlingsdata (IOF, xml)");
gdi.refreshFast();
if (xo.getAttrib("iofVersion")) {
IOF30Interface reader(this, false);
int imp = 0, fail = 0;
reader.readServiceRequestList(gdi, xo, imp, fail);
gdi.dropLine();
gdi.refreshFast();
}
}
vector<int> toRemove;
for (size_t k = 0; k < runnersInTeam.size(); k++) {
int id = runnersInTeam[k].first;
@ -1194,7 +1216,7 @@ void oEvent::importXML_IOF_Data(const wstring &clubfile,
xmlparser xml_club;
xml_club.setProgress(gdibase.getHWNDTarget());
if (clear)
if (clear && !competitorfile.empty())
runnerDB->clearClubs();
gdibase.addString("",0,"Läser klubbar...");
@ -1567,17 +1589,17 @@ bool oEvent::addXMLCourse(const xmlobject &xcrs, bool addClasses, set<wstring> &
}
else {
for (size_t i = 0; i<pCls->getNumStages(); i++)
pCls->addStageCourse(i, courses[0]->getId());
pCls->addStageCourse(i, courses[0]->getId(), -1);
}
}
else {
if (courses.size() == pCls->getNumStages()) {
for (size_t i = 0; i<courses.size(); i++)
pCls->addStageCourse(i, courses[i]->getId());
pCls->addStageCourse(i, courses[i]->getId(), -1);
}
else {
for (size_t i = 0; i<courses.size(); i++)
pCls->addStageCourse(0, courses[i]->getId());
pCls->addStageCourse(0, courses[i]->getId(), -1);
}
}
}
@ -2605,6 +2627,9 @@ void oEvent::exportIOFSplits(IOFVersion version, const wchar_t *file,
calculateResults(set<int>(), ResultType::ClassResult);
calculateTeamResults(true);
calculateTeamResults(false);
sortRunners(SortOrder::ClassResult);
sortTeams(SortOrder::ClassResult, -1, false);
set<int> rgClasses;
for (int clz : classes) {
pClass pc = getClass(clz);

View File

@ -645,7 +645,7 @@ const wstring &oEvent::formatPunchStringAux(const oPrintPost &pp, const oListPar
else {
pControl ctrl = getControl(punch->getControlId());
if (!ctrl)
ctrl = getControl(punch->Type);
ctrl = getControlByType(punch->Type);
if (ctrl && ctrl->hasName()) {
swprintf_s(bfw, L"%s", ctrl->getName().c_str());
@ -654,7 +654,7 @@ const wstring &oEvent::formatPunchStringAux(const oPrintPost &pp, const oListPar
}
break;
case lPunchTimeSinceLast:
if (punch && punch->previousPunchTime && r && r->Card && !invalidClass) {
if (punch && punch->previousPunchTime && r && !invalidClass) {
int time = punch->Time;
int pTime = punch->previousPunchTime;
if (pTime > 0 && time > pTime) {
@ -673,7 +673,7 @@ const wstring &oEvent::formatPunchStringAux(const oPrintPost &pp, const oListPar
case lPunchSplitTime:
case lPunchTotalTime:
case lPunchAbsTime:
if (punch && r && r->Card && !invalidClass) {
if (punch && r && !invalidClass) {
if (punch->tIndex >= 0) {
// Punch in course
counter.level3 = punch->tIndex;
@ -1074,6 +1074,23 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara
return formatSpecialStringAux(pp, par, t, 0, crs, 0, counter);
}
break;
case lClassAvailableMaps:
if (pc) {
int n = pc->getNumRemainingMaps(false);
if (n != numeric_limits<int>::min())
wsptr = &itow(n);
}
break;
case lClassTotalMaps:
if (pc) {
int n = pc->getNumberMaps();
if (n > 0)
wsptr = &itow(n);
}
break;
case lCourseClimb:
if (r) {
pCourse crs = r->getCourse(false);
@ -1119,7 +1136,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara
}
}
if (r->getStatus() == StatusCANCEL) {
wsptr = &oEvent::formatStatus(StatusCANCEL);
wsptr = &oEvent::formatStatus(StatusCANCEL, true);
}
else if (r->startTimeAvailable()) {
if (pp.type != lRunnerStartZero)
@ -1233,7 +1250,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara
}
else {
if (ok)
wsptr = &formatStatus(StatusOK);
wsptr = &formatStatus(StatusOK, true);
else
wsptr = &r->getStatusS(true);
}
@ -1269,7 +1286,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara
swap(vts, timeStatus);
}
else {
wstring vts = formatStatus(ts) + L" (" + timeStatus + L")";
wstring vts = formatStatus(ts, true) + L" (" + timeStatus + L")";
swap(vts, timeStatus);
}
}
@ -1400,7 +1417,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara
if (r->tempStatus==StatusOK && pc && !pc->getNoTiming())
wcscpy_s(wbf, formatTime(r->tempRT).c_str());
else
wcscpy_s(wbf, formatStatus(r->tempStatus).c_str() );
wcscpy_s(wbf, formatStatus(r->tempStatus, true).c_str() );
}
break;
case lRunnerPlace:
@ -1683,6 +1700,12 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara
wcscpy_s(wbf, s.c_str());
}
break;
case lRunnerExpectedFee:
if (r) {
wstring s = formatCurrency(r->getDefaultFee());
wcscpy_s(wbf, s.c_str());
}
break;
case lRunnerPaid:
if (r) {
wstring s = formatCurrency(r->getDCI().getInt("Paid"));
@ -2465,7 +2488,7 @@ void oEvent::listGeneratePunches(const oListInfo &listInfo, gdioutput &gdi,
ppi.counter.level3++;
}
}
else if(type == oListInfo::EBaseType::EBaseTypeAllPunches && r->Card) {
else if(type == oListInfo::EBaseType::EBaseTypeAllPunches) {
int startType = -1;
int finishType = -1;
const pCourse pcrs = r->getCourse(false);
@ -2475,7 +2498,20 @@ void oEvent::listGeneratePunches(const oListInfo &listInfo, gdioutput &gdi,
finishType = pcrs->getFinishPunchType();
}
int prevPunchTime = r->getStartTime();
for (auto &punch : r->Card->punches) {
vector<pPunch> punches;
if (r->Card) {
for (auto &punch : r->Card->punches)
punches.push_back(&punch);
}
else {
vector<pFreePunch> fPunches;
oe->getPunchesForRunner(r->getId(), true, fPunches);
for (auto punch : fPunches)
punches.push_back(punch);
}
for (auto &pPunch : punches) {
const oPunch &punch = *pPunch;
punch.previousPunchTime = prevPunchTime;
if (punch.isCheck() || punch.isStart(startType))
@ -2605,7 +2641,7 @@ bool oListInfo::filterRunner(const oRunner &r) const {
}
if (filter(EFilterAnyResult)) {
if (r.tOnCourseResults.empty())
if (!r.hasOnCourseResult())
return true;
}
@ -2614,6 +2650,11 @@ bool oListInfo::filterRunner(const oRunner &r) const {
return true;
}
if (filter(EFilterWrongFee)) {
if (r.getEntryFee() == r.getDefaultFee())
return true;
}
if (filter(EFilterRentCard) && r.getDCI().getInt("CardFee") == 0)
return true;
@ -2734,27 +2775,24 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form
}
calculateTeamResults(true);
sortRunners(li.sortOrder);
calculateResults(li.lp.selection, ResultType::TotalResult);
if (li.sortOrder != ClassTotalResult)
sortRunners(li.sortOrder);
}
else if (li.calcResults) {
if (li.rogainingResults) {
sortRunners(li.sortOrder);
calculateRogainingResults(li.lp.selection);
if (li.sortOrder != ClassPoints)
sortRunners(li.sortOrder);
}
else if (li.lp.useControlIdResultTo > 0 || li.lp.useControlIdResultFrom > 0)
calculateSplitResults(li.lp.useControlIdResultFrom, li.lp.useControlIdResultTo);
else if (li.sortOrder == CourseResult) {
sortRunners(li.sortOrder);
calculateResults(li.lp.selection, ResultType::CourseResult);
}
else {
calculateTeamResults(false);
sortRunners(li.sortOrder);
calculateResults(li.lp.selection, ResultType::ClassResult);
if (li.sortOrder != ClassResult)
sortRunners(li.sortOrder);
}
}
else
@ -3051,8 +3089,10 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form
if (li.calcResults) {
if (li.lp.useControlIdResultTo > 0 || li.lp.useControlIdResultFrom > 0)
calculateSplitResults(li.lp.useControlIdResultFrom, li.lp.useControlIdResultTo);
else
else {
sortRunners(li.sortOrder);
calculateResults(li.lp.selection, ResultType::ClassResult);
}
}
else sortRunners(li.sortOrder);

View File

@ -51,6 +51,8 @@ enum EPostType
lClassStartTimeRange,
lClassLength,
lClassResultFraction,
lClassAvailableMaps,
lClassTotalMaps,
lCourseLength,
lCourseName,
lCourseClimb,
@ -114,6 +116,7 @@ enum EPostType
lRunnerNationality,
lRunnerPhone,
lRunnerFee,
lRunnerExpectedFee,
lRunnerPaid,
lRunnerPayMethod,
lRunnerEntryDate,
@ -258,6 +261,7 @@ enum EFilterList
EFilterOnlyVacant,
EFilterAnyResult, // With any (radio) punch on a leg
EFilterAPIEntry, // Entry via API
EFilterWrongFee,
_EFilterMax
};

View File

@ -70,6 +70,7 @@ public:
wstring getInfo() const;
bool isHiredCard() const { return Type == HiredCard; }
bool isStart() const {return Type==PunchStart;}
bool isStart(int startType) const {return Type==PunchStart || Type == startType;}
bool isFinish() const {return Type==PunchFinish;}
@ -93,7 +94,7 @@ public:
wstring getRunningTime(int startTime) const;
enum SpecialPunch {PunchStart=1, PunchFinish=2, PunchCheck=3};
enum SpecialPunch {PunchStart=1, PunchFinish=2, PunchCheck=3, HiredCard=11111};
void decodeString(const string &s);
string codeString() const;
oPunch(oEvent *poe);

View File

@ -49,6 +49,16 @@
oRunner::RaceIdFormatter oRunner::raceIdFormatter;
bool oAbstractRunner::DynamicValue::isOld(const oEvent &oe) const {
return oe.dataRevision != dataRevision;
}
void oAbstractRunner::DynamicValue::update(const oEvent &oe, int v) {
value = v;
dataRevision = oe.dataRevision;
}
const wstring &oRunner::RaceIdFormatter::formatData(const oBase *ob) const {
return itow(dynamic_cast<const oRunner &>(*ob).getRaceIdentifier());
}
@ -317,6 +327,20 @@ int oRunner::getBirthAge() const {
return 0;
}
int oAbstractRunner::getDefaultFee() const {
int age = getBirthAge();
wstring date = getEntryDate();
if (Class) {
int fee = Class->getEntryFee(date, age);
return fee;
}
return 0;
}
int oAbstractRunner::getEntryFee() const {
return getDCI().getInt("Fee");
}
void oAbstractRunner::addClassDefaultFee(bool resetFees) {
if (Class) {
oDataInterface di = getDI();
@ -343,8 +367,7 @@ void oAbstractRunner::addClassDefaultFee(bool resetFees) {
}
if ((currentFee == 0 && !hasFlag(FlagFeeSpecified)) || resetFees) {
int age = getBirthAge();
int fee = Class->getEntryFee(date, age);
int fee = getDefaultFee();
di.setInt("Fee", fee);
}
}
@ -657,17 +680,13 @@ int oRunner::getTotalRunningTime() const {
const wstring &oAbstractRunner::getStatusS(bool formatForPrint) const
{
if (formatForPrint && tStatus == StatusUnknown)
return formatTime(-1);
return oEvent::formatStatus(tStatus);
return oEvent::formatStatus(tStatus, formatForPrint);
}
const wstring &oAbstractRunner::getTotalStatusS(bool formatForPrint) const
{
auto ts = getTotalStatus();
if (formatForPrint && ts == StatusUnknown)
return formatTime(-1);
return oEvent::formatStatus(ts);
return oEvent::formatStatus(ts, formatForPrint);
}
/*
@ -2138,6 +2157,14 @@ void oAbstractRunner::setStartNo(int no, bool tmpOnly) {
void oRunner::setStartNo(int no, bool tmpOnly)
{
if (tInTeam) {
if (tInTeam->getStartNo() == 0)
tInTeam->setStartNo(no, tmpOnly);
else {
// Do not allow different from team
no = tInTeam->getStartNo();
}
}
if (tParentRunner)
tParentRunner->setStartNo(no, tmpOnly);
else {
@ -2149,17 +2176,51 @@ void oRunner::setStartNo(int no, bool tmpOnly)
}
}
int oRunner::getPlace() const
{
return tPlace;
void oRunner::updateStartNo(int no) {
if (tInTeam) {
tInTeam->synchronize(false);
for (pRunner r : tInTeam->Runners) {
if (r) {
r->synchronize(false);
}
}
tInTeam->setStartNo(no, false);
for (pRunner r : tInTeam->Runners) {
if (r) {
r->setStartNo(no, false);
}
}
tInTeam->synchronize(true);
for (pRunner r : tInTeam->Runners) {
r->synchronize(true);
}
}
else {
setStartNo(no, false);
synchronize(true);
}
}
int oRunner::getCoursePlace() const
{
int oRunner::getPlace() const {
if (tPlace.isOld(*oe)) {
if (Class) {
oEvent::ResultType rt = oEvent::ResultType::ClassResult;
if (Class->isRogaining())
oe->calculateRogainingResults({ getClassId(true) });
else
oe->calculateResults({ getClassId(true) }, rt, false);
}
}
return tPlace.value;
}
int oRunner::getCoursePlace() const {
return tCoursePlace;
}
int oRunner::getTotalPlace() const
{
if (tInTeam)
@ -3873,7 +3934,8 @@ void oEvent::analyseDNS(vector<pRunner> &unknown_dns, vector<pRunner> &known_dns
typedef multimap<int, pFreePunch>::const_iterator TPunchIter;
for (oFreePunchList::iterator it = punches.begin(); it != punches.end(); ++it) {
punchHash.insert(make_pair(it->getCardNo(), &*it));
if (!it->isRemoved() && !it->isHiredCard())
punchHash.insert(make_pair(it->getCardNo(), &*it));
}
set<int> knownCards;
@ -5015,7 +5077,7 @@ int oRunner::getLegTimeAfter(int ctrlNo) const {
}
int oRunner::getLegPlaceAcc(int ctrlNo) const {
for (auto &res : tOnCourseResults) {
for (auto &res : tOnCourseResults.res) {
if (res.controlIx == ctrlNo)
return res.place;
}
@ -5030,7 +5092,7 @@ int oRunner::getLegPlaceAcc(int ctrlNo) const {
}
int oRunner::getLegTimeAfterAcc(int ctrlNo) const {
for (auto &res : tOnCourseResults) {
for (auto &res : tOnCourseResults.res) {
if (res.controlIx == ctrlNo)
return res.after;
}
@ -5265,7 +5327,7 @@ void oAbstractRunner::setInputStatus(RunnerStatus s) {
}
wstring oAbstractRunner::getInputStatusS() const {
return oe->formatStatus(inputStatus);
return oe->formatStatus(inputStatus, true);
}
void oAbstractRunner::setInputPoints(int p)
@ -5341,16 +5403,26 @@ void oEvent::getDBRunnersInEvent(intkeymap<pClass, __int64> &runners) const {
}
}
void oRunner::init(const RunnerWDBEntry &dbr) {
setTemporary();
dbr.getName(sName);
getRealName(sName, tRealName);
cardNumber = dbr.dbe().cardNo;
Club = oe->getRunnerDatabase().getClub(dbr.dbe().clubNo);
getDI().setString("Nationality", dbr.getNationality());
getDI().setInt("BirthYear", dbr.getBirthYear());
getDI().setString("Sex", dbr.getSex());
setExtIdentifier(dbr.getExtId());
void oRunner::init(const RunnerWDBEntry &dbr, bool updateOnlyExt) {
if (updateOnlyExt) {
dbr.getName(sName);
getRealName(sName, tRealName);
getDI().setString("Nationality", dbr.getNationality());
getDI().setInt("BirthYear", dbr.getBirthYear());
getDI().setString("Sex", dbr.getSex());
setExtIdentifier(dbr.getExtId());
}
else {
setTemporary();
dbr.getName(sName);
getRealName(sName, tRealName);
cardNumber = dbr.dbe().cardNo;
Club = oe->getRunnerDatabase().getClub(dbr.dbe().clubNo);
getDI().setString("Nationality", dbr.getNationality());
getDI().setInt("BirthYear", dbr.getBirthYear());
getDI().setString("Sex", dbr.getSex());
setExtIdentifier(dbr.getExtId());
}
}
void oEvent::selectRunners(const wstring &classType, int lowAge,
@ -5605,11 +5677,11 @@ void oAbstractRunner::setTempResultZero(const TempResult &tr) {
const wstring &oAbstractRunner::TempResult::getStatusS(RunnerStatus inputStatus) const {
if (inputStatus == StatusOK)
return oEvent::formatStatus(getStatus());
return oEvent::formatStatus(getStatus(), true);
else if (inputStatus == StatusUnknown)
return formatTime(-1);
else
return oEvent::formatStatus(max(inputStatus, getStatus()));
return oEvent::formatStatus(max(inputStatus, getStatus()), true);
}
const wstring &oAbstractRunner::TempResult::getPrintPlaceS(bool withDot) const {

View File

@ -198,6 +198,12 @@ public:
// a non-zero fee is changed only if resetFee is true
void addClassDefaultFee(bool resetFees);
/** Returns fee from the class. */
int getDefaultFee() const;
/** Returns the currently assigned fee. */
int getEntryFee() const;
/** Returns true if the entry fee is a late fee. */
bool hasLateEntryFee() const;
@ -362,6 +368,13 @@ public:
oAbstractRunner(oEvent *poe, bool loading);
virtual ~oAbstractRunner() {};
struct DynamicValue {
int dataRevision;
int value;
bool isOld(const oEvent &oe) const;
void update(const oEvent &oe, int v);
};
friend class oListInfo;
friend class GeneralResult;
};
@ -413,7 +426,7 @@ protected:
wstring tRealName;
//Can be changed by apply
mutable int tPlace;
mutable DynamicValue tPlace;
mutable int tCoursePlace;
mutable int tTotalPlace;
mutable int tLeg;
@ -499,8 +512,22 @@ protected:
int place;
int after;
};
pair<int, int> currentControlTime;
mutable vector<OnCourseResult> tOnCourseResults;
mutable pair<int, int> currentControlTime;
struct OnCourseResultCollection {
bool hasAnyRes = false;
vector<OnCourseResult> res;
void clear() { hasAnyRes = false; res.clear(); }
void emplace_back(int courseControlId,
int controlIx,
int time) {
res.emplace_back(courseControlId, controlIx, time);
hasAnyRes = true;
}
bool empty() const { return hasAnyRes == false; }
};
mutable OnCourseResultCollection tOnCourseResults;
// Rogainig results. Control and punch time
vector< pair<pControl, int> > tRogaining;
@ -538,7 +565,7 @@ protected:
public:
// Returns true if there are radio control results, provided result calculation oEvent::ResultType::PreliminarySplitResults was invoked.
bool hasOnCourseResult() const { return tOnCourseResults.size() > 0 || getFinishTime() > 0; }
bool hasOnCourseResult() const { return !tOnCourseResults.empty() || getFinishTime() > 0 || getStatus() != RunnerStatus::StatusUnknown; }
/** Get a runner reference (drawing) */
pRunner getReference() const;
@ -656,7 +683,7 @@ public:
void setTemporary() {isTemporaryObject=true;}
/** Init from dbrunner */
void init(const RunnerWDBEntry &entry);
void init(const RunnerWDBEntry &entry, bool updateOnlyExt);
/** Use db to pdate runner */
bool updateFromDB(const wstring &name, int clubId, int classId,
@ -686,6 +713,8 @@ public:
// which should be more stable.
void setBib(const wstring &bib, int bibNumerical, bool updateStartNo, bool setTmpOnly);
void setStartNo(int no, bool setTmpOnly);
// Update and synch start number for runner and team.
void updateStartNo(int no);
pRunner nextNeedReadout() const;

View File

@ -653,7 +653,7 @@ RunnerStatus oTeam::getLegStatus(int leg, bool multidayTotal) const
const wstring &oTeam::getLegStatusS(int leg, bool multidayTotal) const
{
return oe->formatStatus(getLegStatus(leg, multidayTotal));
return oe->formatStatus(getLegStatus(leg, multidayTotal), true);
}
int oTeam::getLegPlace(int leg, bool multidayTotal) const {

View File

@ -76,9 +76,9 @@ protected:
TeamPlace _places[maxRunnersTeam];
int _sortTime;
int _sortStatus;
RunnerStatus _cachedStatus;
mutable int _sortTime;
mutable int _sortStatus;
mutable RunnerStatus _cachedStatus;
mutable vector< vector< vector<int> > > resultCalculationCache;

View File

@ -422,7 +422,7 @@ void oEvent::setupRelay(oClass &cls, PredefinedTypes type, int nleg, const wstri
cls.setCoursePool(type == PPool);
if (crs) {
cls.addStageCourse(0, crsId);
cls.addStageCourse(0, crsId, -1);
}
break;
@ -437,7 +437,7 @@ void oEvent::setupRelay(oClass &cls, PredefinedTypes type, int nleg, const wstri
cls.setCoursePool(true);
if (crs) {
cls.addStageCourse(0, crsId);
cls.addStageCourse(0, crsId, -1);
}
break;
@ -456,8 +456,8 @@ void oEvent::setupRelay(oClass &cls, PredefinedTypes type, int nleg, const wstri
cls.setRopeTime(1, L"-");
if (crs) {
cls.addStageCourse(0, crsId);
cls.addStageCourse(1, crsId);
cls.addStageCourse(0, crsId, -1);
cls.addStageCourse(1, crsId, -1);
}
cls.setCoursePool(false);
break;
@ -477,8 +477,8 @@ void oEvent::setupRelay(oClass &cls, PredefinedTypes type, int nleg, const wstri
cls.setRopeTime(1, L"-");
if (crs) {
cls.addStageCourse(0, crsId);
cls.addStageCourse(1, crsId);
cls.addStageCourse(0, crsId, -1);
cls.addStageCourse(1, crsId, -1);
}
cls.setCoursePool(false);
break;
@ -498,8 +498,8 @@ void oEvent::setupRelay(oClass &cls, PredefinedTypes type, int nleg, const wstri
cls.setRopeTime(1, L"-");
if (crs) {
cls.addStageCourse(0, crsId);
cls.addStageCourse(1, crsId);
cls.addStageCourse(0, crsId, -1);
cls.addStageCourse(1, crsId, -1);
}
cls.setCoursePool(false);

View File

@ -471,6 +471,9 @@ void OnlineInput::processEntries(oEvent &oe, const xmlList &entries) {
r->setCardNo(cardNo, false, false);
}
if (fee == 0)
fee = r->getDefaultFee();
r->getDI().setInt("Fee", fee);
int toPay = fee;
int cf = 0;
@ -479,6 +482,7 @@ void OnlineInput::processEntries(oEvent &oe, const xmlList &entries) {
if (cf > 0)
toPay += cf;
}
r->setFlag(oRunner::FlagAddedViaAPI, true);
r->getDI().setInt("CardFee", cf);
r->getDI().setInt("Paid", paid ? toPay : 0);

View File

@ -342,8 +342,10 @@ void OnlineResults::status(gdioutput &gdi)
}
if (sendToFile || sendToURL) {
gdi.addString("", 0, "Exporterar om: ");
gdi.addTimer(gdi.getCY(), gdi.getCX(), timerIgnoreSign, (GetTickCount()-timeout)/1000);
if (interval > 0) {
gdi.addString("", 0, "Exporterar om: ");
gdi.addTimer(gdi.getCY(), gdi.getCX(), timerIgnoreSign, (GetTickCount() - timeout) / 1000);
}
gdi.addString("", 0, "Antal skickade uppdateringar X (Y kb)#" +
itos(exportCounter-1) + "#" + itos(bytesExported/1024));
}
@ -515,7 +517,7 @@ void OnlineResults::process(gdioutput &gdi, oEvent *oe, AutoSyncType ast) {
if (ast == SyncNone)
throw;
else
gdi.addInfoBox("", L"Online Results Error X#" + ex.wwhat(), 5000);
gdi.addInfoBox("", L"Online Results Error X#" + lang.tl(ex.wwhat()), 5000);
}
catch(std::exception &ex) {
if (ast == SyncNone)

View File

@ -354,7 +354,7 @@ int QualificationFinal::getLevel(int instance) const {
}
bool QualificationFinal::isFinalClass(int instance) const {
return sourcePlaceToFinalOrder.count(make_pair(instance, 1)) == 0;
return instance > 0 && sourcePlaceToFinalOrder.count(make_pair(instance, 1)) == 0;
}
int QualificationFinal::getNumLevels() const {

View File

@ -88,12 +88,18 @@ static void method_handler(const shared_ptr< restbed::Session > session) {
void RestServer::handleRequest(const shared_ptr<restbed::Session> &session) {
const auto request = session->get_request();
size_t content_length = request->get_header("Content-Length", 0);
string path = request->get_path();
size_t content_length = request->get_header("Content-Length", 0);
chrono::time_point<chrono::system_clock> start, end;
start = chrono::system_clock::now();
auto param = request->get_query_parameters();
if (path == "/" && !root.empty()) {
param = rootMap;
}
auto answer = RestServer::addRequest(param);
{
unique_lock<mutex> mlock(lock);
@ -131,9 +137,30 @@ void RestServer::startThread(int port) {
resource->set_method_handler("GET", method_handler);
restService->publish(resource);
auto resourceRoot = make_shared<MeOSResource>(this);
resourceRoot->set_path("/");
resourceRoot->set_method_handler("GET", method_handler);
restService->publish(resourceRoot);
restService->start(settings);
}
void RestServer::setRootMap(const string &rmap) {
root = rmap;
vector<string> sp;
rootMap.clear();
split(rmap, "&", sp);
for (string arg : sp) {
vector<string> sp2;
split(arg, "=", sp2);
if (sp2.size() == 1)
rootMap.emplace(sp2[0], "");
else if (sp2.size() == 2)
rootMap.emplace(sp2[0], sp2[1]);
}
}
void RestServer::startService(int port) {
if (service)
throw meosException("Server started");
@ -243,6 +270,40 @@ void RestServer::computeInternal(oEvent &ref, shared_ptr<RestServer::EventReques
rq->answer += entryLinks;
vector<pRunner> runners;
ref.getRunners(-1, -1, runners, false);
string sRunnerId, sRunnerBib, sRunnerCard, sRunnerName, sRunnerClub, sRunnerClubId;
for (pRunner r : runners) {
if (sRunnerId.empty())
sRunnerId = itos(r->getId());
if (sRunnerClub.empty()) {
sRunnerName = gdioutput::toUTF8(r->getName());
sRunnerClub = gdioutput::toUTF8(r->getClub());
if (r->getClubId())
sRunnerClubId = gdioutput::toUTF8(r->getClubRef()->getExtIdentifierString());
}
if (sRunnerCard.empty() && r->getCardNo() > 0)
sRunnerCard = itos(r->getCardNo());
if (sRunnerBib.empty() && !r->getBib().empty())
sRunnerBib = gdioutput::recodeToNarrow(r->getBib());
}
if (sRunnerId.empty())
sRunnerId = "1";
if (sRunnerCard.empty())
sRunnerCard = "12345";
if (sRunnerBib.empty())
sRunnerBib = "100";
if (sRunnerClub.empty())
sRunnerClub = "Lost and Found";
if (sRunnerName.empty())
sRunnerName = "Charles Kinsley";
string sRunnerDbName, sRunnerDbId, sRunnerDbClub;
//oe.getRunnerDatabase().
HINSTANCE hInst = GetModuleHandle(0);
HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(132), RT_HTML);
@ -257,8 +318,44 @@ void RestServer::computeInternal(oEvent &ref, shared_ptr<RestServer::EventReques
htmlS += "&lt;";
else if (*html == '#')
htmlS += "&amp;";
else if (*html == '%') {
string key;
char *pKey = html + 1;
resSize--;
while (*pKey != '%' && resSize > 0) {
key += *pKey;
++pKey;
resSize--;
}
if (key == "runnerid") {
htmlS += sRunnerId;
}
else if (key == "card") {
htmlS += sRunnerCard;
}
else if (key == "bib") {
htmlS += sRunnerBib;
}
else if (key == "name") {
htmlS += sRunnerName;
}
else if (key == "club") {
htmlS += sRunnerClub;
}
else if (key == "dbname") {
htmlS += sRunnerName.substr(0, sRunnerName.size() / 2);
}
else if (key == "dbclub") {
htmlS += sRunnerClub;
}
else if (key == "clubid") {
htmlS += sRunnerClubId;
}
html = pKey;
}
else
htmlS += *html;
++html;
resSize--;
}
@ -959,17 +1056,13 @@ void RestServer::lookup(oEvent &oe, const string &what, const multimap<string, s
if (param.count("card") > 0) {
int card = atoi(param.find("card")->second.c_str());
int time = 0;
if (param.count("running") && param.find("card")->second == "true") {
if (param.count("running") && param.find("running")->second == "true") {
time = oe.getRelativeTime(getLocalTime());
if (time < 0)
time = 0;
}
pRunner r = oe.getRunnerByCardNo(card, time, oEvent::CardLookupProperty::CardInUse);
if (!r)
r = oe.getRunnerByCardNo(card, time, oEvent::CardLookupProperty::Any);
if (r)
runners.push_back(r);
oe.getRunnersByCardNo(card, false, oEvent::CardLookupProperty::Any, runners);
}
if (param.count("name") > 0) {
wstring club;
@ -986,7 +1079,7 @@ void RestServer::lookup(oEvent &oe, const string &what, const multimap<string, s
runners.push_back(r);
}
if (param.count("bib") > 0) {
pRunner r = oe.getRunnerByBibOrStartNo(wideParam(param.find("bib")->second), false);
pRunner r = oe.getRunnerByBibOrStartNo(wideParam(param.find("bib")->second), true);
if (r)
runners.push_back(r);
}
@ -1012,7 +1105,9 @@ void RestServer::lookup(oEvent &oe, const string &what, const multimap<string, s
}
if (r->getTeam()) {
xml.write("Team", { make_pair("id", itow(r->getTeam()->getId())) }, r->getTeam()->getName());
xml.write("Leg", r->getLegNumber());
pClass cls = r->getClassRef(false);
if (cls)
xml.write("Leg", cls->getLegNumber(r->getLegNumber()));
}
if ((r->getFinishTime() > 0 || r->getCard() != nullptr) && r->getCourse(false)) {
auto &sd = r->getSplitTimes(false);
@ -1200,7 +1295,16 @@ void RestServer::newEntry(oEvent &oe, const multimap<string, string> &param, str
if (epType != EntryPermissionType::Any) {
if (extId == 0) {
dbr = oe.getRunnerDatabase().getRunnerByName(name, clubId, 0);
int clubExtId = 0;
if (existingClub)
clubExtId = (int)existingClub->getExtIdentifier();
else if (!club.empty()) {
pClub dbClub = oe.getRunnerDatabase().getClub(club);
if (dbClub) {
clubExtId = dbClub->getId();
}
}
dbr = oe.getRunnerDatabase().getRunnerByName(name, clubExtId, 0);
if (dbr)
extId = dbr->getExtId();
}
@ -1220,6 +1324,12 @@ void RestServer::newEntry(oEvent &oe, const multimap<string, string> &param, str
else if (epClass != EntryPermissionClass::Any && !cls->getAllowQuickEntry()) {
permissionDenied = true;
}
else {
int nm = cls->getNumRemainingMaps(false);
if (nm != numeric_limits<int>::min() && nm<=0) {
error = L"Klassen är full";
}
}
if (epType != EntryPermissionType::Any && extId == 0) {
error = L"Anmälan måste hanteras manuellt";
@ -1249,7 +1359,7 @@ void RestServer::newEntry(oEvent &oe, const multimap<string, string> &param, str
if (!permissionDenied && error.empty()) {
pRunner r = oe.addRunner(name, club, classId, cardNo, 0, true);
if (r && dbr) {
r->init(*dbr);
r->init(*dbr, true);
}
if (r) {

View File

@ -108,6 +108,9 @@ private:
void computeInternal(oEvent &ref, shared_ptr<RestServer::EventRequest> &rq);
map<int, pair<oListParam, shared_ptr<oListInfo> > > listCache;
string root;
multimap<string, string> rootMap;
public:
~RestServer();
@ -121,6 +124,9 @@ public:
void setEntryPermission(EntryPermissionClass epClass, EntryPermissionType epType);
void setRootMap(const string &rootMap);
const string &getRootMap() const { return root; }
tuple<EntryPermissionClass, EntryPermissionType> getEntryPermission() const {
return make_tuple(epClass, epType);
}

View File

@ -126,6 +126,8 @@ void DirectSocket::listenDirectSocket() {
closesocket(clientSocket);
}
extern HWND hWndMain;
void startListeningDirectSocket(void *p) {
wstring error;
try {
@ -142,8 +144,9 @@ void startListeningDirectSocket(void *p) {
error = L"Unknown error";
}
if (!error.empty()) {
error = L"Setting up advance information service for punches failed. Punches will be recieved with some seconds delay. Is the network port blocked by an other MeOS session?\n\n" + error;
MessageBox(NULL, error.c_str(), L"MeOS", MB_OK|MB_ICONSTOP);
PostMessage(hWndMain, WM_USER + 5, 0, 0);
//error = L"Setting up advance information service for punches failed. Punches will be recieved with some seconds delay. Is the network port blocked by an other MeOS session?\n\n" + error;
//MessageBox(NULL, error.c_str(), L"MeOS", MB_OK|MB_ICONSTOP);
}
}

View File

@ -1310,7 +1310,7 @@ help:30750 = Här kan du skapa olika sorters listor och rapporter. Du kan visa d
help:31661 = En omstart definieras av en repdragningstid och en omstartstid. Vid repdragningstiden stängs växlingen, och inga startande släpps ut i skogen. Vid omstartstiden går omstarten. Det går att ställa in olika omstartstid på olika sträckor, men med hjälp av den här funktionen sätter du snabbt omstartstid för hela klasser.
help:33940 = Importera anmälningsdata i fritextformat. Ange Namn, Klubb, Klass, och SI (och eventuell starttid) gärna separerade med komma, en person (ett lag) per rad. Det går också att anmäla flera personer i samma klubb/klass genom att (delvis) utelämna fälten klubb/klass. Det är också möjligt att importera anmälningar formaterade på andra sätt.\n\nKlasser skapas automatiskt, men om du importerar lag för stafett eller patrull bör du lägga upp klasserna själv innan du importerar anmälningarna. Annars finns risk att sträcktilldelningen blir fel.
help:41072 = Markera en stämpling i stämplingslistan för att ta bort den eller ändra tiden. Från banmallen kan saknade stämplingar läggas till. Saknas måltid får löparen status utgått. Saknas stämpling får löparen status felstämplat. Det går inte att sätta en status på löparen som inte överensstämmer med stämplingsdata. Finns målstämpling måste tiden för denna ändras för att ändra måltiden; samma princip gäller för startstämpling.
help:41641 = Fyll i första starttid och startintervall. Lottning innebär slumpmässig lottning, SOFT-lottning en lottning enligt SOFT:s klubbfördelningsregler. Klungstart innebär att hela klassen startar i småklungor under det intervall du anger ("utdragen" masstart). \n\nAnge intervall 0 för gemensam start.\n\nNummerlappar: Ange första nummer eller lämna blankt för inga nummerlappar. I fältet sträcka (Str.) anger du vilken sträcka som ska lottas (om klassen har flera sträckor).
help:41641 = Välj metod för lottning. <Lottning> är helt slumpmässig. <Lottning (MeOS)> och <SOFT-lottning> ser till löpare från samma klubb inte startar på närliggande tider.\n\n<Klungstart> innebär att hela klassen startar i småklungor under det intervall du anger ("utdragen" masstart). \n\nNummerlappar: Ange första nummer som ska användas i klassen. I fältet sträcka (Str.) anger du vilken sträcka som ska lottas (om klassen har flera sträckor).
help:425188 = Du kan automatiskt hantera <Ej Start> genom att läsa in SI-stationer (töm/check/start/kontroller) i SI-Config, spara inläsningen som en semikolonseparerad textfil och importera denna i MeOS. De löpare som förekommer i denna import får en registrering. Därefter kan du sätta <Ej Start> på löpare utan registrering. Läser du senare in fler löpare kan du återställa de löpare som tidigare fått Ej Start men nu fått en registrering.
help:471101 = Aktivera SI-enheten genom att välja rätt COM-port, eller genom att söka efter installerade SI-enheter. Info ger dig information om den valda enheten/porten. För att läsa in brickor ska enheten vara programmerad utan autosänd (men för radiokontroller används autosänd). Utökat protokoll rekommenderas, då det ger en stabilare uppkoppling. Enheten programmeras med SportIdents programvara SI-Config.\n\nInteraktiv inläsning används om du direkt vill ta hand om eventuella problem som felaktigt bricknummer; avmarkera om arrangemanget använder 'röd utgång'.\n\nLöpardatabasen används om du automatiskt vill lägga till inkommande löpare med hjälp av löpardatabasen. Löparens stämplingar används för att välja rätt klass.
help:50431 = Du är nu ansluten mot en server. För att öppna en tävling från servern, markera den i listan och välj öppna. För att lägga upp en tävling på servern, öppna först tävlingen lokalt, och använd därefter knappen ladda upp tävling. När du öppnat en tävling på servern ser du vilka andra MeOS-klienter som är anslutna mot den.\n\nOm det står (på server) efter tävlingen är den öppnad på en server och kan delas av andra MeOS-klienter. Står det (lokalt) kan man bara komma åt tävlingen från den aktuella datorn.
@ -2409,3 +2409,30 @@ info:pageswithcolumns = Visa listan en sida i taget med angivet antal kolumner.
Pages with columns = Sidor med kolumner
Pages with columns, no header = Sidor med kolumner utan rubrik
Externa adresser = Externa adresser
info:advanceinfo = Det gick inte att starta tjänsten för förhandsinformation om resultat; resultat kommer med några sekunders fördröjning. Detta är väntat om fler än en MeOS-process körs på samma dator.
Klassen är full = Klassen är full
Flytta upp = Flytta upp
Flytta ner = Flytta ner
EFilterWrongFee = Oväntat avgift
RunnerExpectedFee = Deltagares förväntade avgift
Unexpected Fee = Oväntade anmälningsavgifter
Anmälningsdatum = Anmälningsdatum
Förväntad = Förväntad
Registrera hyrbrickor = Registrera hyrbrickor
Vill du sätta hyrbricka på befintliga löpare med dessa brickor? = Vill du sätta hyrbricka på befintliga löpare med dessa brickor?
Vill du ta bort brickan från hyrbrickslistan? = Vill du ta bort brickan från hyrbrickslistan?
Vill du tömma listan med hyrbrickor? = Vill du tömma listan med hyrbrickor?
help:registerhiredcards = Förregistrera hyrbrickor för att få automatisk hyrbricka när den används.
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
ClassAvailableMaps = Klassens lediga kartor
ClassTotalMaps = Klassens antal kartor
Patrol overtime = Patrulls tid övertid
Patrol score reduction = Patrulls poängreduktion
Patrol score, rogaining = Patrullpoäng, rogaining
Rogaining points before automatic reduction = Rogainingpoäng före automatisk reduktion
Runner's course id = Deltagares banans id
Status code for cancelled entry = Statuskod för återbud