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="classconfiginfo.h" />
<ClInclude Include="csvparser.h" />
<ClInclude Include="datadefiners.h" />
<ClInclude Include="download.h" />
<ClInclude Include="gdiconstants.h" />
<ClInclude Include="gdifonts.h" />

View File

@ -534,6 +534,16 @@ bool MeosSQL::openDB(oEvent *oe)
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) {
con->query().exec("UNLOCK TABLES");
}

View File

@ -111,9 +111,8 @@ void RestService::settings(gdioutput &gdi, oEvent &oe, State state) {
else {
gdi.addString("", 0, "Server startad på X#" + itos(port));
auto per = server->getEntryPermission();
if (get<RestServer::EntryPermissionType>(per) != RestServer::EntryPermissionType::None)
if (get<RestServer::EntryPermissionType>(per) != RestServer::EntryPermissionType::None) {
disablePermisson = false;
else {
gdi.selectItemByData("PermissionPerson", size_t(get<RestServer::EntryPermissionType>(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);
string uniqueTag(const char *base);
void LoadPage(const string &name);
wstring getTempFile();
wstring getTempPath();
void removeTempFile(const wstring &file); // Delete a temporyary

View File

@ -77,11 +77,11 @@ protected:
TabBase * const tab = nullptr;
public:
const string name;
const wstring name;
const int imageId = -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;}

View File

@ -54,9 +54,6 @@ TabCourse::~TabCourse(void)
{
}
void LoadCoursePage(gdioutput &gdi);
void LoadClassPage(gdioutput &gdi);
void TabCourse::selectCourse(gdioutput &gdi, pCourse pc)
{
if (gdi.hasWidget("Rogaining")) {
@ -473,6 +470,29 @@ int TabCourse::courseCB(gdioutput &gdi, GuiEventType type, BaseInfo* data) {
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") {
setupCourseImport(gdi, CourseCB);
}
@ -651,7 +671,7 @@ int TabCourse::courseCB(gdioutput &gdi, GuiEventType type, BaseInfo* data) {
refreshCourse(gdi.getText("Controls"), gdi);
}
else if (bi.id=="Cancel"){
LoadPage("Banor");
loadPage(gdi);
}
}
else if (type==GUI_LISTBOX){
@ -780,6 +800,8 @@ bool TabCourse::loadPage(gdioutput &gdi) {
gdi.addButton("DrawCourse", "Lotta starttider..", CourseCB);
gdi.disableInput("DrawCourse");
}
gdi.addButton("DeleteAll", "Radera alla...", CourseCB);
gdi.newColumn();
gdi.fillDown();

View File

@ -2634,7 +2634,29 @@ bool TabList::loadPage(gdioutput &gdi)
gdi.fillRight();
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);
if (cnf.hasIndividual()) {
gdi.addButton("PriceList", "Prisutdelningslista", ListsCB);
}
@ -2661,25 +2683,6 @@ bool TabList::loadPage(gdioutput &gdi)
gdi.addButton("GenLst:teamchanges", "Lagändringblankett", ListsCB).setExtra(AddTeamClasses | ForcePageBreak);
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) {
gdi.addButton("GenLst:vacnacy", "Vakanser", ListsCB);

View File

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

View File

@ -27,6 +27,7 @@
class Table;
struct AutoCompleteRecord;
class RankScoreFormatter;
class TabRunner :
public TabBase, AutoCompleteHandler
@ -87,9 +88,11 @@ private:
bool savePunchTime(pRunner r, gdioutput &gdi);
PrinterObject splitPrinter;
static shared_ptr<RankScoreFormatter> rankFormatter;
void showRunnerReport(gdioutput &gdi);
static void runnerReport(oEvent &oe, gdioutput &gdi,
int id, bool compactReport,
int maxWidth,
@ -132,27 +135,29 @@ private:
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);
protected:
void clearCompetitionData();
public:
static void renderComments(gdioutput& gdi, oAbstractRunner& r);
static void loadComments(gdioutput& gdi, oAbstractRunner& r);
class CommentHandler : public GuiHandler {
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);

View File

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

View File

@ -167,9 +167,11 @@ int TabTeam::searchCB(gdioutput &gdi, int type, void *data) {
return 0;
}
void TabTeam::selectTeam(gdioutput &gdi, pTeam t)
{
if (gdioutput* gdi_comments = getExtraWindow("comments", false); gdi_comments)
gdi_comments->closeWindow();
if (t){
t->synchronize();
t->evaluate(oBase::ChangeType::Quiet);
@ -588,7 +590,20 @@ int TabTeam::teamCB(gdioutput &gdi, GuiEventType type, BaseInfo* data) {
pTeam t = oe->getTeam(teamId);
if (t && getExtraWindow("comments", true) == nullptr) {
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") {
@ -758,8 +773,12 @@ int TabTeam::teamCB(gdioutput &gdi, GuiEventType type, BaseInfo* data) {
gdi.selectItemByData("ForkKey", currentKey);
gdi.dropLine(0.9);
gdi.addButton("SaveKey", "Ändra", TeamCB);
gdi.refreshFast();
gdi.addButton("SaveKey", "Ändra", TeamCB).setDefault();
gdi.addButton("Cancel", "Avbryt", TeamCB).setCancel();
gdi.popX();
gdi.dropLine(1);
renderComments(t, gdi);
gdi.refresh();
}
else if (bi.id == "SaveKey") {
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.addButton("SelectRunner", "OK", TeamCB).setExtra(leg);
gdi.fillDown();
gdi.addButton("Cancel", "Avbryt", TeamCB);
gdi.addButton("Cancel", "Avbryt", TeamCB).setCancel();
gdi.setItems("SelectR", otherR);
}
else {
gdi.addButton("Cancel", "Avbryt", TeamCB);
gdi.addButton("Cancel", "Avbryt", TeamCB).setCancel();
}
gdi.popX();
renderComments(t, gdi);
gdi.refresh();
}
else if (bi.id=="SelectRunner") {
@ -1238,6 +1259,9 @@ int TabTeam::teamCB(gdioutput &gdi, GuiEventType type, BaseInfo* data) {
if (teamId>0)
save(gdi, true);
if (gdioutput* gdi_settings = getExtraWindow("comments", false); gdi_settings)
gdi_settings->closeWindow();
return true;
}
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) {
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.setRestorePoint("SelectR");
@ -1375,6 +1394,17 @@ void TabTeam::forkingKey(gdioutput& gdi, pTeam t) {
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) {

View File

@ -78,7 +78,9 @@ private:
void enableRunner(gdioutput &gdi, int index, bool enable);
/// 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:
void clearCompetitionData();

View File

@ -2687,11 +2687,11 @@ Aktivera kioskläge = Activate kiosk mode
Avstånd mellan förslag (minuter) = Distance between suggestions (minutes)
Boka starttid = Request start time
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
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å 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
Tiden har passerat sista tillåtna starttid = Time has passed last allowed start time
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
Bästa tid = Best time
Final = Final
Klass / placering = Class / place
Klass / placering = Class / placement
Klass X (namnsuffix) = Class X (name suffix)
Klass efter ranking = Select class by ranking
Kval = Qualification
@ -2795,7 +2795,19 @@ TeamTextA = Team text
Födelsedatum = Birth date
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 team
Lägg till eller redigera kommentarer om laget = Add or edit a comment about the team
Manuellt ändrad brickdata = Manually modified card data
Efteranmälan = Late entry
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;
string cmd;
if (getRecorder().recording()) {
cmd = "tableCmd(\"" + id + "\"); //" + narrow(tbl->getTableName());
cmd = "tableCmd(\"" + id + "\"); //" + toUTF8(tbl->getTableName());
}
try {
ButtonInfo bi;

View File

@ -12,7 +12,7 @@
<b>Example:</b>
<pre>
*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>
</pre>

View File

@ -131,9 +131,9 @@ bool InfoCompetition::synchronize(oEvent &oe, bool onlyCmp, const set<int> &incl
changed = true;
}
if (oe.getZeroTimeNum() != zerotime) {
zerotime = oe.getZeroTimeNum();
changed = true;
if (oe.getZeroTime() != zeroTime) {
zeroTime = oe.getZeroTime();
changed = true;
}
if (changed)
@ -457,7 +457,7 @@ void InfoCompetition::serialize(xmlbuffer &xml, bool diffOnly) const {
prop.push_back(make_pair("date", date));
prop.push_back(make_pair("organizer", organizer));
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);
}

View File

@ -221,7 +221,7 @@ private:
wstring date;
wstring organizer;
wstring homepage;
int zerotime;
wstring zeroTime;
protected:
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++) {
pc->clearStageCourses(leg);
for (int m = 0; m < period; m++)
pc->addStageCourse(leg, coursePattern[(period - patternStart + m)%period][leg], -1);
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++)
pc->addStageCourse(leg, coursePattern[(period - patternStart + m) % period][legToUse], -1);
}
}
bool classHeader = false;
@ -1049,10 +1065,20 @@ void IOF30Interface::readEntryList(gdioutput &gdi, xmlobject &xo, bool removeNon
allR[k]->flagEntryTouched(false);
}
map<pair<int, int>, int> teamLegToFixedCourse;
oe.getTeams(0, allT, false);
for (size_t k = 0; k < allT.size(); k++) {
if (allT[k]->getEntrySource() == entrySourceId)
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;
@ -1311,6 +1337,25 @@ void IOF30Interface::readEntryList(gdioutput &gdi, xmlobject &xo, bool removeNon
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) {
@ -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;
xmlobject ext = xo.getObject("Extensions");
if (ext) {

View File

@ -126,21 +126,6 @@ void 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() {
_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 ys = gEvent->getPropertyInt("ysize", max(650, min<int>(int(rc.bottom)-yp-40, (rc.bottom*8)/10)));
if ((xp + xs > rc.right)
|| xp < rc.left
|| yp + ys > rc.bottom
|| yp < rc.top)
{
// out of bounds, just use default position and size
xp = 50;
yp = 20;
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));
if ((xp + xs > rc.right) || xp < rc.left || yp + ys > rc.bottom || yp < rc.top) {
// out of bounds, just use default position and size
xp = 50;
yp = 20;
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));
}
gEvent->setProperty("ypos", yp + 16);
gEvent->setProperty("xpos", xp + 32);
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:
tabList->emplace_back(gdi_main->getTabs().get(TCmpTab), "Tävling", 10);
tabList->emplace_back(gdi_main->getTabs().get(TRunnerTab), "Deltagare", 6);
tabList->emplace_back(gdi_main->getTabs().get(TTeamTab), "Lag(flera)", 7);
tabList->emplace_back(gdi_main->getTabs().get(TListTab), "Listor", 5);
tabList->emplace_back(gdi_main->getTabs().get(TCmpTab), L"Tävling", 10);
tabList->emplace_back(gdi_main->getTabs().get(TRunnerTab), L"Deltagare", 6);
tabList->emplace_back(gdi_main->getTabs().get(TTeamTab), L"Lag(flera)", 7);
tabList->emplace_back(gdi_main->getTabs().get(TListTab), L"Listor", 5);
{
TabAuto *ta = (TabAuto *)gdi_main->getTabs().get(TAutoTab);
tabList->emplace_back(ta, "Automater", 4);
tabList->emplace_back(ta, L"Automater", 4);
TabAuto::tabAutoRegister(ta);
}
tabList->emplace_back(gdi_main->getTabs().get(TSpeakerTab), "Speaker", 3);
tabList->emplace_back(gdi_main->getTabs().get(TClassTab), "Klasser", 0);
tabList->emplace_back(gdi_main->getTabs().get(TCourseTab), "Banor", 1);
tabList->emplace_back(gdi_main->getTabs().get(TControlTab), "Kontroller", 2);
tabList->emplace_back(gdi_main->getTabs().get(TClubTab), "Klubbar", 8);
tabList->emplace_back(gdi_main->getTabs().get(TSpeakerTab), L"Speaker", 3);
tabList->emplace_back(gdi_main->getTabs().get(TClassTab), L"Klasser", 0);
tabList->emplace_back(gdi_main->getTabs().get(TCourseTab), L"Banor", 1);
tabList->emplace_back(gdi_main->getTabs().get(TControlTab), L"Kontroller", 2);
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;
@ -1190,7 +1172,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
if (it->id==id) {
try {
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);
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++]));
}
if (word == "of" || word == "for" || word == "at" || word == "by")
if (word == "of" || word == "for" || word == "at" || word == "by" || word == "on")
return true;
if (word == "and" || word == "or" || word == "from" || word == "as" || word == "in")

View File

@ -25,17 +25,17 @@
//ABCDEFGHIJKLMNOPQ
int getMeosBuild() {
string revision("$Rev: 1326 $");
string revision("$Rev: 1341 $");
return 174 + atoi(revision.substr(5, string::npos).c_str());
}
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);
}
wstring getBuildType() {
return L"Beta 1"; // No parantheses (...)
return L"RC 1"; // No parantheses (...)
}
wstring getMajorVersion() {
@ -153,6 +153,13 @@ void getSupporters(vector<wstring>& supp, vector<wstring>& developSupp)
supp.emplace_back(L"Silkeborg OK");
supp.emplace_back(L"IK Uven");
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());
}

View File

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

View File

@ -5060,10 +5060,14 @@ void oClass::loadQualificationFinalScheme(const QualificationFinal& scheme) {
inst->synchronize();
}
synchronize();
set<int> base;
qf->getBaseClassInstances(base);
for (oRunner &r : oe->Runners) {
if (r.getClassRef(false) == this) {
if (r.getLegNumber() == 0 && !base.count(r.getDCI().getInt("Heat")))
r.getDI().setInt("Heat", 0);
pTeam t = r.getTeam();
if (t == 0) {
if (t == nullptr) {
t = oe->addTeam(r.getName(), r.getClubId(), getId());
t->setStartNo(r.getStartNo(), oBase::ChangeType::Update);
t->setRunner(0, &r, true);
@ -5084,6 +5088,8 @@ void oClass::updateFinalClasses(oRunner* causingResult, bool updateStartNumbers)
return;
assert(!causingResult || causingResult->Class == this);
int causingLevel = causingResult ? causingResult->tLeg : 0;
//oe->gdibase.addStringUT(0, L"UF:" + getName() + L" for " + (causingResult ? causingResult->getName() : L"-"));
auto computeInstance = [this](const pRunner causingResult) {
if (causingResult) {
@ -5158,6 +5164,28 @@ void oClass::updateFinalClasses(oRunner* causingResult, bool updateStartNumbers)
bool hasRemaining = qualificatonFinal->hasRemainingClass();
GeneralResult gr;
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;
for (int i = instance; i < limit; i++) {
if (classSplit[i].empty())
@ -5206,6 +5234,7 @@ void oClass::updateFinalClasses(oRunner* causingResult, bool updateStartNumbers)
int lastPlace = 0, orderPlace = 1;
int numEqual = 0;
for (size_t k = 0; k < classSplit[i].size(); k++) {
const auto &res = classSplit[i][k]->getTempResult();
if (res.getStatus() == StatusOK) {
@ -5215,17 +5244,31 @@ void oClass::updateFinalClasses(oRunner* causingResult, bool updateStartNumbers)
else
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;
}
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++;
}
}
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);
}
}
if (hasRemaining) {
for (int level = 0; level < maxLevel; level++) {
vector<pair<int, pRunner>> sortedByResult;
@ -5339,7 +5382,7 @@ void oClass::updateFinalClasses(oRunner* causingResult, bool updateStartNumbers)
if (inst < nextLevelInstance)
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();
int oldHeat = di.getInt("Heat");
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 {
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);
else if (nr > 0 && nr < 9999)
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;
}
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);
else if (nr > 9999)
swprintf_s(bf, 64, L"%04d", nr / 10000);

View File

@ -34,6 +34,8 @@ class Table;
class InputInfo;
enum CellType;
constexpr int MaxVarNameLength = 28;
class oDataDefiner {
public:
virtual ~oDataDefiner() {}
@ -53,7 +55,7 @@ public:
};
struct oDataInfo {
char Name[28];
char Name[MaxVarNameLength];
int Index;
int Size;
int Type;
@ -70,7 +72,7 @@ struct oDataInfo {
};
struct oVariableInt {
char name[20];
char name[MaxVarNameLength];
int *data32;
__int64 *data64;
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(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; }
char name[20];
char name[MaxVarNameLength];
bool store(const wchar_t *str);
private:
wchar_t *data;

View File

@ -57,6 +57,8 @@
#include "TabSI.h"
#include "binencoder.h"
#include "image.h"
#include "datadefiners.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
@ -70,7 +72,7 @@
extern Image image;
//Version of database
int oEvent::dbVersion = 94;
int oEvent::dbVersion = 95;
bool oEvent::useSubSecond() const {
if (useSubsecondsVersion == dataRevision)
@ -95,356 +97,6 @@ bool oEvent::useSubSecond() const {
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)
{
readOnly = false;
@ -611,7 +263,7 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi)
oRunnerData->addVariableCurrency("Taxable", "Skattad avgift");
oRunnerData->addVariableInt("BirthYear", oDataContainer::oISDateOrYear, "RunnerBirthDate");
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->addVariableInt("EntryTime", oDataContainer::oISTime, "Anm. tid", make_shared<AbsoluteTimeFormatter>("EntryTime"));
@ -3589,82 +3241,138 @@ void oEvent::generateMinuteStartlist(gdioutput &gdi) {
gdi.addStringUT(fontMedium, bf);
}
vector< vector< vector<pRunner> > > sb;
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])
vector<vector<vector<const oRunner *>>> sb;
sb.reserve(Runners.size() / 5+10);
int lastStartTime = -1;
for (const auto &r : Runners) {
if (r.isRemoved())
continue;
if (it->Class && it->Class->getStart() != starts[k])
if (r.Class && r.Class->getBlock() != blocks[k])
continue;
if (!it->Class && blocks[k]!=0)
if (r.Class && r.Class->getStart() != starts[k])
continue;
if (it->getStatus() == StatusNotCompetiting || it->getStatus() == StatusCANCEL)
if (!r.Class && blocks[k] != 0)
continue;
if (r.getStatus() == StatusNotCompetiting || r.getStatus() == StatusCANCEL)
continue;
if (LastStartTime!=it->tStartTime) {
sb.resize(sb.size() + 1);
LastStartTime = it->tStartTime;
if (lastStartTime != r.tStartTime) {
sb.emplace_back();
lastStartTime = r.tStartTime;
}
if (sb.empty())
sb.resize(1);
if (it->tInTeam == 0)
sb.back().push_back(vector<pRunner>(1, &*it));
if (r.tInTeam == 0)
sb.back().push_back(vector<const oRunner *>(1, &r));
else {
if (it->legToRun() > 0 && it->getStartTime() == 0)
if (r.legToRun() > 0 && r.getStartTime() == 0)
continue;
int minIx = 10000;
for (int j = 0; j < it->tInTeam->getNumRunners(); j++) {
if (j != it->tLeg &&
it->tInTeam->Runners[j] &&
it->tInTeam->Runners[j]->tStartTime == it->tStartTime)
for (int j = 0; j < r.tInTeam->getNumRunners(); j++) {
if (j != r.tLeg && r.tInTeam->Runners[j] && r.tInTeam->Runners[j]->tStartTime == r.tStartTime)
minIx = min(minIx, j);
}
if (minIx == 10000)
sb.back().push_back(vector<pRunner>(1, &*it)); // Single runner on this start time
else if (minIx > it->tLeg) {
sb.back().push_back(vector<pRunner>());
for (int j = 0; j < it->tInTeam->getNumRunners(); j++) {
if (it->tInTeam->Runners[j] &&
it->tInTeam->Runners[j]->tStartTime == it->tStartTime)
sb.back().back().push_back(it->tInTeam->Runners[j]);
sb.back().push_back(vector<const oRunner *>(1, &r)); // Single runner on this start time
else if (minIx > r.tLeg) {
sb.back().emplace_back();
for (int j = 0; j < r.tInTeam->getNumRunners(); j++) {
if (r.tInTeam->Runners[j] && r.tInTeam->Runners[j]->tStartTime == r.tStartTime)
sb.back().back().push_back(r.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();
lastStartTime = -1;
for (size_t k = 0; k < sb.size(); k++) {
if (sb[k].empty())
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;
gdi.addStringUT(y, x+dx[0], boldText, sb[k][0][0]->getStartTimeS());
y+=lh;
if (st > 0) {
gdi.addStringUT(y, x + dx[0], boldText, sb[k][0][0]->getStartTimeS());
y += lh;
}
for (size_t j = 0; j < sb[k].size(); j++) {
const int src_y = y;
int indent = 0;
const vector<pRunner> &r = sb[k][j];
if (r.size() == 1) {
if (r[0]->getCardNo()>0)
gdi.addStringUT(y, x+dx[0], fontMedium, itos(r[0]->getCardNo()));
const auto &rList = sb[k][j];
if (rList.size() == 1) {
if (rList[0]->getCardNo()>0)
gdi.addStringUT(y, x+dx[0], fontMedium, itos(rList[0]->getCardNo()));
wstring name;
if (r[0]->getBib().empty())
name = r[0]->getName();
if (rList[0]->getBib().empty())
name = rList[0]->getName();
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);
}
else {
wstring name;
if (!r[0]->tInTeam->getBib().empty())
name = r[0]->tInTeam->getBib() + L": ";
if (!rList[0]->tInTeam->getBib().empty())
name = rList[0]->tInTeam->getBib() + L": ";
int nnames = 0;
for (size_t i = 0; i < r.size(); i++) {
for (size_t i = 0; i < rList.size(); i++) {
if (nnames>0)
name += L", ";
nnames++;
@ -3677,21 +3385,20 @@ void oEvent::generateMinuteStartlist(gdioutput &gdi) {
indent = gdi.scaleLength(20);
}
name += r[i]->getName();
if (r[i]->getCardNo()>0) {
name += L" (" + itow(r[i]->getCardNo()) + L")";
}
name += rList[i]->getName();
if (rList[i]->getCardNo()>0)
name += L" (" + itow(rList[i]->getCardNo()) + L")";
}
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[3], fontMedium, r[0]->getClass(true));
gdi.addStringUT(src_y, x+dx[2], fontMedium, rList[0]->getClub(), dx[3]-dx[2]-4);
gdi.addStringUT(src_y, x+dx[3], fontMedium, rList[0]->getClass(true));
y+=lh;
}
}
}
gdi.refresh();
}
@ -4784,13 +4491,13 @@ int oEvent::getFirstStart(int classId) const {
return minTime;
}
bool oEvent::hasRank() const
{
oRunnerList::const_iterator it;
for (it=Runners.begin(); it != Runners.end(); ++it){
if (it->getDCI().getInt("Rank")>0)
return true;
bool oEvent::hasRank() const {
for (auto &r : Runners){
if (!r.isRemoved()) {
int rank = r.getDCI().getInt("Rank");
if (rank > 0 && rank < MaxOrderRank)
return true;
}
}
return false;
}

View File

@ -50,6 +50,9 @@
#define cVacantId 888888888
#define cNoClubId 999999999
constexpr int MaxOrderRank = 20000;
constexpr int MaxRankingConstant = numeric_limits<int>::max();
class MeOSFileLock;
class RunnerDB;
class gdioutput;
@ -188,11 +191,6 @@ struct BackupInfo : public CompetitionInfo {
class oListInfo;
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,
oLControlId=8, oLClubId=16, oLCardId=32,
oLPunchId=64, oLTeamId=128, oLEventId=256};

View File

@ -45,8 +45,6 @@
#include "meos.h"
#include <cassert>
typedef bool (__cdecl* OPENDB_FCN)(void);
typedef int (__cdecl* SYNCHRONIZE_FCN)(oBase *obj);
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)
row.push_back(gdibase.recodeToNarrow(formatTimeHMS(t - it->tStartTime)));
else
return "-----";
row.push_back("-----");
}
}

View File

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

View File

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

View File

@ -617,6 +617,17 @@ void oRunner::setClassId(int id, bool isManualUpdate) {
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) {
assert(!isManualUpdate); // Do not support! This may be destroyed if calling tParentRunner->setClass
return;
@ -685,16 +696,15 @@ void oRunner::setClassId(int id, bool isManualUpdate) {
}
}
void oRunner::setCourseId(int id)
{
pCourse pc=Course;
void oRunner::setCourseId(int id) {
pCourse pc = Course;
if (id>0)
Course=oe->getCourse(id);
if (id > 0)
Course = oe->getCourse(id);
else
Course=0;
Course = nullptr;
if (Course!=pc) {
if (Course != pc) {
updateChanged();
if (Class)
getClassRef(true)->clearSplitAnalysis();
@ -7005,6 +7015,28 @@ int oRunner::getRanking() const {
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() {
if (Class && Class->hasClassGlobalDependence()) {
set<int> cls;

View File

@ -116,7 +116,6 @@ typedef const oTeam* cTeam;
struct SICard;
const int MaxRankingConstant = 99999999;
class oAbstractRunner : public oBase {
protected:
@ -850,7 +849,9 @@ public:
void markClassChanged(int controlId);
int getRanking() const;
int getRanking() const final;
wstring getRankingScore() const;
void setRankingScore(double score);
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
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
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 {
if (size_t(instance) >= classDefinition.size())
return false;
if (instance > 0 && classDefinition[instance].level > classDefinition[instance - 1].level)
return false; // Second level
return classDefinition[instance].qualificationMap.empty() &&
classDefinition[instance].numTimeQualifications == 0 &&
classDefinition[instance].extraQualification == QFClass::ExtraQualType::None;
@ -774,7 +775,7 @@ void QualificationFinal::printScheme(const oClass& cls, gdioutput &gdi) const {
gdi.dropLine(0.2);
int rankingBased = 0;
vector<wstring> dst;
int misCntLimit = 0;
for (int place = 1; place < 100; place++) {
auto res = sourcePlaceToFinalOrder.find(make_pair(i + 1, place));
@ -785,7 +786,10 @@ void QualificationFinal::printScheme(const oClass& cls, gdioutput &gdi) const {
else
dst.push_back(itow(place) + L". ➞ " + cname[res->second.first - 1]);
}
else break;
else {
if (++misCntLimit > 10)
break;
}
}
if (rankingBased) {

View File

@ -2701,7 +2701,7 @@ Minsta tid till start (minuter) = Minsta tid till start (minuter)
Sista starttid = Sista starttid
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å 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
Tiden har passerat sista tillåtna starttid = Tiden har passerat sista tillåtna starttid
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
Efteranmälan = Efteranmälan
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);
}
void TestMeOS::setFile(const wstring& file) const {
gdi_main->dbPushDialogAnswer("*" + gdioutput::narrow(file));
}
void TestMeOS::cleanup() const {
}

View File

@ -128,6 +128,7 @@ public:
void setAnswer(const char *ans) const;
void setFile(const string &file) const;
void setFile(const wstring& file) const;
void checkString(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