MeOS V4.0, RC1

This commit is contained in:
Erik Melin 2024-04-07 08:37:06 +02:00
parent d06f4cbd7b
commit 403373fcc6
42 changed files with 676 additions and 6403 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -261,6 +261,7 @@
<ClInclude Include="binencoder.h" /> <ClInclude Include="binencoder.h" />
<ClInclude Include="classconfiginfo.h" /> <ClInclude Include="classconfiginfo.h" />
<ClInclude Include="csvparser.h" /> <ClInclude Include="csvparser.h" />
<ClInclude Include="datadefiners.h" />
<ClInclude Include="download.h" /> <ClInclude Include="download.h" />
<ClInclude Include="gdiconstants.h" /> <ClInclude Include="gdiconstants.h" />
<ClInclude Include="gdifonts.h" /> <ClInclude Include="gdifonts.h" />

View File

@ -534,6 +534,16 @@ bool MeosSQL::openDB(oEvent *oe)
upgradeTimeFormat(dbname); upgradeTimeFormat(dbname);
} }
if (version <= 94) {
auto query = con->query();
string v = "ALTER TABLE oRunner MODIFY COLUMN Rank INT NOT NULL DEFAULT 0";
try {
query.execute(v);
}
catch (const Exception&) {
}
}
if (tookLock) { if (tookLock) {
con->query().exec("UNLOCK TABLES"); con->query().exec("UNLOCK TABLES");
} }

View File

@ -111,9 +111,8 @@ void RestService::settings(gdioutput &gdi, oEvent &oe, State state) {
else { else {
gdi.addString("", 0, "Server startad på X#" + itos(port)); gdi.addString("", 0, "Server startad på X#" + itos(port));
auto per = server->getEntryPermission(); auto per = server->getEntryPermission();
if (get<RestServer::EntryPermissionType>(per) != RestServer::EntryPermissionType::None) if (get<RestServer::EntryPermissionType>(per) != RestServer::EntryPermissionType::None) {
disablePermisson = false; disablePermisson = false;
else {
gdi.selectItemByData("PermissionPerson", size_t(get<RestServer::EntryPermissionType>(per))); gdi.selectItemByData("PermissionPerson", size_t(get<RestServer::EntryPermissionType>(per)));
gdi.selectItemByData("PermissionClass", size_t(get<RestServer::EntryPermissionClass>(per))); gdi.selectItemByData("PermissionClass", size_t(get<RestServer::EntryPermissionClass>(per)));
} }

View File

@ -61,8 +61,6 @@ gdioutput *createExtraWindow(const string &tag, const wstring &title, int max_x
gdioutput *getExtraWindow(const string &tag, bool toForeGround); gdioutput *getExtraWindow(const string &tag, bool toForeGround);
string uniqueTag(const char *base); string uniqueTag(const char *base);
void LoadPage(const string &name);
wstring getTempFile(); wstring getTempFile();
wstring getTempPath(); wstring getTempPath();
void removeTempFile(const wstring &file); // Delete a temporyary void removeTempFile(const wstring &file); // Delete a temporyary

View File

@ -77,11 +77,11 @@ protected:
TabBase * const tab = nullptr; TabBase * const tab = nullptr;
public: public:
const string name; const wstring name;
const int imageId = -1; const int imageId = -1;
int id = -1; int id = -1;
TabObject(TabBase *t, string n, int imageId) : name(n), tab(t), imageId(imageId) {} TabObject(TabBase *t, wstring n, int imageId) : name(n), tab(t), imageId(imageId) {}
void setId(int i){id=i; if (tab) tab->tabId=id;} void setId(int i){id=i; if (tab) tab->tabId=id;}

View File

@ -54,9 +54,6 @@ TabCourse::~TabCourse(void)
{ {
} }
void LoadCoursePage(gdioutput &gdi);
void LoadClassPage(gdioutput &gdi);
void TabCourse::selectCourse(gdioutput &gdi, pCourse pc) void TabCourse::selectCourse(gdioutput &gdi, pCourse pc)
{ {
if (gdi.hasWidget("Rogaining")) { if (gdi.hasWidget("Rogaining")) {
@ -473,6 +470,29 @@ int TabCourse::courseCB(gdioutput &gdi, GuiEventType type, BaseInfo* data) {
xml.closeOut(); xml.closeOut();
} }
} }
else if (bi.id == "DeleteAll") {
if (!gdi.ask(L"Vill du ta bort alla banor från tävlingen?"))
return 0;
// Clear all course references
vector<pRunner> rr;
oe->getRunners(0, 0, rr);
for (pRunner r : rr) {
r->setCourseId(0);
}
vector<pClass> cc;
oe->getClasses(cc, true);
for (pClass c : cc) {
c->setCourse(nullptr);
for (int i = 0; i < c->getNumStages(); i++)
c->clearStageCourses(i);
}
vector<pCourse> crs;
oe->getCourses(crs);
for (pCourse c : crs) {
oe->removeCourse(c->getId());
}
loadPage(gdi);
}
else if (bi.id=="ImportCourses") { else if (bi.id=="ImportCourses") {
setupCourseImport(gdi, CourseCB); setupCourseImport(gdi, CourseCB);
} }
@ -651,7 +671,7 @@ int TabCourse::courseCB(gdioutput &gdi, GuiEventType type, BaseInfo* data) {
refreshCourse(gdi.getText("Controls"), gdi); refreshCourse(gdi.getText("Controls"), gdi);
} }
else if (bi.id=="Cancel"){ else if (bi.id=="Cancel"){
LoadPage("Banor"); loadPage(gdi);
} }
} }
else if (type==GUI_LISTBOX){ else if (type==GUI_LISTBOX){
@ -780,6 +800,8 @@ bool TabCourse::loadPage(gdioutput &gdi) {
gdi.addButton("DrawCourse", "Lotta starttider..", CourseCB); gdi.addButton("DrawCourse", "Lotta starttider..", CourseCB);
gdi.disableInput("DrawCourse"); gdi.disableInput("DrawCourse");
} }
gdi.addButton("DeleteAll", "Radera alla...", CourseCB);
gdi.newColumn(); gdi.newColumn();
gdi.fillDown(); gdi.fillDown();

View File

@ -2634,7 +2634,29 @@ bool TabList::loadPage(gdioutput &gdi)
gdi.fillRight(); gdi.fillRight();
gdi.pushX(); gdi.pushX();
bool hasVac = false;
bool hasAPIEntry = false;
bool hasModifiedCard = false;
{
vector<pRunner> rr;
oe->getRunners(0, 0, rr, false);
for (pRunner r : rr) {
if (r->isVacant())
hasVac = true;
if (r->hasFlag(oRunner::FlagAddedViaAPI))
hasAPIEntry = true;
if (r->getCard() && r->getCard()->isOriginalCard() == oCard::PunchOrigin::Manual)
hasModifiedCard = true;
}
}
if (hasModifiedCard) {
gdi.addButton("GenLst:modifiedcard", "Modifierade resultat", ListsCB);
checkWidth(gdi);
}
gdi.addButton("InForestList", "Kvar-i-skogen", ListsCB, "tooltip:inforest").setExtra(IgnoreLimitPer); gdi.addButton("InForestList", "Kvar-i-skogen", ListsCB, "tooltip:inforest").setExtra(IgnoreLimitPer);
if (cnf.hasIndividual()) { if (cnf.hasIndividual()) {
gdi.addButton("PriceList", "Prisutdelningslista", ListsCB); gdi.addButton("PriceList", "Prisutdelningslista", ListsCB);
} }
@ -2661,25 +2683,6 @@ bool TabList::loadPage(gdioutput &gdi)
gdi.addButton("GenLst:teamchanges", "Lagändringblankett", ListsCB).setExtra(AddTeamClasses | ForcePageBreak); gdi.addButton("GenLst:teamchanges", "Lagändringblankett", ListsCB).setExtra(AddTeamClasses | ForcePageBreak);
checkWidth(gdi); checkWidth(gdi);
} }
bool hasVac = false;
bool hasAPIEntry = false;
{
vector<pRunner> rr;
oe->getRunners(0, 0, rr, false);
for (pRunner r : rr) {
if (r->isVacant()) {
hasVac = true;
break;
}
}
for (pRunner r : rr) {
if (r->hasFlag(oRunner::FlagAddedViaAPI)) {
hasAPIEntry = true;
break;
}
}
}
if (hasVac) { if (hasVac) {
gdi.addButton("GenLst:vacnacy", "Vakanser", ListsCB); gdi.addButton("GenLst:vacnacy", "Vakanser", ListsCB);

View File

@ -49,9 +49,11 @@
#include "meosexception.h" #include "meosexception.h"
#include "MeOSFeatures.h" #include "MeOSFeatures.h"
#include "autocomplete.h" #include "autocomplete.h"
#include "datadefiners.h"
#include "RunnerDB.h" #include "RunnerDB.h"
int SportIdentCB(gdioutput *gdi, GuiEventType type, BaseInfo *data); int SportIdentCB(gdioutput *gdi, GuiEventType type, BaseInfo *data);
shared_ptr<RankScoreFormatter> TabRunner::rankFormatter;
TabRunner::TabRunner(oEvent *poe):TabBase(poe) { TabRunner::TabRunner(oEvent *poe):TabBase(poe) {
clearCompetitionData(); clearCompetitionData();
@ -359,7 +361,7 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) {
loadExtraFields(gdi, r); loadExtraFields(gdi, r);
renderComments(gdi, *r); renderComments(gdi, *r, true, true);
gdioutput *gdi_settings = getExtraWindow("ecosettings", false); gdioutput *gdi_settings = getExtraWindow("ecosettings", false);
if (gdi_settings) { if (gdi_settings) {
@ -1073,7 +1075,7 @@ int TabRunner::runnerCB(gdioutput &gdi, GuiEventType type, BaseInfo* data) {
pRunner r = oe->getRunner(runnerId, 0); pRunner r = oe->getRunner(runnerId, 0);
if (r && getExtraWindow("comments", true) == nullptr) { if (r && getExtraWindow("comments", true) == nullptr) {
gdioutput* settings = createExtraWindow("comments", L"Kommentarer", gdi.scaleLength(550), gdi.scaleLength(350), true); gdioutput* settings = createExtraWindow("comments", L"Kommentarer", gdi.scaleLength(550), gdi.scaleLength(350), true);
TabRunner::loadComments(*settings, *r); TabRunner::loadComments(*settings, *r, make_shared<CommentHandler>(*r));
} }
} }
else if (bi.id=="NoStart") { else if (bi.id=="NoStart") {
@ -1424,8 +1426,10 @@ int TabRunner::runnerCB(gdioutput &gdi, GuiEventType type, BaseInfo* data) {
} }
} }
else if (type==GUI_CLEAR) { else if (type==GUI_CLEAR) {
gdioutput *gdi_settings = getExtraWindow("ecosettings", false); if (gdioutput* gdi_settings = getExtraWindow("ecosettings", false); gdi_settings)
if (gdi_settings) gdi_settings->closeWindow();
if (gdioutput* gdi_settings = getExtraWindow("comments", false); gdi_settings)
gdi_settings->closeWindow(); gdi_settings->closeWindow();
if (runnerId>0 && currentMode == 0) if (runnerId>0 && currentMode == 0)
@ -3325,8 +3329,12 @@ void TabRunner::loadExtraFields(gdioutput& gdi, const oBase* r) {
if (gdi.hasWidget("Phone")) if (gdi.hasWidget("Phone"))
gdi.setText("Phone", r ? r->getDCI().getString("Phone") : L""); gdi.setText("Phone", r ? r->getDCI().getString("Phone") : L"");
if (gdi.hasWidget("Rank")) if (gdi.hasWidget("Rank")) {
gdi.setTextZeroBlank("Rank", r ? r->getDCI().getInt("Rank") : 0); wstring out;
if (r)
out = rankFormatter->formatData(r);
gdi.setText("Rank", out);
}
} }
void TabRunner::saveExtraFields(gdioutput& gdi, oBase &r) { void TabRunner::saveExtraFields(gdioutput& gdi, oBase &r) {
@ -3353,8 +3361,11 @@ void TabRunner::saveExtraFields(gdioutput& gdi, oBase &r) {
if (gdi.hasWidget("Phone")) if (gdi.hasWidget("Phone"))
di.setString("Phone", gdi.getText("Phone")); di.setString("Phone", gdi.getText("Phone"));
if (gdi.hasWidget("Rank")) if (gdi.hasWidget("Rank")) {
di.setInt("Rank", gdi.getTextNo("Rank")); wstring out;
rankFormatter->setData(&r, gdi.getText("Rank"), out, 0);
}
// di.setInt("Rank", gdi.getTextNo("Rank"));
} }
void TabRunner::addToolbar(gdioutput &gdi) { void TabRunner::addToolbar(gdioutput &gdi) {
@ -3541,6 +3552,7 @@ void TabRunner::clearCompetitionData() {
timeToFill = 0; timeToFill = 0;
ownWindow = false; ownWindow = false;
listenToPunches = false; listenToPunches = false;
rankFormatter = make_shared<RankScoreFormatter>();
} }
void TabRunner::autoGrowCourse(gdioutput &gdi) { void TabRunner::autoGrowCourse(gdioutput &gdi) {
@ -3739,7 +3751,6 @@ void TabRunner::CommentHandler::handle(gdioutput& gdi, BaseInfo& info, GuiEventT
ButtonInfo& bi = dynamic_cast<ButtonInfo&>(info); ButtonInfo& bi = dynamic_cast<ButtonInfo&>(info);
if (bi.id == "Cancel") { if (bi.id == "Cancel") {
gdi.closeWindow(); gdi.closeWindow();
gdi.closeWindow();
} }
else if (bi.id == "Save") { else if (bi.id == "Save") {
save(gdi); save(gdi);
@ -3748,15 +3759,18 @@ void TabRunner::CommentHandler::handle(gdioutput& gdi, BaseInfo& info, GuiEventT
} }
} }
void TabRunner::CommentHandler::save(gdioutput& gdi) { void TabRunner::CommentHandler::doSave(gdioutput& gdi) {
oAbstractRunner& r = getRunner(); oAbstractRunner& r = getRunner();
wstring comment = gdi.getText("Comments"); wstring comment = gdi.getText("Comments");
r.getDI().setString("Annotation", getLocalTime() + L"@" + comment); r.getDI().setString("Annotation", getLocalTime() + L"@" + comment);
TabRunner::renderComments(oe->gdiBase(), r);
} }
void TabRunner::renderComments(gdioutput& gdi, oAbstractRunner& r) { void TabRunner::CommentHandler::save(gdioutput& gdi) {
doSave(gdi);
TabRunner::renderComments(oe->gdiBase(), getRunner(), true, true);
}
void TabRunner::renderComments(gdioutput& gdi, oAbstractRunner& r, bool newColumn, bool refresh) {
gdi.restore("Annotation", false); gdi.restore("Annotation", false);
gdi.setRestorePoint("Annotation"); gdi.setRestorePoint("Annotation");
@ -3774,33 +3788,37 @@ void TabRunner::renderComments(gdioutput& gdi, oAbstractRunner& r) {
} }
gdi.fillDown(); gdi.fillDown();
if (newColumn)
gdi.newColumn(); gdi.newColumn();
int cx = gdi.getCX();
gdi.dropLine(); gdi.dropLine();
RECT rc; RECT rc;
rc.left = gdi.getCX(); rc.left = gdi.getCX();
rc.top = gdi.getCY(); rc.top = gdi.getCY();
gdi.dropLine(0.5); gdi.dropLine(0.5);
gdi.setCX(gdi.getCX() + gdi.scaleLength(5)); gdi.setCX(cx + gdi.scaleLength(5));
gdi.addString("", fontMediumPlus, "Kommentarer"); gdi.addString("", fontMediumPlus, "Kommentarer");
gdi.addString("", 0, date); gdi.addString("", 0, date);
gdi.dropLine(); gdi.dropLine();
gdi.addStringUT(gdi.getCY(), gdi.getCX(), breakLines, an, 200); TextInfo &ti = gdi.addStringUT(gdi.getCY(), gdi.getCX(), breakLines, an, gdi.scaleLength(newColumn ? 200 : 300));
gdi.dropLine(); gdi.dropLine();
rc.bottom = gdi.getCY(); rc.bottom = gdi.getCY();
rc.right = gdi.getWidth() + gdi.scaleLength(3); rc.right = max<int>(ti.textRect.right, cx+gdi.scaleLength(150)) + gdi.scaleLength(6);
gdi.addRectangle(rc, GDICOLOR::colorLightYellow); gdi.addRectangle(rc, GDICOLOR::colorLightYellow);
gdi.setCX(cx);
if (refresh)
gdi.refresh(); gdi.refresh();
} }
void TabRunner::loadComments(gdioutput& gdi, oAbstractRunner& r) { void TabRunner::loadComments(gdioutput& gdi, oAbstractRunner& r, const shared_ptr<CommentHandler>& handler) {
gdi.clearPage(false); gdi.clearPage(false);
gdi.fillDown(); gdi.fillDown();
gdi.pushX(); gdi.pushX();
gdi.addString("", fontMediumPlus, L"Kommentarer för X#" + r.getName()); gdi.addString("", fontMediumPlus, L"Kommentarer för X#" + r.getName());
auto h = make_shared<CommentHandler>(r);
wstring an = r.getDCI().getString("Annotation"); wstring an = r.getDCI().getString("Annotation");
for (int j = 0; j + 1 < an.length(); j++) { for (int j = 0; j + 1 < an.length(); j++) {
if (an[j] == '@') { if (an[j] == '@') {
@ -3810,8 +3828,8 @@ void TabRunner::loadComments(gdioutput& gdi, oAbstractRunner& r) {
gdi.addInputBox("Comments", 250, 100, an, nullptr, L""); gdi.addInputBox("Comments", 250, 100, an, nullptr, L"");
gdi.fillRight(); gdi.fillRight();
gdi.addButton("Cancel", "Avbryt").setCancel().setHandler(h); gdi.addButton("Cancel", "Avbryt").setCancel().setHandler(handler);
gdi.addButton("Save", "Spara").setHandler(h); gdi.addButton("Save", "Spara").setHandler(handler);
gdi.refresh(); gdi.refresh();
} }

View File

@ -27,6 +27,7 @@
class Table; class Table;
struct AutoCompleteRecord; struct AutoCompleteRecord;
class RankScoreFormatter;
class TabRunner : class TabRunner :
public TabBase, AutoCompleteHandler public TabBase, AutoCompleteHandler
@ -87,9 +88,11 @@ private:
bool savePunchTime(pRunner r, gdioutput &gdi); bool savePunchTime(pRunner r, gdioutput &gdi);
PrinterObject splitPrinter; PrinterObject splitPrinter;
static shared_ptr<RankScoreFormatter> rankFormatter;
void showRunnerReport(gdioutput &gdi); void showRunnerReport(gdioutput &gdi);
static void runnerReport(oEvent &oe, gdioutput &gdi, static void runnerReport(oEvent &oe, gdioutput &gdi,
int id, bool compactReport, int id, bool compactReport,
int maxWidth, int maxWidth,
@ -132,27 +135,29 @@ private:
void save(gdioutput &gdi); void save(gdioutput &gdi);
}; };
class CommentHandler : public GuiHandler {
int runnerId;
bool isTeam = false;
oEvent* oe;
oAbstractRunner& getRunner() const;
public:
CommentHandler(oAbstractRunner& r) : oe(r.getEvent()) {
runnerId = r.getId(); isTeam = r.isTeam();
}
void handle(gdioutput& gdi, BaseInfo& info, GuiEventType type);
void save(gdioutput& gdi);
};
void getAutoCompleteUnpairedCards(gdioutput &gdi, const wstring& w, vector<AutoCompleteRecord>& records); void getAutoCompleteUnpairedCards(gdioutput &gdi, const wstring& w, vector<AutoCompleteRecord>& records);
protected: protected:
void clearCompetitionData(); void clearCompetitionData();
public: public:
static void renderComments(gdioutput& gdi, oAbstractRunner& r); class CommentHandler : public GuiHandler {
static void loadComments(gdioutput& gdi, oAbstractRunner& r); int runnerId;
bool isTeam = false;
protected:
oAbstractRunner& getRunner() const;
oEvent* oe;
void doSave(gdioutput& gdi);
public:
CommentHandler(oAbstractRunner& r) : oe(r.getEvent()) {
runnerId = r.getId(); isTeam = r.isTeam();
}
void handle(gdioutput& gdi, BaseInfo& info, GuiEventType type);
virtual void save(gdioutput& gdi);
};
static void renderComments(gdioutput& gdi, oAbstractRunner& r, bool newColumn, bool refresh);
static void loadComments(gdioutput& gdi, oAbstractRunner& r, const shared_ptr<CommentHandler> &handler);
static pClub extractClub(oEvent *oe, gdioutput &gdi); static pClub extractClub(oEvent *oe, gdioutput &gdi);

View File

@ -4488,6 +4488,13 @@ void TabSI::clearCompetitionData() {
requestStartTimeHandler.reset(); requestStartTimeHandler.reset();
sortAssignCards = SortOrder::Custom; sortAssignCards = SortOrder::Custom;
#ifdef _DEBUG
showTestingPanel = !oe->gdiBase().isTest();
#else
showTestingPanel = false;
#endif // _DEBUG
} }
SICard& TabSI::getCard(int id) const { SICard& TabSI::getCard(int id) const {

View File

@ -167,9 +167,11 @@ int TabTeam::searchCB(gdioutput &gdi, int type, void *data) {
return 0; return 0;
} }
void TabTeam::selectTeam(gdioutput &gdi, pTeam t) void TabTeam::selectTeam(gdioutput &gdi, pTeam t)
{ {
if (gdioutput* gdi_comments = getExtraWindow("comments", false); gdi_comments)
gdi_comments->closeWindow();
if (t){ if (t){
t->synchronize(); t->synchronize();
t->evaluate(oBase::ChangeType::Quiet); t->evaluate(oBase::ChangeType::Quiet);
@ -588,7 +590,20 @@ int TabTeam::teamCB(gdioutput &gdi, GuiEventType type, BaseInfo* data) {
pTeam t = oe->getTeam(teamId); pTeam t = oe->getTeam(teamId);
if (t && getExtraWindow("comments", true) == nullptr) { if (t && getExtraWindow("comments", true) == nullptr) {
gdioutput* settings = createExtraWindow("comments", L"Kommentarer", gdi.scaleLength(550), gdi.scaleLength(350), true); gdioutput* settings = createExtraWindow("comments", L"Kommentarer", gdi.scaleLength(550), gdi.scaleLength(350), true);
TabRunner::loadComments(*settings, *t);
class TeamComments : public TabRunner::CommentHandler {
public:
TeamComments(oTeam& t) : CommentHandler(t) {}
void save(gdioutput& gdi) override {
doSave(gdi);
auto t = &getRunner();
oe->gdiBase().restore("SelectR");
TabTeam::forkingKey(oe->gdiBase(), pTeam(t));
oe->gdiBase().refresh();
}
};
TabRunner::loadComments(*settings, *t, make_shared<TeamComments>(*t));
} }
} }
else if (bi.id=="Search") { else if (bi.id=="Search") {
@ -758,8 +773,12 @@ int TabTeam::teamCB(gdioutput &gdi, GuiEventType type, BaseInfo* data) {
gdi.selectItemByData("ForkKey", currentKey); gdi.selectItemByData("ForkKey", currentKey);
gdi.dropLine(0.9); gdi.dropLine(0.9);
gdi.addButton("SaveKey", "Ändra", TeamCB); gdi.addButton("SaveKey", "Ändra", TeamCB).setDefault();
gdi.refreshFast(); gdi.addButton("Cancel", "Avbryt", TeamCB).setCancel();
gdi.popX();
gdi.dropLine(1);
renderComments(t, gdi);
gdi.refresh();
} }
else if (bi.id == "SaveKey") { else if (bi.id == "SaveKey") {
pTeam t = oe->getTeam(teamId); pTeam t = oe->getTeam(teamId);
@ -918,13 +937,15 @@ int TabTeam::teamCB(gdioutput &gdi, GuiEventType type, BaseInfo* data) {
gdi.addSelection("SelectR", 250, 400, TeamCB); gdi.addSelection("SelectR", 250, 400, TeamCB);
gdi.addButton("SelectRunner", "OK", TeamCB).setExtra(leg); gdi.addButton("SelectRunner", "OK", TeamCB).setExtra(leg);
gdi.fillDown(); gdi.fillDown();
gdi.addButton("Cancel", "Avbryt", TeamCB); gdi.addButton("Cancel", "Avbryt", TeamCB).setCancel();
gdi.setItems("SelectR", otherR); gdi.setItems("SelectR", otherR);
} }
else { else {
gdi.addButton("Cancel", "Avbryt", TeamCB); gdi.addButton("Cancel", "Avbryt", TeamCB).setCancel();
} }
gdi.popX();
renderComments(t, gdi);
gdi.refresh(); gdi.refresh();
} }
else if (bi.id=="SelectRunner") { else if (bi.id=="SelectRunner") {
@ -1238,6 +1259,9 @@ int TabTeam::teamCB(gdioutput &gdi, GuiEventType type, BaseInfo* data) {
if (teamId>0) if (teamId>0)
save(gdi, true); save(gdi, true);
if (gdioutput* gdi_settings = getExtraWindow("comments", false); gdi_settings)
gdi_settings->closeWindow();
return true; return true;
} }
else if (type==GUI_POSTCLEAR) { else if (type==GUI_POSTCLEAR) {
@ -1352,11 +1376,6 @@ void TabTeam::loadTeamMembers(gdioutput &gdi, int ClassId, int ClubId, pTeam t)
void TabTeam::forkingKey(gdioutput& gdi, pTeam t) { void TabTeam::forkingKey(gdioutput& gdi, pTeam t) {
pClass pc = t->getClassRef(false); pClass pc = t->getClassRef(false);
int cx = gdi.getCX();
int cy = gdi.getCY();
TabRunner::renderComments(gdi, *t);
gdi.setCX(cx);
gdi.setCY(cy);
gdi.pushX(); gdi.pushX();
gdi.setRestorePoint("SelectR"); gdi.setRestorePoint("SelectR");
@ -1375,6 +1394,17 @@ void TabTeam::forkingKey(gdioutput& gdi, pTeam t) {
gdi.addButton("ChangeKey", "Ändra lagets gaffling", TeamCB); gdi.addButton("ChangeKey", "Ändra lagets gaffling", TeamCB);
} }
} }
renderComments(t, gdi);
}
void TabTeam::renderComments(const pTeam& t, gdioutput& gdi) {
if (t->getNumRunners() > 5)
TabRunner::renderComments(gdi, *t, true, false);
else {
gdi.dropLine(1); // Leave room for the below
TabRunner::renderComments(gdi, *t, false, false);
}
} }
void TabTeam::enableRunner(gdioutput& gdi, int index, bool enable) { void TabTeam::enableRunner(gdioutput& gdi, int index, bool enable) {

View File

@ -78,7 +78,9 @@ private:
void enableRunner(gdioutput &gdi, int index, bool enable); void enableRunner(gdioutput &gdi, int index, bool enable);
/// Show forking key in the window /// Show forking key in the window
void forkingKey(gdioutput& gdi, pTeam t); static void forkingKey(gdioutput& gdi, pTeam t);
static void renderComments(const pTeam& t, gdioutput& gdi);
protected: protected:
void clearCompetitionData(); void clearCompetitionData();

View File

@ -2687,11 +2687,11 @@ Aktivera kioskläge = Activate kiosk mode
Avstånd mellan förslag (minuter) = Distance between suggestions (minutes) Avstånd mellan förslag (minuter) = Distance between suggestions (minutes)
Boka starttid = Request start time Boka starttid = Request start time
Ingen ledig starttid kunde hittas = No free start time was found Ingen ledig starttid kunde hittas = No free start time was found
Minsta tid till start (minuter) = Least time to start (minutes) Minsta tid till start (minuter) = Minimum time to start (minutes)
Sista starttid = Last start time Sista starttid = Last start time
Startintervall (minuter) = Start interval (minutes) Startintervall (minuter) = Start interval (minutes)
Tillåt klass med samma bana på samma starttid = Allow class with same course on same start time Tillåt klass med samma bana på samma starttid = Allow class with same course on same start time
Tillåt klass med samma bana på stattid före/efter = Allow class with same course before and after Tillåt klass med samma bana på starttid före/efter = Allow class with same course before and after
Tillåt klass med samma första kontroll vid samma starttid = Allow class with same first control at the same time Tillåt klass med samma första kontroll vid samma starttid = Allow class with same first control at the same time
Tiden har passerat sista tillåtna starttid = Time has passed last allowed start time Tiden har passerat sista tillåtna starttid = Time has passed last allowed start time
Starttiden är låst = The start time is locked Starttiden är låst = The start time is locked
@ -2735,7 +2735,7 @@ Antal nivåer = Number of levels
Använd ranking istället för placering i kval för att placera kvalificerade löpare i klasser = Use ranking instead of placement to distribute qualified runners into classes Använd ranking istället för placering i kval för att placera kvalificerade löpare i klasser = Use ranking instead of placement to distribute qualified runners into classes
Bästa tid = Best time Bästa tid = Best time
Final = Final Final = Final
Klass / placering = Class / place Klass / placering = Class / placement
Klass X (namnsuffix) = Class X (name suffix) Klass X (namnsuffix) = Class X (name suffix)
Klass efter ranking = Select class by ranking Klass efter ranking = Select class by ranking
Kval = Qualification Kval = Qualification
@ -2795,7 +2795,19 @@ TeamTextA = Team text
Födelsedatum = Birth date Födelsedatum = Birth date
Kommentar = Comment Kommentar = Comment
Lägg till eller redigera kommentarer om deltagaren = Add or edit a comment about the competitor Lägg till eller redigera kommentarer om deltagaren = Add or edit a comment about the competitor
Lägg till eller redigera kommentarer om deltagaren = Add or edit a comment about the team Lägg till eller redigera kommentarer om laget = Add or edit a comment about the team
Manuellt ändrad brickdata = Manually modified card data Manuellt ändrad brickdata = Manually modified card data
Efteranmälan = Late entry Efteranmälan = Late entry
Reducerad = Reduced Reducerad = Reduced
Kommentarer för X = Comments for X
Layout = Layout
kolumner = columns
rader = rows
Modifierade resultat = Modified results
EFilterModifiedCard = With modified result
Vill du uppdatera X med ändringarna? = Do you want to update X with the changes?
Vill du spara en kopia av tävlingen med starttider för ytterligare analys? = Do you want to save a copy of the competition with start times for further analysis?||||||| .r1334
RunnerRankScore = Ranking (score)
Radera alla = Remove all
Vill du ta bort alla banor från tävlingen? = Do you want to remove all courses from the competition?
Tillåt klass med samma bana på stattid före/efter = Allow class with the same course before/after

View File

@ -6350,7 +6350,7 @@ void gdioutput::processToolbarMessage(const string &id, Table *tbl) {
wstring msg; wstring msg;
string cmd; string cmd;
if (getRecorder().recording()) { if (getRecorder().recording()) {
cmd = "tableCmd(\"" + id + "\"); //" + narrow(tbl->getTableName()); cmd = "tableCmd(\"" + id + "\"); //" + toUTF8(tbl->getTableName());
} }
try { try {
ButtonInfo bi; ButtonInfo bi;

View File

@ -12,7 +12,7 @@
<b>Example:</b> <b>Example:</b>
<pre> <pre>
*MOPComplete> *MOPComplete>
*competition date="2015-09-06" organizer="Orienteringsklubben Linn&eacute;" homepage="http://www.oklinne.nu" zerotime="216000">Stafett-DM, Uppland*/competition> *competition date="2015-09-06" organizer="Orienteringsklubben Linn&eacute;" homepage="http://www.oklinne.nu" zerotime="08:00:00">Stafett-DM, Uppland*/competition>
*/MOPComplete> */MOPComplete>
</pre> </pre>

View File

@ -131,8 +131,8 @@ bool InfoCompetition::synchronize(oEvent &oe, bool onlyCmp, const set<int> &incl
changed = true; changed = true;
} }
if (oe.getZeroTimeNum() != zerotime) { if (oe.getZeroTime() != zeroTime) {
zerotime = oe.getZeroTimeNum(); zeroTime = oe.getZeroTime();
changed = true; changed = true;
} }
@ -457,7 +457,7 @@ void InfoCompetition::serialize(xmlbuffer &xml, bool diffOnly) const {
prop.push_back(make_pair("date", date)); prop.push_back(make_pair("date", date));
prop.push_back(make_pair("organizer", organizer)); prop.push_back(make_pair("organizer", organizer));
prop.push_back(make_pair("homepage", homepage)); prop.push_back(make_pair("homepage", homepage));
prop.emplace_back("zerotime", itow(zerotime)); prop.push_back(make_pair("zerotime", zeroTime));
xml.write("competition", prop, name); xml.write("competition", prop, name);
} }

View File

@ -221,7 +221,7 @@ private:
wstring date; wstring date;
wstring organizer; wstring organizer;
wstring homepage; wstring homepage;
int zerotime; wstring zeroTime;
protected: protected:
bool forceComplete; bool forceComplete;

View File

@ -261,8 +261,24 @@ void IOF30Interface::readCourseData(gdioutput &gdi, const xmlobject &xo, bool up
} }
for (unsigned leg = 0; leg < pc->getNumStages() && leg < coursePattern[0].size(); leg++) { for (unsigned leg = 0; leg < pc->getNumStages() && leg < coursePattern[0].size(); leg++) {
pc->clearStageCourses(leg); pc->clearStageCourses(leg);
int legToUse = leg;
while (legToUse > 0 && (pc->getLegType(legToUse) == LegTypes::LTExtra ||
pc->getLegType(legToUse) == LegTypes::LTIgnore))
legToUse--;
bool sameLeg = true;
for (int m = 1; m < period; m++) {
if (coursePattern[m][legToUse] != coursePattern[0][legToUse]) {
sameLeg = false;
break;
}
}
if (sameLeg) { // No forking
pc->addStageCourse(leg, coursePattern[0][legToUse], -1);
}
else {
for (int m = 0; m < period; m++) for (int m = 0; m < period; m++)
pc->addStageCourse(leg, coursePattern[(period - patternStart + m)%period][leg], -1); pc->addStageCourse(leg, coursePattern[(period - patternStart + m) % period][legToUse], -1);
}
} }
bool classHeader = false; bool classHeader = false;
@ -1049,10 +1065,20 @@ void IOF30Interface::readEntryList(gdioutput &gdi, xmlobject &xo, bool removeNon
allR[k]->flagEntryTouched(false); allR[k]->flagEntryTouched(false);
} }
map<pair<int, int>, int> teamLegToFixedCourse;
oe.getTeams(0, allT, false); oe.getTeams(0, allT, false);
for (size_t k = 0; k < allT.size(); k++) { for (size_t k = 0; k < allT.size(); k++) {
if (allT[k]->getEntrySource() == entrySourceId) if (allT[k]->getEntrySource() == entrySourceId)
allT[k]->flagEntryTouched(false); allT[k]->flagEntryTouched(false);
for (int leg = 0; leg < allT[k]->getNumRunners(); leg++) {
pRunner r = allT[k]->getRunner(leg);
if (r && r->getCourseId() > 0) {
pair key(allT[k]->getId(), leg);
teamLegToFixedCourse[key] = r->getCourseId();
}
}
} }
xmlList pEntries; xmlList pEntries;
@ -1311,6 +1337,25 @@ void IOF30Interface::readEntryList(gdioutput &gdi, xmlobject &xo, bool removeNon
oe.getDI().setString("LateEntryFactor", fs); oe.getDI().setString("LateEntryFactor", fs);
} }
} }
oe.getTeams(0, allT, false);
for (size_t k = 0; k < allT.size(); k++) {
for (int leg = 0; leg < allT[k]->getNumRunners(); leg++) {
pRunner r = allT[k]->getRunner(leg);
if (r) {
pair key(allT[k]->getId(), leg);
auto res = teamLegToFixedCourse.find(key);
if (res == teamLegToFixedCourse.end()) {
r->setCourseId(0);
}
else {
r->setCourseId(res->second);
}
r->synchronize(true);
}
}
}
} }
void IOF30Interface::readServiceRequestList(gdioutput &gdi, xmlobject &xo, int &entRead, int &entFail) { void IOF30Interface::readServiceRequestList(gdioutput &gdi, xmlobject &xo, int &entRead, int &entFail) {
@ -2183,6 +2228,12 @@ pRunner IOF30Interface::readPersonEntry(gdioutput &gdi, xmlobject &xo, pTeam tea
} }
} }
xmlobject score = xo.getObject("Score");
if (score && score.getRawPtr()) {
double s = atof(score.getRawPtr());
r->setRankingScore(s);
}
bool hasTime = true; bool hasTime = true;
xmlobject ext = xo.getObject("Extensions"); xmlobject ext = xo.getObject("Extensions");
if (ext) { if (ext) {

View File

@ -126,21 +126,6 @@ void resetSaveTimer() {
autoTask->resetSaveTimer(); autoTask->resetSaveTimer();
} }
void LoadPage(const string &name)
{
list<TabObject>::iterator it;
for (it=tabList->begin(); it!=tabList->end(); ++it) {
if (it->name==name)
it->loadPage(*gdi_main);
}
}
void LoadClassPage(gdioutput &gdi)
{
LoadPage("Klasser");
}
void dumpLeaks() { void dumpLeaks() {
_CrtDumpMemoryLeaks(); _CrtDumpMemoryLeaks();
} }
@ -788,17 +773,14 @@ BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
int xs = gEvent->getPropertyInt("xsize", max(850, min<int>(int(rc.right)-yp, (rc.right*9)/10))); int xs = gEvent->getPropertyInt("xsize", max(850, min<int>(int(rc.right)-yp, (rc.right*9)/10)));
int ys = gEvent->getPropertyInt("ysize", max(650, min<int>(int(rc.bottom)-yp-40, (rc.bottom*8)/10))); int ys = gEvent->getPropertyInt("ysize", max(650, min<int>(int(rc.bottom)-yp-40, (rc.bottom*8)/10)));
if ((xp + xs > rc.right) if ((xp + xs > rc.right) || xp < rc.left || yp + ys > rc.bottom || yp < rc.top) {
|| xp < rc.left
|| yp + ys > rc.bottom
|| yp < rc.top)
{
// out of bounds, just use default position and size // out of bounds, just use default position and size
xp = 50; xp = 50;
yp = 20; yp = 20;
xs = max(850, min<int>(int(rc.right) - yp, (rc.right * 9) / 10)); xs = max(850, min<int>(int(rc.right) - yp, (rc.right * 9) / 10));
ys = max(650, min<int>(int(rc.bottom) - yp - 40, (rc.bottom * 8) / 10)); ys = max(650, min<int>(int(rc.bottom) - yp - 40, (rc.bottom * 8) / 10));
} }
gEvent->setProperty("ypos", yp + 16); gEvent->setProperty("ypos", yp + 16);
gEvent->setProperty("xpos", xp + 32); gEvent->setProperty("xpos", xp + 32);
gEvent->saveProperties(settings); // For other instance starting while running gEvent->saveProperties(settings); // For other instance starting while running
@ -1054,22 +1036,22 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{ {
case WM_CREATE: case WM_CREATE:
tabList->emplace_back(gdi_main->getTabs().get(TCmpTab), "Tävling", 10); tabList->emplace_back(gdi_main->getTabs().get(TCmpTab), L"Tävling", 10);
tabList->emplace_back(gdi_main->getTabs().get(TRunnerTab), "Deltagare", 6); tabList->emplace_back(gdi_main->getTabs().get(TRunnerTab), L"Deltagare", 6);
tabList->emplace_back(gdi_main->getTabs().get(TTeamTab), "Lag(flera)", 7); tabList->emplace_back(gdi_main->getTabs().get(TTeamTab), L"Lag(flera)", 7);
tabList->emplace_back(gdi_main->getTabs().get(TListTab), "Listor", 5); tabList->emplace_back(gdi_main->getTabs().get(TListTab), L"Listor", 5);
{ {
TabAuto *ta = (TabAuto *)gdi_main->getTabs().get(TAutoTab); TabAuto *ta = (TabAuto *)gdi_main->getTabs().get(TAutoTab);
tabList->emplace_back(ta, "Automater", 4); tabList->emplace_back(ta, L"Automater", 4);
TabAuto::tabAutoRegister(ta); TabAuto::tabAutoRegister(ta);
} }
tabList->emplace_back(gdi_main->getTabs().get(TSpeakerTab), "Speaker", 3); tabList->emplace_back(gdi_main->getTabs().get(TSpeakerTab), L"Speaker", 3);
tabList->emplace_back(gdi_main->getTabs().get(TClassTab), "Klasser", 0); tabList->emplace_back(gdi_main->getTabs().get(TClassTab), L"Klasser", 0);
tabList->emplace_back(gdi_main->getTabs().get(TCourseTab), "Banor", 1); tabList->emplace_back(gdi_main->getTabs().get(TCourseTab), L"Banor", 1);
tabList->emplace_back(gdi_main->getTabs().get(TControlTab), "Kontroller", 2); tabList->emplace_back(gdi_main->getTabs().get(TControlTab), L"Kontroller", 2);
tabList->emplace_back(gdi_main->getTabs().get(TClubTab), "Klubbar", 8); tabList->emplace_back(gdi_main->getTabs().get(TClubTab), L"Klubbar", 8);
tabList->emplace_back(gdi_main->getTabs().get(TSITab), "SportIdent", 9); tabList->emplace_back(gdi_main->getTabs().get(TSITab), L"SportIdent", 9);
INITCOMMONCONTROLSEX ic; INITCOMMONCONTROLSEX ic;
@ -1190,7 +1172,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
if (it->id==id) { if (it->id==id) {
try { try {
gdi_main->setWaitCursor(true); gdi_main->setWaitCursor(true);
string cmd = "showTab("+ string(it->getTab().getTypeStr()) + "); //" + it->name; string cmd = "showTab("+ string(it->getTab().getTypeStr()) + "); //" + gdi_main->toUTF8(it->name);
it->loadPage(*gdi_main); it->loadPage(*gdi_main);
gdi_main->getRecorder().record(cmd); gdi_main->getRecorder().record(cmd);
} }

View File

@ -2243,7 +2243,7 @@ static bool noCapitalize(const wstring &str, size_t pos) {
word.push_back(char(str[pos++])); word.push_back(char(str[pos++]));
} }
if (word == "of" || word == "for" || word == "at" || word == "by") if (word == "of" || word == "for" || word == "at" || word == "by" || word == "on")
return true; return true;
if (word == "and" || word == "or" || word == "from" || word == "as" || word == "in") if (word == "and" || word == "or" || word == "from" || word == "as" || word == "in")

View File

@ -25,17 +25,17 @@
//ABCDEFGHIJKLMNOPQ //ABCDEFGHIJKLMNOPQ
int getMeosBuild() { int getMeosBuild() {
string revision("$Rev: 1326 $"); string revision("$Rev: 1341 $");
return 174 + atoi(revision.substr(5, string::npos).c_str()); return 174 + atoi(revision.substr(5, string::npos).c_str());
} }
wstring getMeosDate() { wstring getMeosDate() {
wstring date(L"$Date: 2024-03-02 21:15:41 +0100 (lör, 02 mar 2024) $"); wstring date(L"$Date: 2024-04-06 22:32:45 +0200 (lör, 06 apr 2024) $");
return date.substr(7,10); return date.substr(7,10);
} }
wstring getBuildType() { wstring getBuildType() {
return L"Beta 1"; // No parantheses (...) return L"RC 1"; // No parantheses (...)
} }
wstring getMajorVersion() { wstring getMajorVersion() {
@ -153,6 +153,13 @@ void getSupporters(vector<wstring>& supp, vector<wstring>& developSupp)
supp.emplace_back(L"Silkeborg OK"); supp.emplace_back(L"Silkeborg OK");
supp.emplace_back(L"IK Uven"); supp.emplace_back(L"IK Uven");
supp.emplace_back(L"Attunda OK"); supp.emplace_back(L"Attunda OK");
supp.emplace_back(L"Gunnar Svanberg");
supp.emplace_back(L"Forsa OK");
supp.emplace_back(L"Långhundra IF");
supp.emplace_back(L"Mariestads friluftsklubb");
supp.emplace_back(L"Ligue PACA");
supp.emplace_back(L"SV Robotron Dresden");
supp.emplace_back(L"Mats Holmberg, OK Gränsen");
reverse(supp.begin(), supp.end()); reverse(supp.begin(), supp.end());
} }

View File

@ -2223,6 +2223,7 @@ void MetaList::initSymbols() {
typeToSymbol[lRunnerBib] = L"RunnerBib"; typeToSymbol[lRunnerBib] = L"RunnerBib";
typeToSymbol[lRunnerStartNo] = L"RunnerStartNo"; typeToSymbol[lRunnerStartNo] = L"RunnerStartNo";
typeToSymbol[lRunnerRank] = L"RunnerRank"; typeToSymbol[lRunnerRank] = L"RunnerRank";
typeToSymbol[lRunnerRankScore] = L"RunnerRankScore";
typeToSymbol[lRunnerCourse] = L"RunnerCourse"; typeToSymbol[lRunnerCourse] = L"RunnerCourse";
typeToSymbol[lRunnerRogainingPoint] = L"RunnerRogainingPoint"; typeToSymbol[lRunnerRogainingPoint] = L"RunnerRogainingPoint";
typeToSymbol[lRunnerRogainingPointTotal] = L"RunnerRogainingPointTotal"; typeToSymbol[lRunnerRogainingPointTotal] = L"RunnerRogainingPointTotal";
@ -2462,6 +2463,7 @@ void MetaList::initSymbols() {
filterToSymbol[EFilterAPIEntry] = "EFilterAPIEntry"; filterToSymbol[EFilterAPIEntry] = "EFilterAPIEntry";
filterToSymbol[EFilterWrongFee] = "EFilterWrongFee"; filterToSymbol[EFilterWrongFee] = "EFilterWrongFee";
filterToSymbol[EFilterIncludeNotParticipating] = "EFilterIncludeNotParticipating"; filterToSymbol[EFilterIncludeNotParticipating] = "EFilterIncludeNotParticipating";
filterToSymbol[EFilterModifiedCard] = "EFilterModifiedCard";
for (map<EFilterList, string>::iterator it = filterToSymbol.begin(); for (map<EFilterList, string>::iterator it = filterToSymbol.begin();
it != filterToSymbol.end(); ++it) { it != filterToSymbol.end(); ++it) {

View File

@ -5060,10 +5060,14 @@ void oClass::loadQualificationFinalScheme(const QualificationFinal& scheme) {
inst->synchronize(); inst->synchronize();
} }
synchronize(); synchronize();
set<int> base;
qf->getBaseClassInstances(base);
for (oRunner &r : oe->Runners) { for (oRunner &r : oe->Runners) {
if (r.getClassRef(false) == this) { if (r.getClassRef(false) == this) {
if (r.getLegNumber() == 0 && !base.count(r.getDCI().getInt("Heat")))
r.getDI().setInt("Heat", 0);
pTeam t = r.getTeam(); pTeam t = r.getTeam();
if (t == 0) { if (t == nullptr) {
t = oe->addTeam(r.getName(), r.getClubId(), getId()); t = oe->addTeam(r.getName(), r.getClubId(), getId());
t->setStartNo(r.getStartNo(), oBase::ChangeType::Update); t->setStartNo(r.getStartNo(), oBase::ChangeType::Update);
t->setRunner(0, &r, true); t->setRunner(0, &r, true);
@ -5084,6 +5088,8 @@ void oClass::updateFinalClasses(oRunner* causingResult, bool updateStartNumbers)
return; return;
assert(!causingResult || causingResult->Class == this); assert(!causingResult || causingResult->Class == this);
int causingLevel = causingResult ? causingResult->tLeg : 0;
//oe->gdibase.addStringUT(0, L"UF:" + getName() + L" for " + (causingResult ? causingResult->getName() : L"-")); //oe->gdibase.addStringUT(0, L"UF:" + getName() + L" for " + (causingResult ? causingResult->getName() : L"-"));
auto computeInstance = [this](const pRunner causingResult) { auto computeInstance = [this](const pRunner causingResult) {
if (causingResult) { if (causingResult) {
@ -5158,6 +5164,28 @@ void oClass::updateFinalClasses(oRunner* causingResult, bool updateStartNumbers)
bool hasRemaining = qualificatonFinal->hasRemainingClass(); bool hasRemaining = qualificatonFinal->hasRemainingClass();
GeneralResult gr; GeneralResult gr;
qualificatonFinal->prepareCalculations(); qualificatonFinal->prepareCalculations();
struct TotalLevelRes {
pRunner r;
int place;
int instance;
int orderPlace;
int numEqual;
TotalLevelRes(int instance, pRunner r, int place, int orderPlace, int numEqual) : r(r), instance(instance),
orderPlace(orderPlace), numEqual(numEqual), place(place) {}
TotalLevelRes(int instance, pRunner r) : r(r), instance(instance),
orderPlace(numeric_limits<int>::max()), numEqual(0),
place(numeric_limits<int>::max()) {}
bool operator<(const TotalLevelRes& other) const {
return orderPlace < other.orderPlace;
}
};
vector<vector<TotalLevelRes>> levelRes(maxDepth+1);
int maxLevel = 0; int maxLevel = 0;
for (int i = instance; i < limit; i++) { for (int i = instance; i < limit; i++) {
if (classSplit[i].empty()) if (classSplit[i].empty())
@ -5206,6 +5234,7 @@ void oClass::updateFinalClasses(oRunner* causingResult, bool updateStartNumbers)
int lastPlace = 0, orderPlace = 1; int lastPlace = 0, orderPlace = 1;
int numEqual = 0; int numEqual = 0;
for (size_t k = 0; k < classSplit[i].size(); k++) { for (size_t k = 0; k < classSplit[i].size(); k++) {
const auto &res = classSplit[i][k]->getTempResult(); const auto &res = classSplit[i][k]->getTempResult();
if (res.getStatus() == StatusOK) { if (res.getStatus() == StatusOK) {
@ -5215,14 +5244,28 @@ void oClass::updateFinalClasses(oRunner* causingResult, bool updateStartNumbers)
else else
numEqual = 0; numEqual = 0;
qualificatonFinal->provideQualificationResult(classSplit[i][k], i, orderPlace, numEqual); levelRes[thisLevel].emplace_back(i, classSplit[i][k], place, orderPlace, numEqual);
// qualificatonFinal->provideQualificationResult(classSplit[i][k], i, orderPlace, numEqual);
lastPlace = place; lastPlace = place;
} }
else if (hasRemaining && res.getStatus() != StatusUnknown) { else if (hasRemaining && res.getStatus() != StatusUnknown) {
qualificatonFinal->provideUnqualified(thisLevel, classSplit[i][k]); levelRes[thisLevel].emplace_back(i, classSplit[i][k]);
//qualificatonFinal->provideUnqualified(thisLevel, classSplit[i][k]);
}
orderPlace++;
}
} }
orderPlace++; for (int level = 0; level < levelRes.size(); level++) {
if (levelRes[level].empty())
continue;
stable_sort(levelRes[level].begin(), levelRes[level].end());
for (auto& res : levelRes[level]) {
if (res.orderPlace < numeric_limits<int>::max())
qualificatonFinal->provideQualificationResult(res.r, res.instance, res.orderPlace, res.numEqual);
else
qualificatonFinal->provideUnqualified(level, res.r);
} }
} }
@ -5339,7 +5382,7 @@ void oClass::updateFinalClasses(oRunner* causingResult, bool updateStartNumbers)
if (inst < nextLevelInstance) if (inst < nextLevelInstance)
continue; continue;
if (!cc.second && cc.first->tLeg > 0 && !qualifiedRunners.count(cc.first->getId())) { if (!cc.second && cc.first->tLeg > causingLevel && !qualifiedRunners.count(cc.first->getId())) {
auto di = cc.first->getDI(); auto di = cc.first->getDI();
int oldHeat = di.getInt("Heat"); int oldHeat = di.getInt("Heat");
if (oldHeat != 0 && !getVirtualClass(oldHeat)->lockedClassAssignment()) { if (oldHeat != 0 && !getVirtualClass(oldHeat)->lockedClassAssignment()) {

View File

@ -1213,7 +1213,9 @@ void oDataContainer::buildTableCol(Table *table)
bool oDataContainer::formatNumber(int nr, const oDataInfo &di, wchar_t bf[64]) const { bool oDataContainer::formatNumber(int nr, const oDataInfo &di, wchar_t bf[64]) const {
if (di.SubType == oISDate) { if (di.SubType == oISDate) {
if ((nr < 99999999 && nr % 10000 != 0) || nr == 0) if (nr == 0)
bf[0] = 0;
else if ((nr < 99999999 && nr % 10000 != 0) || nr == 0)
swprintf_s(bf, 64, L"%d-%02d-%02d", nr / (100 * 100), (nr / 100) % 100, nr % 100); swprintf_s(bf, 64, L"%d-%02d-%02d", nr / (100 * 100), (nr / 100) % 100, nr % 100);
else if (nr > 0 && nr < 9999) else if (nr > 0 && nr < 9999)
swprintf_s(bf, 64, L"%04d", nr / 10000); swprintf_s(bf, 64, L"%04d", nr / 10000);
@ -1224,7 +1226,9 @@ bool oDataContainer::formatNumber(int nr, const oDataInfo &di, wchar_t bf[64]) c
return true; return true;
} }
else if (di.SubType == oISDateOrYear) { else if (di.SubType == oISDateOrYear) {
if (nr > 9999 && nr % 10000 != 0) if (nr == 0)
bf[0] = 0;
else if (nr > 9999 && nr % 10000 != 0)
swprintf_s(bf, 64, L"%04d-%02d-%02d", nr / 10000, (nr / 100) % 100, nr % 100); swprintf_s(bf, 64, L"%04d-%02d-%02d", nr / 10000, (nr / 100) % 100, nr % 100);
else if (nr > 9999) else if (nr > 9999)
swprintf_s(bf, 64, L"%04d", nr / 10000); swprintf_s(bf, 64, L"%04d", nr / 10000);

View File

@ -34,6 +34,8 @@ class Table;
class InputInfo; class InputInfo;
enum CellType; enum CellType;
constexpr int MaxVarNameLength = 28;
class oDataDefiner { class oDataDefiner {
public: public:
virtual ~oDataDefiner() {} virtual ~oDataDefiner() {}
@ -53,7 +55,7 @@ public:
}; };
struct oDataInfo { struct oDataInfo {
char Name[28]; char Name[MaxVarNameLength];
int Index; int Index;
int Size; int Size;
int Type; int Type;
@ -70,7 +72,7 @@ struct oDataInfo {
}; };
struct oVariableInt { struct oVariableInt {
char name[20]; char name[MaxVarNameLength];
int *data32; int *data32;
__int64 *data64; __int64 *data64;
oVariableInt() : data32(0), data64(0) {name[0] = 0;} oVariableInt() : data32(0), data64(0) {name[0] = 0;}
@ -81,7 +83,7 @@ class oVariableString {
oVariableString(wchar_t *buff, int size) : data(buff), maxSize(size), strData(0), strIndex(-2) { name[0] = 0; } oVariableString(wchar_t *buff, int size) : data(buff), maxSize(size), strData(0), strIndex(-2) { name[0] = 0; }
oVariableString(vector<wstring> &vec) : data(0), maxSize(0), strData(&vec), strIndex(-1) { name[0] = 0; } oVariableString(vector<wstring> &vec) : data(0), maxSize(0), strData(&vec), strIndex(-1) { name[0] = 0; }
oVariableString(vector<wstring> &vec, int position) : data(0), maxSize(0), strData(&vec), strIndex(position) { name[0] = 0; } oVariableString(vector<wstring> &vec, int position) : data(0), maxSize(0), strData(&vec), strIndex(position) { name[0] = 0; }
char name[20]; char name[MaxVarNameLength];
bool store(const wchar_t *str); bool store(const wchar_t *str);
private: private:
wchar_t *data; wchar_t *data;

View File

@ -57,6 +57,8 @@
#include "TabSI.h" #include "TabSI.h"
#include "binencoder.h" #include "binencoder.h"
#include "image.h" #include "image.h"
#include "datadefiners.h"
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Construction/Destruction // Construction/Destruction
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -70,7 +72,7 @@
extern Image image; extern Image image;
//Version of database //Version of database
int oEvent::dbVersion = 94; int oEvent::dbVersion = 95;
bool oEvent::useSubSecond() const { bool oEvent::useSubSecond() const {
if (useSubsecondsVersion == dataRevision) if (useSubsecondsVersion == dataRevision)
@ -95,356 +97,6 @@ bool oEvent::useSubSecond() const {
return false; return false;
} }
class RelativeTimeFormatter : public oDataDefiner {
string name;
public:
RelativeTimeFormatter(const char *n) : name(n) {}
const wstring &formatData(const oBase *obj) const override {
int t = obj->getDCI().getInt(name);
if (t <= 0)
return makeDash(L"-");
return obj->getEvent()->getAbsTime(t);
}
pair<int, bool> setData(oBase *obj, const wstring &input, wstring &output, int inputId) const override {
int t = obj->getEvent()->getRelativeTime(input);
obj->getDI().setInt(name.c_str(), t);
output = formatData(obj);
return make_pair(0, false);
}
int addTableColumn(Table *table, const string &description, int minWidth) const override {
return table->addColumn(description, max(minWidth, 90), false, true);
}
};
class AbsoluteTimeFormatter : public oDataDefiner {
string name;
public:
AbsoluteTimeFormatter(const char *n) : name(n) {}
const wstring &formatData(const oBase *obj) const override {
int t = obj->getDCI().getInt(name);
return formatTime(t);
}
pair<int, bool> setData(oBase *obj, const wstring &input, wstring &output, int inputId) const override {
int t = convertAbsoluteTimeMS(input);
if (t == NOTIME)
t = 0;
obj->getDI().setInt(name.c_str(), t);
output = formatData(obj);
return make_pair(0, false);
}
int addTableColumn(Table *table, const string &description, int minWidth) const override {
return table->addColumn(description, max(minWidth, 90), false, true);
}
};
class PayMethodFormatter : public oDataDefiner {
mutable vector< pair<wstring, size_t> > modes;
mutable map<wstring, int> setCodes;
mutable long rev;
public:
PayMethodFormatter() : rev(-1) {}
void prepare(oEvent *oe) const override {
oe->getPayModes(modes);
for (size_t i = 0; i < modes.size(); i++) {
setCodes[canonizeName(modes[i].first.c_str())] = modes[i].second;
}
}
const wstring &formatData(const oBase *ob) const override {
if (ob->getEvent()->getRevision() != rev)
prepare(ob->getEvent());
int p = ob->getDCI().getInt("Paid");
if (p == 0)
return lang.tl("Faktura");
else {
int pm = ob->getDCI().getInt("PayMode");
for (size_t i = 0; i < modes.size(); i++) {
if (modes[i].second == pm)
return modes[i].first;
}
return _EmptyWString;
}
}
pair<int, bool> setData(oBase *ob, const wstring &input, wstring &output, int inputId) const override {
auto res = setCodes.find(canonizeName(input.c_str()));
if (res != setCodes.end()) {
ob->getDI().setInt("PayMode", res->second);
}
output = formatData(ob);
return make_pair(0, false);
}
int addTableColumn(Table *table, const string &description, int minWidth) const override {
return table->addColumn(description, max(minWidth, 90), true, true);
}
};
class StartGroupFormatter : public oDataDefiner {
mutable long rev = -1;
mutable map<int, wstring> sgmap;
mutable wstring out;
int static getGroup(const oBase *ob) {
const oRunner *r = dynamic_cast<const oRunner *>(ob);
int sg = 0;
if (r)
sg = r->getStartGroup(false);
else {
const oClub *c = dynamic_cast<const oClub *>(ob);
if (c)
sg = c->getStartGroup();
}
return sg;
}
public:
StartGroupFormatter() {}
void prepare(oEvent *oe) const override {
auto &sg = oe->getStartGroups(true);
for (auto &g : sg) {
int t = g.second.firstStart;
sgmap[g.first] = oe->getAbsTimeHM(t);
}
}
const wstring &formatData(const oBase *ob) const override {
if (ob->getEvent()->getRevision() != rev)
prepare(ob->getEvent());
int sg = getGroup(ob);
if (sg > 0) {
auto res = sgmap.find(sg);
if (res != sgmap.end())
out = itow(sg) + L" (" + res->second + L")";
else
out = itow(sg) + L" (??)";
return out;
}
else
return _EmptyWString;
}
pair<int, bool> setData(oBase *ob, const wstring &input, wstring &output, int inputId) const override {
int g = inputId;
if (inputId <= 0 && !input.empty()) {
vector<wstring> sIn;
split(input, L" ", sIn);
for (wstring &in : sIn) {
int num = _wtoi(in.c_str());
if (in.find_first_of(':') != input.npos) {
int t = ob->getEvent()->convertAbsoluteTime(input);
if (t > 0) {
for (auto &sg : ob->getEvent()->getStartGroups(false)) {
if (sg.second.firstStart == t) {
g = sg.first;
break;
}
}
}
}
else if (sgmap.count(num)) {
g = num;
break;
}
}
}
oRunner *r = dynamic_cast<oRunner *>(ob);
if (r) {
r->setStartGroup(g);
}
else {
oClub *c = dynamic_cast<oClub *>(ob);
if (c)
c->setStartGroup(g);
}
output = formatData(ob);
return make_pair(0, false);
}
int addTableColumn(Table *table, const string &description, int minWidth) const override {
return table->addColumn(description, max(minWidth, 90), true, false);
}
// Return the desired cell type
CellType getCellType() const {
return CellType::cellSelection;
}
void fillInput(const oBase *obj, vector<pair<wstring, size_t>> &out, size_t &selected) const final {
if (obj->getEvent()->getRevision() != rev)
prepare(obj->getEvent());
int sg = getGroup(obj);
out.emplace_back(_EmptyWString, 0);
selected = 0;
for (auto &v : sgmap) {
out.emplace_back(v.second, v.first);
if (sg == v.first)
selected = sg;
}
}
};
class DataHider : public oDataDefiner {
public:
const wstring &formatData(const oBase *obj) const override {
return _EmptyWString;
}
pair<int, bool> setData(oBase *obj, const wstring &input, wstring &output, int inputId) const override {
return make_pair(0, false);
}
int addTableColumn(Table *table, const string &description, int minWidth) const override {
return -1;
}
};
class DataBoolean : public oDataDefiner {
string attrib;
public:
DataBoolean(const string &attrib) : attrib(attrib) {}
const wstring &formatData(const oBase *obj) const override {
int v = obj->getDCI().getInt(attrib);
return lang.tl(v ? "true[boolean]" : "false[boolean]");
}
pair<int, bool> setData(oBase *obj, const wstring &input, wstring &output, int inputId) const override {
bool v = compareStringIgnoreCase(L"true", input) == 0 || _wtoi64(input.c_str())>0;
if (!v) {
const wstring &T = lang.tl("true[boolean]");
v = compareStringIgnoreCase(T, input) == 0;
}
obj->getDI().setInt(attrib.c_str(), v);
output = formatData(obj);
return make_pair(0, false);
}
int addTableColumn(Table *table, const string &description, int minWidth) const override {
return table->addColumn(description, max(minWidth, 90), true, true);
}
};
class ResultModuleFormatter : public oDataDefiner {
public:
const wstring &formatData(const oBase *obj) const override {
return obj->getDCI().getString("Result");
}
pair<int, bool> setData(oBase *obj, const wstring &input, wstring &output, int inputId) const override {
string tag(input.begin(), input.end());
dynamic_cast<oClass &>(*obj).setResultModule(tag);
output = formatData(obj);
return make_pair(0, false);
}
int addTableColumn(Table *table, const string &description, int minWidth) const override {
return table->addColumn(description, max(minWidth, 90), false, true);
}
};
class SplitPrintListFormatter : public oDataDefiner {
public:
const wstring& formatData(const oBase* obj) const override {
wstring listId = obj->getDCI().getString("SplitPrint");
if (listId.empty()) {
return lang.tl("Standard");
}
try {
const MetaListContainer& lc = obj->getEvent()->getListContainer();
EStdListType type = lc.getCodeFromUnqiueId(gdioutput::narrow(listId));
const MetaList& ml = lc.getList(type);
return ml.getListName();
}
catch (meosException&) {
return _EmptyWString;
}
}
void fillInput(const oBase* obj, vector<pair<wstring, size_t>>& out, size_t& selected) const {
oEvent* oe = obj->getEvent();
oe->getListContainer().getLists(out, false, false, false, true);
out.insert(out.begin(), make_pair(lang.tl("Standard"), -10));
wstring listId = obj->getDCI().getString("SplitPrint");
EStdListType type = oe->getListContainer().getCodeFromUnqiueId(gdioutput::narrow(listId));
if (type == EStdListType::EStdNone)
selected = -10;
else {
for (auto& t : out) {
if (type == oe->getListContainer().getType(t.second)) {
selected = t.second;
break;
}
}
}
}
CellType getCellType() const final {
return CellType::cellSelection;
}
pair<int, bool> setData(oBase* obj, const wstring& input, wstring& output, int inputId) const override {
if (inputId == -10)
obj->getDI().setString("SplitPrint", L"");
else {
EStdListType type = obj->getEvent()->getListContainer().getType(inputId);
string id = obj->getEvent()->getListContainer().getUniqueId(type);
obj->getDI().setString("SplitPrint", gdioutput::widen(id));
}
output = formatData(obj);
return make_pair(0, false);
}
int addTableColumn(Table* table, const string& description, int minWidth) const override {
oEvent* oe = table->getEvent();
vector<pair<wstring, size_t>> out;
oe->getListContainer().getLists(out, false, false, false, true);
for (auto& t : out) {
minWidth = max<int>(minWidth, t.first.size() * 6);
}
return table->addColumn(description, max(minWidth, 90), false, true);
}
};
class AnnotationFormatter : public oDataDefiner {
mutable int numChar = 12;
public:
const wstring& formatData(const oBase* obj) const override {
const wstring &ws = obj->getDCI().getString("Annotation");
if (ws.empty())
return ws;
int pos = ws.find_first_of('@');
if (pos != wstring::npos)
return limitText(ws.substr(pos+1), numChar);
return limitText(ws, numChar);
}
bool canEdit() const override {
return false;
}
pair<int, bool> setData(oBase* obj, const wstring& input, wstring& output, int inputId) const override {
return make_pair(0, false);
}
int addTableColumn(Table* table, const string& description, int minWidth) const override {
numChar = minWidth / 5;
return table->addColumn(description, max(minWidth, 90), false, true);
}
};
oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi)
{ {
readOnly = false; readOnly = false;
@ -611,7 +263,7 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi)
oRunnerData->addVariableCurrency("Taxable", "Skattad avgift"); oRunnerData->addVariableCurrency("Taxable", "Skattad avgift");
oRunnerData->addVariableInt("BirthYear", oDataContainer::oISDateOrYear, "RunnerBirthDate"); oRunnerData->addVariableInt("BirthYear", oDataContainer::oISDateOrYear, "RunnerBirthDate");
oRunnerData->addVariableString("Bib", 8, "Nummerlapp").zeroSortPadding = 5; oRunnerData->addVariableString("Bib", 8, "Nummerlapp").zeroSortPadding = 5;
oRunnerData->addVariableInt("Rank", oDataContainer::oIS16U, "Ranking"); oRunnerData->addVariableInt("Rank", oDataContainer::oIS32, "Ranking", make_shared<RankScoreFormatter>());
oRunnerData->addVariableDate("EntryDate", "Anm. datum"); oRunnerData->addVariableDate("EntryDate", "Anm. datum");
oRunnerData->addVariableInt("EntryTime", oDataContainer::oISTime, "Anm. tid", make_shared<AbsoluteTimeFormatter>("EntryTime")); oRunnerData->addVariableInt("EntryTime", oDataContainer::oISTime, "Anm. tid", make_shared<AbsoluteTimeFormatter>("EntryTime"));
@ -3589,82 +3241,138 @@ void oEvent::generateMinuteStartlist(gdioutput &gdi) {
gdi.addStringUT(fontMedium, bf); gdi.addStringUT(fontMedium, bf);
} }
vector< vector< vector<pRunner> > > sb; vector<vector<vector<const oRunner *>>> sb;
sb.reserve(Runners.size()); sb.reserve(Runners.size() / 5+10);
int LastStartTime=-1; int lastStartTime = -1;
for (oRunnerList::iterator it=Runners.begin(); it != Runners.end(); ++it) {
if (it->Class && it->Class->getBlock() != blocks[k]) for (const auto &r : Runners) {
if (r.isRemoved())
continue; continue;
if (it->Class && it->Class->getStart() != starts[k]) if (r.Class && r.Class->getBlock() != blocks[k])
continue; continue;
if (!it->Class && blocks[k]!=0) if (r.Class && r.Class->getStart() != starts[k])
continue; continue;
if (it->getStatus() == StatusNotCompetiting || it->getStatus() == StatusCANCEL) if (!r.Class && blocks[k] != 0)
continue;
if (r.getStatus() == StatusNotCompetiting || r.getStatus() == StatusCANCEL)
continue; continue;
if (LastStartTime!=it->tStartTime) { if (lastStartTime != r.tStartTime) {
sb.resize(sb.size() + 1); sb.emplace_back();
LastStartTime = it->tStartTime; lastStartTime = r.tStartTime;
} }
if (sb.empty()) if (sb.empty())
sb.resize(1); sb.resize(1);
if (it->tInTeam == 0) if (r.tInTeam == 0)
sb.back().push_back(vector<pRunner>(1, &*it)); sb.back().push_back(vector<const oRunner *>(1, &r));
else { else {
if (it->legToRun() > 0 && it->getStartTime() == 0) if (r.legToRun() > 0 && r.getStartTime() == 0)
continue; continue;
int minIx = 10000; int minIx = 10000;
for (int j = 0; j < it->tInTeam->getNumRunners(); j++) { for (int j = 0; j < r.tInTeam->getNumRunners(); j++) {
if (j != it->tLeg && if (j != r.tLeg && r.tInTeam->Runners[j] && r.tInTeam->Runners[j]->tStartTime == r.tStartTime)
it->tInTeam->Runners[j] &&
it->tInTeam->Runners[j]->tStartTime == it->tStartTime)
minIx = min(minIx, j); minIx = min(minIx, j);
} }
if (minIx == 10000) if (minIx == 10000)
sb.back().push_back(vector<pRunner>(1, &*it)); // Single runner on this start time sb.back().push_back(vector<const oRunner *>(1, &r)); // Single runner on this start time
else if (minIx > it->tLeg) { else if (minIx > r.tLeg) {
sb.back().push_back(vector<pRunner>()); sb.back().emplace_back();
for (int j = 0; j < it->tInTeam->getNumRunners(); j++) { for (int j = 0; j < r.tInTeam->getNumRunners(); j++) {
if (it->tInTeam->Runners[j] && if (r.tInTeam->Runners[j] && r.tInTeam->Runners[j]->tStartTime == r.tStartTime)
it->tInTeam->Runners[j]->tStartTime == it->tStartTime) sb.back().back().push_back(r.tInTeam->Runners[j]);
sb.back().back().push_back(it->tInTeam->Runners[j]);
} }
} }
} }
} }
lastStartTime = -1;
map<int, int> startIntervalCount;
int totalStartTimes = 0;
for (size_t k = 0; k < sb.size(); k++) {
if (!sb[k].empty()) {
int st = sb[k][0][0]->getStartTime();
if (lastStartTime != -1 && lastStartTime != st) {
int startInterval = st - lastStartTime;
++startIntervalCount[startInterval];
totalStartTimes++;
lastStartTime = st;
}
lastStartTime = st;
}
}
int typicalStartInterval = 0;
int maxStartIntervalCount = 0;
for (auto& sic : startIntervalCount) {
if (sic.second > maxStartIntervalCount) {
maxStartIntervalCount = sic.second;
typicalStartInterval = sic.first;
}
}
int startInterval = -1;
if (maxStartIntervalCount > totalStartTimes / 4) {
startInterval = typicalStartInterval;
}
y = gdi.getCY(); y = gdi.getCY();
lastStartTime = -1;
for (size_t k = 0; k < sb.size(); k++) { for (size_t k = 0; k < sb.size(); k++) {
if (sb[k].empty()) if (sb[k].empty())
continue; continue;
const int st = sb[k][0][0]->getStartTime();
if (startInterval > 0 && lastStartTime > 0 && st - lastStartTime > startInterval) {
int missingStartCount = (st - lastStartTime) / startInterval;
if (st == lastStartTime + missingStartCount * startInterval) {
lastStartTime += startInterval;
int count = 0;
while (lastStartTime < st) {
if (++count < 2) {
y += lh / 2;
gdi.addStringUT(y, x + dx[0], boldText, getAbsTime(lastStartTime));
y += lh;
gdi.addStringUT(y, x + dx[1], fontMedium, L"\u2014");
y += lh;
}
lastStartTime += startInterval;
}
}
}
lastStartTime = st;
y+=lh/2; y+=lh/2;
gdi.addStringUT(y, x+dx[0], boldText, sb[k][0][0]->getStartTimeS()); if (st > 0) {
y+=lh; gdi.addStringUT(y, x + dx[0], boldText, sb[k][0][0]->getStartTimeS());
y += lh;
}
for (size_t j = 0; j < sb[k].size(); j++) { for (size_t j = 0; j < sb[k].size(); j++) {
const int src_y = y; const int src_y = y;
int indent = 0; int indent = 0;
const vector<pRunner> &r = sb[k][j]; const auto &rList = sb[k][j];
if (r.size() == 1) { if (rList.size() == 1) {
if (r[0]->getCardNo()>0) if (rList[0]->getCardNo()>0)
gdi.addStringUT(y, x+dx[0], fontMedium, itos(r[0]->getCardNo())); gdi.addStringUT(y, x+dx[0], fontMedium, itos(rList[0]->getCardNo()));
wstring name; wstring name;
if (r[0]->getBib().empty()) if (rList[0]->getBib().empty())
name = r[0]->getName(); name = rList[0]->getName();
else else
name = r[0]->getName() + L" (" + r[0]->getBib() + L")"; name = rList[0]->getName() + L" (" + rList[0]->getBib() + L")";
gdi.addStringUT(y, x+dx[1], fontMedium, name, dx[2]-dx[1]-4); gdi.addStringUT(y, x+dx[1], fontMedium, name, dx[2]-dx[1]-4);
} }
else { else {
wstring name; wstring name;
if (!r[0]->tInTeam->getBib().empty()) if (!rList[0]->tInTeam->getBib().empty())
name = r[0]->tInTeam->getBib() + L": "; name = rList[0]->tInTeam->getBib() + L": ";
int nnames = 0; int nnames = 0;
for (size_t i = 0; i < r.size(); i++) { for (size_t i = 0; i < rList.size(); i++) {
if (nnames>0) if (nnames>0)
name += L", "; name += L", ";
nnames++; nnames++;
@ -3677,21 +3385,20 @@ void oEvent::generateMinuteStartlist(gdioutput &gdi) {
indent = gdi.scaleLength(20); indent = gdi.scaleLength(20);
} }
name += r[i]->getName(); name += rList[i]->getName();
if (r[i]->getCardNo()>0) { if (rList[i]->getCardNo()>0)
name += L" (" + itow(r[i]->getCardNo()) + L")"; name += L" (" + itow(rList[i]->getCardNo()) + L")";
}
} }
gdi.addStringUT(y, x+dx[0]+indent, fontMedium, name, dx[2]-dx[0]-4-indent); gdi.addStringUT(y, x+dx[0]+indent, fontMedium, name, dx[2]-dx[0]-4-indent);
} }
gdi.addStringUT(src_y, x+dx[2], fontMedium, r[0]->getClub(), dx[3]-dx[2]-4); gdi.addStringUT(src_y, x+dx[2], fontMedium, rList[0]->getClub(), dx[3]-dx[2]-4);
gdi.addStringUT(src_y, x+dx[3], fontMedium, r[0]->getClass(true)); gdi.addStringUT(src_y, x+dx[3], fontMedium, rList[0]->getClass(true));
y+=lh; y+=lh;
} }
} }
} }
gdi.refresh(); gdi.refresh();
} }
@ -4784,14 +4491,14 @@ int oEvent::getFirstStart(int classId) const {
return minTime; return minTime;
} }
bool oEvent::hasRank() const bool oEvent::hasRank() const {
{ for (auto &r : Runners){
oRunnerList::const_iterator it; if (!r.isRemoved()) {
int rank = r.getDCI().getInt("Rank");
for (it=Runners.begin(); it != Runners.end(); ++it){ if (rank > 0 && rank < MaxOrderRank)
if (it->getDCI().getInt("Rank")>0)
return true; return true;
} }
}
return false; return false;
} }

View File

@ -50,6 +50,9 @@
#define cVacantId 888888888 #define cVacantId 888888888
#define cNoClubId 999999999 #define cNoClubId 999999999
constexpr int MaxOrderRank = 20000;
constexpr int MaxRankingConstant = numeric_limits<int>::max();
class MeOSFileLock; class MeOSFileLock;
class RunnerDB; class RunnerDB;
class gdioutput; class gdioutput;
@ -188,11 +191,6 @@ struct BackupInfo : public CompetitionInfo {
class oListInfo; class oListInfo;
class MetaListContainer; class MetaListContainer;
typedef bool (__cdecl* ERRORMESG_FCN)(char *bf256);
typedef bool (__cdecl* OPENDB_FCN)(void);
typedef int (__cdecl* SYNCHRONIZE_FCN)(oBase *obj);
typedef bool (__cdecl* SYNCHRONIZELIST_FCN)(oBase *obj, int lid);
enum class oListId {oLRunnerId=1, oLClassId=2, oLCourseId=4, enum class oListId {oLRunnerId=1, oLClassId=2, oLCourseId=4,
oLControlId=8, oLClubId=16, oLCardId=32, oLControlId=8, oLClubId=16, oLCardId=32,
oLPunchId=64, oLTeamId=128, oLEventId=256}; oLPunchId=64, oLTeamId=128, oLEventId=256};

View File

@ -45,8 +45,6 @@
#include "meos.h" #include "meos.h"
#include <cassert> #include <cassert>
typedef bool (__cdecl* OPENDB_FCN)(void);
typedef int (__cdecl* SYNCHRONIZE_FCN)(oBase *obj);
extern Image image; extern Image image;

View File

@ -292,7 +292,7 @@ bool oEvent::exportOECSV(const wchar_t *file, const set<int>& classes, int langu
if (it->tStartTime > 0 && t > 0 && t > it->tStartTime) if (it->tStartTime > 0 && t > 0 && t > it->tStartTime)
row.push_back(gdibase.recodeToNarrow(formatTimeHMS(t - it->tStartTime))); row.push_back(gdibase.recodeToNarrow(formatTimeHMS(t - it->tStartTime)));
else else
return "-----"; row.push_back("-----");
} }
} }

View File

@ -1883,16 +1883,16 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara
break; break;
case lRunnerRank: case lRunnerRank:
if (r) { if (r) {
int rank=r->getDCI().getInt("Rank"); int rank = r->getRanking();
if (rank == 0) { if (rank>0 && rank < MaxOrderRank)
if (r->tParentRunner)
rank = r->tParentRunner->getDCI().getInt("Rank");
}
if (rank>0)
wcscpy_s(wbf, formatRank(rank).c_str()); wcscpy_s(wbf, formatRank(rank).c_str());
} }
break; break;
case lRunnerRankScore:
if (r) {
wcscpy_s(wbf, r->getRankingScore().c_str());
}
break;
case lRunnerBib: case lRunnerBib:
if (r) { if (r) {
const wstring &bib = r->getBib(); const wstring &bib = r->getBib();
@ -3029,6 +3029,11 @@ bool oListInfo::filterRunner(const oRunner &r) const {
return true; return true;
} }
if (filter(EFilterModifiedCard)) {
if (!r.getCard() || r.getCard()->isOriginalCard() != oCard::PunchOrigin::Manual)
return true;
}
if (filter(EFilterRentCard) && !r.isRentalCard()) if (filter(EFilterRentCard) && !r.isRentalCard())
return true; return true;
@ -3290,6 +3295,16 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form
return false; return false;
} }
if (li.filter(EFilterModifiedCard)) {
bool modified = false;
for (pRunner r : it->Runners) {
if (r && r->getCard() && r->getCard()->isOriginalCard() == oCard::PunchOrigin::Manual)
modified = true;
}
if (modified)
return false;
}
if (li.filter(EFilterHasResult)) { if (li.filter(EFilterHasResult)) {
if (gResult) { if (gResult) {
RunnerStatus st = it->getTempResult(0).getStatus(); RunnerStatus st = it->getTempResult(0).getStatus();

View File

@ -105,6 +105,7 @@ enum EPostType {
lRunnerBib, lRunnerBib,
lRunnerStartNo, lRunnerStartNo,
lRunnerRank, lRunnerRank,
lRunnerRankScore,
lRunnerCourse, lRunnerCourse,
lRunnerRogainingPoint, lRunnerRogainingPoint,
lRunnerRogainingPointTotal, lRunnerRogainingPointTotal,
@ -305,6 +306,7 @@ enum EFilterList
EFilterAPIEntry, // Entry via API EFilterAPIEntry, // Entry via API
EFilterWrongFee, EFilterWrongFee,
EFilterIncludeNotParticipating, EFilterIncludeNotParticipating,
EFilterModifiedCard,
_EFilterMax _EFilterMax
}; };

View File

@ -617,6 +617,17 @@ void oRunner::setClassId(int id, bool isManualUpdate) {
return; return;
} }
if (nPc && isManualUpdate && nPc->isQualificationFinalBaseClass() && nPc != Class) {
int h = getDI().getInt("Heat"); // Clear heat if not a base class
if (h != 0) {
set<int> base;
nPc->getQualificationFinal()->getBaseClassInstances(base);
if (!base.count(h))
getDI().setInt("Heat", 0);
}
}
if (tParentRunner) { if (tParentRunner) {
assert(!isManualUpdate); // Do not support! This may be destroyed if calling tParentRunner->setClass assert(!isManualUpdate); // Do not support! This may be destroyed if calling tParentRunner->setClass
return; return;
@ -685,16 +696,15 @@ void oRunner::setClassId(int id, bool isManualUpdate) {
} }
} }
void oRunner::setCourseId(int id) void oRunner::setCourseId(int id) {
{ pCourse pc = Course;
pCourse pc=Course;
if (id>0) if (id > 0)
Course=oe->getCourse(id); Course = oe->getCourse(id);
else else
Course=0; Course = nullptr;
if (Course!=pc) { if (Course != pc) {
updateChanged(); updateChanged();
if (Class) if (Class)
getClassRef(true)->clearSplitAnalysis(); getClassRef(true)->clearSplitAnalysis();
@ -7005,6 +7015,28 @@ int oRunner::getRanking() const {
return rank; return rank;
} }
wstring oRunner::getRankingScore() const {
int raw = getDCI().getInt("Rank");
wchar_t wbf[32] = { 0 };
if (raw > MaxOrderRank) {
constexpr int TurnAround = MaxOrderRank * 100000;
double score = double(TurnAround - raw)/100;
if (score > 0 && score < 10000) {
swprintf_s(wbf, L"%.2f", score);
}
}
return wbf;
}
void oRunner::setRankingScore(double score) {
int rank = 0;
if (score > -10 && score < 10000) {
constexpr int TurnAround = MaxOrderRank * 100000;
rank = TurnAround - int(score * 100);
}
getDI().setInt("Rank", rank);
}
void oAbstractRunner::hasManuallyUpdatedTimeStatus() { void oAbstractRunner::hasManuallyUpdatedTimeStatus() {
if (Class && Class->hasClassGlobalDependence()) { if (Class && Class->hasClassGlobalDependence()) {
set<int> cls; set<int> cls;

View File

@ -116,7 +116,6 @@ typedef const oTeam* cTeam;
struct SICard; struct SICard;
const int MaxRankingConstant = 99999999;
class oAbstractRunner : public oBase { class oAbstractRunner : public oBase {
protected: protected:
@ -850,7 +849,9 @@ public:
void markClassChanged(int controlId); void markClassChanged(int controlId);
int getRanking() const; int getRanking() const final;
wstring getRankingScore() const;
void setRankingScore(double score);
bool isResultUpdated(bool totalResult) const override; bool isResultUpdated(bool totalResult) const override;

View File

@ -2674,3 +2674,126 @@ Bevara höjd/bredd-relationen = Preservar proporção
RunnerLegTeamLeaderName = Primeiro competidor da equipe a terminar a perna RunnerLegTeamLeaderName = Primeiro competidor da equipe a terminar a perna
info:offsetclassid = Se você importar inscrições e categorias de fontes diferentes para a mesma competição, pode acontecer que haja conflitos nos números de ID das categorias. Para separar as categorias, você pode inserir um deslocamento para o ID da categoria ao trabalhar com arquivos de uma fonte específica; isso será adicionado aos números de Id.\n\nVocê deve especificar o mesmo deslocamento cada vez que importar dessa fonte.\n\n Um número adequado pode ser 1000 (que funcionará desde que cada Id seja menor que 1000). info:offsetclassid = Se você importar inscrições e categorias de fontes diferentes para a mesma competição, pode acontecer que haja conflitos nos números de ID das categorias. Para separar as categorias, você pode inserir um deslocamento para o ID da categoria ao trabalhar com arquivos de uma fonte específica; isso será adicionado aos números de Id.\n\nVocê deve especificar o mesmo deslocamento cada vez que importar dessa fonte.\n\n Um número adequado pode ser 1000 (que funcionará desde que cada Id seja menor que 1000).
Förskjutning av klassers Id = ID da categoria de corte Förskjutning av klassers Id = ID da categoria de corte
Aktivera kioskläge = Ativar modo quiosque
Alla okvalificerade = Qualquer não qualificado
Alla övriga = Todos os restantes
Anslut en SI-enhet och aktivera den = Conectar e ativar uma unidade SI
Antal nivåer = Número de níveis
Använd ranking istället för placering i kval för att placera kvalificerade löpare i klasser = Usar ranking em vez de colocação para distribuir competidores qualificados em categorias
Avgiftshöjning två (procent) = Segundo aumento de taxa (por cento)
Avläsning = Leitura
Avstånd mellan förslag (minuter) = Distância entre sugestões (minutos)
Boka starttid = Solicitar horário de início
Bästa tid = Melhor tempo
ClassDataA = Dados da categoria A
ClassDataB = Dados da categoria B
ClassTextA = Texto da categoria
CourseNumControls = Número de controles
Datortid = Hora do computador
Extra data X = Dados extras X
Extra datafält = Campos de dados extras
Final = Final
Födelsedatum = Data de nascimento
För kommunikationstest kan man använda en separat testtävling = Para testar comunicação, pode-se usar uma competição de teste separada
Individual Rogaining Split Print = Impressão de divisão de cross-country individual
Ingen ledig starttid kunde hittas = Não foi encontrado horário de início livre
Inmatning Testning = Teste de entrada
Klass / placering = Categoria / posição
Klass X (namnsuffix) = Categoria X (sufixo do nome)
Klass efter ranking = Selecionar categoria por ranking
Klassen lottas inte, boka starttid = Lista de largada não foi sorteada, solicite horário de partida na página SportIdent
Klassen tillåter ej val av starttid = A categoria não permite solicitação de horário de partida
Kommentar = Comentário
Kommentarer = Comentários
Kval = Qualificação
Kvalificeringsregler för X = Regras de qualificação para X
Kvalklass = Categoria de qualificação
Ladda = Carregar
Lap Count = Contagem de voltas
Lap count with extra punch = Contagem de voltas com picote extra
Live results, radio = Resultados ao vivo, rádio
Lägg till eller redigera kommentarer om deltagaren = Adicionar ou editar comentários sobre o competidor
Lägg till regler = Adicionar regras
Manuellt startönskemål = Pedido de início manual
Manuellt ändrad brickdata = Dados do chip alterados manualmente
Manuellt ändrad stämpling = Dados alterados manualmente
Minsta tid till start (minuter) = Menor tempo até o início (minutos)
N bästa = N melhores
Nivå X = Nível X
Observera att stämplingar före tävlingens nolltid inte kan hämtas = Observação: O MeOS não pode buscar picotes antes do tempo zero da competição
Oförändrad brickdata = Dados originais do chip
Okänd ursrpungsstatus = Status de origem desconhecido
Omstart: X = Reinício: X
Patrol results = Resultados de patrulha
Patrol, start list = Patrulha, lista de largada
Placering X kvalificerar till Y = Posição X qualifica para Y
Placeringar = Colocações
Primärt = Primário
PunchControlPlaceTeamAcc = Posição total da equipe no controle
PunchTeamTime = Tempo total da equipe e divisão no controle
PunchTeamTotalNamedTime = Tempo total da equipe nomeado
PunchTeamTotalTime = Tempo total da equipe para o controle
PunchTeamTotalTimeAfter = Tempo da equipe após o controle
Regler = Regras
Reptid: X = Tempo de corda: X
Resultatlista, Rogaining, lag = Resultados, cross-country, equipe
RunnerDataA = Dados do competidor A
RunnerDataB = Dados do competidor B
RunnerRentalCard = Chip de aluguel
RunnerTextA = Texto do competidor
Sekundärt = Secundário
Sida = Página
Simulerar starttidstilldelning för X deltagare = Simulando atribuição de horário de início para X competidores
Simulering = Simulação
Sista starttid = Último horário de início
Start på signal = Iniciar no sinal
Starta nu = Iniciar agora
Startintervall (minuter) = Intervalo de início (minutos)
Startlista, lag = Lista de largada, equipe
Starttiden är låst = O horário de início está bloqueado
Starttiden är redan tilldelad = O horário de início já foi atribuído
Stoppdatum två = Segunda data de encerramento
Stämpla för välja starttid = Picote para escolher um horário de início
Sätt tiden = Definir o tempo
Team Rogaining Split Print = Impressão de divisão de cross-country em equipe
TeamDataA = Dados da equipe A
TeamDataB = Dados da equipe B
TeamTextA = Texto da equipe
Testning = Teste
Tiden har passerat sista tillåtna starttid = O tempo passou o último horário de partida permitido
Tidsgräns X = Limite de tempo X
Tidskval = Qualificação de tempo
Tilldela nummerlapp till vakanter = Atribuir números a vagas
Tillåt deltagare med samma klubb och klass på närliggande starttid = Permitir competidores do mesmo clube e categoria no próximo horário de partida
Tillåt klass med samma bana på samma starttid = Permitir categoria com o mesmo percurso no mesmo horário de partida
Tillåt klass med samma bana på stattid före/efter = Permitir categoria com o mesmo percurso antes e depois
Tillåt klass med samma första kontroll vid samma starttid = Permitir primeira marcação no mesmo horário de partida dentro da mesma categoria
Ursprunglig tid: X = Tempo original: X
Use in X = Usar em X
Vill du starta automaten? = Deseja iniciar a máquina?
Välj från flera förslag = Escolher entre várias sugestões
Välj starttid för X = Escolher horário de partida para X
Välj typ av ID att exportera = Selecionar tipos de ID para exportar
Välj vilken tid du vill sätta = Escolher a hora que deseja definir
X bästa = X melhores
X bästa okvalificerade = X melhores não qualificados
X kvalificerade = X qualificados
X: Startid för Y kl Z = X: Horário de partida para Y na Z
ask:simulatestart = Realize uma simulação com as configurações que você fez (categoria, profundidade de início, distância, regras) e com os participantes registrados que estão nas categorias. A competição não é afetada. O resultado mostra se é razoável que todos tenham tempo para começar dentro da profundidade de início definida e quanto tempo extra de espera pode ser necessário em diferentes categorias.\n\nDepois, você também pode salvar uma cópia da competição com os horários de partida simulados para estudar, por exemplo, a lista de horários de partida em minutos.\n\nVocê deseja executar a simulação?
ask:startkiosk = Se você iniciar um quiosque de reserva de horário de partida, o MeOS entra em um estado onde nenhuma outra operação é suportada ou configurações podem ser alteradas. Você precisa de uma unidade SI conectada e ativada.\n\nVocê deve considerar proteger o banco de dados com senha se estiver expondo um quiosque.\n\nVocê deseja iniciar o modo quiosque?
help:requeststart = Solicitar horário de partida é algo entre um horário de partida livre e sorteado. Ao solicitar um horário de partida, o MeOS garante que haja uma certa distância entre os participantes na mesma categoria ou no mesmo percurso. Se você tiver um horário de partida solicitado, não é necessário usar um chip de partida.\n\nDefina parâmetros para a profundidade de partida e quantos/quem podem começar ao mesmo tempo. A distância do local onde a reserva é feita até a partida, determina qual é um valor mínimo de tempo de partida apropriado.\n\nVocê pode permitir que os participantes obtenham automaticamente o primeiro horário de partida disponível após o tempo mínimo de partida, ou permitir que escolham entre vários horários de partida possíveis. É possível e adequado imprimir um certificado de partida para levar consigo para a partida.\n\nSe você estiver usando um serviço de resultados ao vivo, o horário de partida provavelmente será exibido lá quando reservado.
help_second_fee = Use uma segunda taxa aumentada para definir uma taxa mais alta para inscrições no dia da competição (por exemplo). A taxa é cobrada a partir da data especificada. A porcentagem é relativa à taxa regular
prefsExpStaFilename = Exportar lista de partida
prefsRequestClubNeighbour = Solicitar partida: Permitir vizinho do mesmo clube
prefsRequestLastStart = Solicitar partida: Último partida
prefsRequestMaxParallel = Solicitar partida: Máximo de inícios paralelos
prefsRequestMinTime = Solicitar partida: Tempo para começar
prefsRequestProvideSuggestion = Solicitar partida: Fornecer sugestões
prefsRequestSameCourse = Solicitar partida: Mesmo percurso
prefsRequestSameCourseNeighbour = Solicitar partida: Mesmo percurso para vizinho
prefsRequestSameFirstControl = Solicitar partida: Mesma primeira marcação
prefsRequestStartInterval = Solicitar partida: Intervalo
prefsRequestSuggestInterval = Solicitar partida: Tempo entre sugestões
prevsExpResFilename = Exportar resultados
Önskad starttid = Horário de partida desejado
Övriga okvalificerade = Não qualificados restantes

View File

@ -69,7 +69,8 @@ pair<int, int> QualificationFinal::getPrelFinalFromPlace(int instance, int order
bool QualificationFinal::noQualification(int instance) const { bool QualificationFinal::noQualification(int instance) const {
if (size_t(instance) >= classDefinition.size()) if (size_t(instance) >= classDefinition.size())
return false; return false;
if (instance > 0 && classDefinition[instance].level > classDefinition[instance - 1].level)
return false; // Second level
return classDefinition[instance].qualificationMap.empty() && return classDefinition[instance].qualificationMap.empty() &&
classDefinition[instance].numTimeQualifications == 0 && classDefinition[instance].numTimeQualifications == 0 &&
classDefinition[instance].extraQualification == QFClass::ExtraQualType::None; classDefinition[instance].extraQualification == QFClass::ExtraQualType::None;
@ -774,7 +775,7 @@ void QualificationFinal::printScheme(const oClass& cls, gdioutput &gdi) const {
gdi.dropLine(0.2); gdi.dropLine(0.2);
int rankingBased = 0; int rankingBased = 0;
vector<wstring> dst; vector<wstring> dst;
int misCntLimit = 0;
for (int place = 1; place < 100; place++) { for (int place = 1; place < 100; place++) {
auto res = sourcePlaceToFinalOrder.find(make_pair(i + 1, place)); auto res = sourcePlaceToFinalOrder.find(make_pair(i + 1, place));
@ -785,7 +786,10 @@ void QualificationFinal::printScheme(const oClass& cls, gdioutput &gdi) const {
else else
dst.push_back(itow(place) + L". ➞ " + cname[res->second.first - 1]); dst.push_back(itow(place) + L". ➞ " + cname[res->second.first - 1]);
} }
else break; else {
if (++misCntLimit > 10)
break;
}
} }
if (rankingBased) { if (rankingBased) {

View File

@ -2701,7 +2701,7 @@ Minsta tid till start (minuter) = Minsta tid till start (minuter)
Sista starttid = Sista starttid Sista starttid = Sista starttid
Startintervall (minuter) = Startintervall (minuter) Startintervall (minuter) = Startintervall (minuter)
Tillåt klass med samma bana på samma starttid = Tillåt klass med samma bana på samma starttid Tillåt klass med samma bana på samma starttid = Tillåt klass med samma bana på samma starttid
Tillåt klass med samma bana på starttid före/efter = Tillåt klass med samma bana på stattid före/efter Tillåt klass med samma bana på starttid före/efter = Tillåt klass med samma bana på starttid före/efter
Tillåt klass med samma första kontroll vid samma starttid = Tillåt klass med samma första kontroll vid samma starttid Tillåt klass med samma första kontroll vid samma starttid = Tillåt klass med samma första kontroll vid samma starttid
Tiden har passerat sista tillåtna starttid = Tiden har passerat sista tillåtna starttid Tiden har passerat sista tillåtna starttid = Tiden har passerat sista tillåtna starttid
Starttiden är låst = Starttiden är låst Starttiden är låst = Starttiden är låst
@ -2809,3 +2809,17 @@ Lägg till eller redigera kommentarer om laget = Lägg till eller redigera komme
Manuellt ändrad brickdata = Manuellt ändrad brickdata Manuellt ändrad brickdata = Manuellt ändrad brickdata
Efteranmälan = Efteranmälan Efteranmälan = Efteranmälan
Reducerad = Reducerad Reducerad = Reducerad
Kommentarer för X = Kommentarer för X
Lägg till eller redigera kommentarer om laget = Lägg till eller redigera kommentarer om laget
Layout = Layout
kolumner = kolumner
rader = rader
Modifierade resultat = Modifierade resultat
prefsRequestLastStat = prefsRequestLastStat
EFilterModifiedCard = Med modifierat resultat
Vill du uppdatera X med ändringarna? = Vill du uppdatera X med ändringarna?
Vill du spara en kopia av tävlingen med starttider för ytterligare analys? = Vill du spara en kopia av tävlingen med starttider för ytterligare analys?||||||| .r1334
RunnerRankScore = Ranking (poäng)
Radera alla = Radera alla
Vill du ta bort alla banor från tävlingen? = Vill du ta bort alla banor från tävlingen?
Tillåt klass med samma bana på stattid före/efter = Tillåt klass med samma bana på stattid före/efter

View File

@ -406,6 +406,10 @@ void TestMeOS::setFile(const string &file) const {
gdi_main->dbPushDialogAnswer("*" + file); gdi_main->dbPushDialogAnswer("*" + file);
} }
void TestMeOS::setFile(const wstring& file) const {
gdi_main->dbPushDialogAnswer("*" + gdioutput::narrow(file));
}
void TestMeOS::cleanup() const { void TestMeOS::cleanup() const {
} }

View File

@ -128,6 +128,7 @@ public:
void setAnswer(const char *ans) const; void setAnswer(const char *ans) const;
void setFile(const string &file) const; void setFile(const string &file) const;
void setFile(const wstring& file) const;
void checkString(const char *str, int count = 1) const; void checkString(const char *str, int count = 1) const;
void checkStringRes(const char *str, int count = 1) const; void checkStringRes(const char *str, int count = 1) const;

View File

@ -1 +0,0 @@
Patrol result = Patrol result