From 89eec34633a8223e642cb25ab0b0e6ccf81d0580 Mon Sep 17 00:00:00 2001 From: erikmelin <31467290+erikmelin@users.noreply.github.com> Date: Sun, 29 Oct 2017 18:48:54 +0100 Subject: [PATCH] MeOS version 3.5.795. --- code/HTMLWriter.cpp | 2 + code/RestService.cpp | 92 ++++++++++ code/RestService.h | 48 +++++ code/SportIdent.cpp | 8 +- code/TabAuto.cpp | 18 +- code/TabAuto.h | 1 + code/TabClass.cpp | 223 ++++++++++++++++++----- code/TabClass.h | 9 + code/TabCompetition.cpp | 105 +++++++---- code/TabCompetition.h | 3 + code/TabControl.cpp | 13 +- code/TabCourse.cpp | 4 +- code/TabList.cpp | 392 ++++++++++++++++++++++++++++++++++++---- code/TabList.h | 13 +- code/TabRunner.cpp | 100 ++++++---- code/TabSI.cpp | 32 ++-- code/TabSpeaker.cpp | 16 +- code/TabTeam.cpp | 12 +- code/Table.cpp | 42 +++-- code/Table.h | 4 + code/animationdata.cpp | 228 +++++++++++++++++++++++ code/animationdata.h | 64 +++++++ code/csvparser.cpp | 2 +- code/english.lng | 42 ++++- code/gdifonts.h | 3 +- code/gdioutput.cpp | 323 ++++++++++++++++++++++++++------- code/gdioutput.h | 37 +++- code/gdistructures.h | 18 +- code/generalresult.cpp | 8 +- code/infoserver.cpp | 32 +++- code/infoserver.h | 22 ++- code/iof30interface.cpp | 34 +++- code/listeditor.cpp | 12 +- code/liveresult.cpp | 6 +- code/meos.cpp | 52 ++---- code/meos_util.cpp | 149 ++++++++------- code/meos_util.h | 43 +++-- code/meosdb/MeosSQL.cpp | 4 +- code/meosvc15.vcxproj | 9 + code/meosversion.cpp | 5 +- code/metalist.cpp | 61 ++++++- code/methodeditor.cpp | 8 +- code/mysqldaemon.cpp | 4 +- code/newcompetition.cpp | 7 +- code/oBase.h | 3 +- code/oCard.cpp | 2 +- code/oClass.cpp | 14 +- code/oClass.h | 4 + code/oControl.cpp | 14 +- code/oCourse.cpp | 2 +- code/oDataContainer.cpp | 15 ++ code/oDataContainer.h | 14 +- code/oEvent.cpp | 292 +++++++++++++++++++++++++----- code/oEvent.h | 11 +- code/oEventSpeaker.cpp | 10 +- code/oImportExport.cpp | 19 +- code/oListInfo.cpp | 59 ++++-- code/oListInfo.h | 20 +- code/oPunch.cpp | 2 +- code/oRunner.cpp | 94 ++++++---- code/oRunner.h | 3 +- code/oTeam.cpp | 31 ++-- code/oTeam.h | 2 +- code/oTeamEvent.cpp | 8 +- code/onlineinput.cpp | 2 +- code/onlineresults.cpp | 23 ++- code/pdfwriter.cpp | 1 + code/printer.cpp | 19 +- code/restserver.cpp | 261 ++++++++++++++++++++++++++ code/restserver.h | 99 ++++++++++ code/speakermonitor.cpp | 6 +- code/swedish.lng | 29 ++- code/testmeos.cpp | 20 +- code/testmeos.h | 13 +- code/toolbar.cpp | 4 +- code/xmlparser.cpp | 2 +- code/xmlparser.h | 12 ++ 77 files changed, 2792 insertions(+), 628 deletions(-) create mode 100644 code/RestService.cpp create mode 100644 code/RestService.h create mode 100644 code/animationdata.cpp create mode 100644 code/animationdata.h create mode 100644 code/restserver.cpp create mode 100644 code/restserver.h diff --git a/code/HTMLWriter.cpp b/code/HTMLWriter.cpp index 9d0732a..2038cc1 100644 --- a/code/HTMLWriter.cpp +++ b/code/HTMLWriter.cpp @@ -189,6 +189,7 @@ static void getStyle(const map< pair, pair > & bool gdioutput::writeHTML(const wstring &file, const wstring &title, int refreshTimeOut) const { + checkWriteAccess(file); ofstream fout(file.c_str()); if (fout.bad()) @@ -294,6 +295,7 @@ bool sortTL_X(const TextInfo *a, const TextInfo *b) bool gdioutput::writeTableHTML(const wstring &file, const wstring &title, int refreshTimeOut) const { + checkWriteAccess(file); ofstream fout(file.c_str()); if (fout.bad()) diff --git a/code/RestService.cpp b/code/RestService.cpp new file mode 100644 index 0000000..53893cb --- /dev/null +++ b/code/RestService.cpp @@ -0,0 +1,92 @@ +#include "stdafx.h" +#include "RestService.h" +#include "meos_util.h" +#include "restserver.h" + +int AutomaticCB(gdioutput *gdi, int type, void *data); + +RestService::RestService() : AutoMachine("RestService"), port(-1) { +} + + +RestService::~RestService() { + if (server) { + server->stop(); + RestServer::remove(server); + } +} + +void RestService::save(oEvent &oe, gdioutput &gdi) { + if (!server) + server = RestServer::construct(); + + int port = gdi.getTextNo("Port"); + if (port > 0 && port < 65536) + server->startService(port); + else + throw std::exception("Invalid port number"); +} + +void RestService::settings(gdioutput &gdi, oEvent &oe, bool created) { + if (port == -1) + port = oe.getPropertyInt("ServicePort", 2009); + + settingsTitle(gdi, "MeOS Informationsserver REST-API"); + startCancelInterval(gdi, "Save", created, IntervalNone, L""); + + if (!server) + gdi.addInput("Port", itow(port), 10, 0, L"Port:", L"Testa genom http://localhost:[PORT]/meos"); + else + gdi.addString("", 0, "Server startad på X#" + itos(port)); + + gdi.popX(); + gdi.addString("", 10, "help:rest"); +} + +void RestService::status(gdioutput &gdi) { + gdi.pushX(); + gdi.addString("", 1, name); + /*if (!baseFile.empty()) { + gdi.fillRight(); + gdi.pushX(); + gdi.addString("", 0, L"Destination: X#" + baseFile); + + if (interval>0) { + gdi.popX(); + gdi.dropLine(1); + gdi.addString("", 0, "Säkerhetskopierar om: "); + gdi.addTimer(gdi.getCY(), gdi.getCX(), timerIgnoreSign, (GetTickCount() - timeout) / 1000); + } + + gdi.popX(); + }*/ + + if (server) { + gdi.addString("", 0, "Server startad på port X#" + itos(port)); + + RestServer::Statistics rs; + server->getStatistics(rs); + gdi.addString("", 0, "Antal förfrågningar: X#" + itos(rs.numRequests)); + gdi.addString("", 0, "Genomsnittlig svarstid: X ms#" + itos(rs.averageResponseTime)); + gdi.addString("", 0, "Längsta svarstid: X ms#" + itos(rs.maxResponseTime)); + } + + gdi.dropLine(2); + gdi.fillRight(); + gdi.addButton("Stop", "Stoppa automaten", AutomaticCB).setExtra(getId()); + gdi.fillDown(); + gdi.addButton("InfoService", "Inställningar...", AutomaticCB).setExtra(getId()); + gdi.popX(); +} + +void RestService::process(gdioutput &gdi, oEvent *oe, AutoSyncType ast) { + +} + + +void RestService::handle(gdioutput &gdi, BaseInfo &info, GuiEventType type) { + if (type == GUI_BUTTON) { + ButtonInfo &bi = static_cast(info); + + } +} \ No newline at end of file diff --git a/code/RestService.h b/code/RestService.h new file mode 100644 index 0000000..92dfdcb --- /dev/null +++ b/code/RestService.h @@ -0,0 +1,48 @@ +/************************************************************************ +MeOS - Orienteering Software +Copyright (C) 2009-2017 Melin Software HB + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Melin Software HB - software@melin.nu - www.melin.nu +Eksoppsvägen 16, SE-75646 UPPSALA, Sweden + +************************************************************************/ + +#pragma once +#include "TabAuto.h" +#include + +class RestServer; + +class RestService : + public AutoMachine, GuiHandler +{ + int port; + shared_ptr server; + +public: + + void save(oEvent &oe, gdioutput &gdi) override; + void settings(gdioutput &gdi, oEvent &oe, bool created) override; + RestService *clone() const override { return new RestService(*this); } + void status(gdioutput &gdi) override; + void process(gdioutput &gdi, oEvent *oe, AutoSyncType ast) override; + + void handle(gdioutput &gdi, BaseInfo &info, GuiEventType type) override; + + RestService(); + ~RestService(); +}; + diff --git a/code/SportIdent.cpp b/code/SportIdent.cpp index a864fb8..054c611 100644 --- a/code/SportIdent.cpp +++ b/code/SportIdent.cpp @@ -2140,13 +2140,19 @@ void SportIdent::getInfoString(const wstring &com, vector &infov) } } +static string formatTimeN(int t) { + const wstring &wt = formatTime(t); + string nt(wt.begin(), wt.end()); + return nt; +} + vector SICard::codeLogData(int row) const { vector log; log.push_back(itos(row)); if (readOutTime[0] == 0) - log.push_back(getLocalTime()); + log.push_back(getLocalTimeN()); else log.push_back(readOutTime); log.push_back(itos(CardNumber)); diff --git a/code/TabAuto.cpp b/code/TabAuto.cpp index 4ba343d..3e91537 100644 --- a/code/TabAuto.cpp +++ b/code/TabAuto.cpp @@ -35,6 +35,7 @@ #include "classconfiginfo.h" #include "onlineresults.h" #include "onlineinput.h" +#include "RestService.h" #include "TabAuto.h" #include "TabSI.h" @@ -78,6 +79,8 @@ AutoMachine* AutoMachine::construct(Machines ms) { return new OnlineResults(); case mSaveBackup: return new SaveMachine(); + case mInfoService: + return new RestService(); } throw meosException("Invalid machine"); } @@ -316,6 +319,9 @@ int TabAuto::processButton(gdioutput &gdi, const ButtonInfo &bu) SaveMachine *sm=dynamic_cast(getMachine(bu.getExtraInt())); settings(gdi, sm, mSaveBackup); } + else if (bu.id == "InfoService") { + settings(gdi, getMachine(bu.getExtraInt()), mInfoService); + } else if (bu.id=="StartResult") { #ifndef MEOSDB wstring minute=gdi.getText("Interval"); @@ -602,12 +608,14 @@ bool TabAuto::loadPage(gdioutput &gdi) gdi.addButton("OnlineInput", "Inmatning online", AutomaticCB, "Hämta stämplingar m.m. från nätet"); gdi.popX(); gdi.dropLine(2.5); - gdi.addButton("Splits", "Sträcktider (WinSplits)", AutomaticCB, "Spara sträcktider till en fil för automatisk synkronisering med WinSplits"); - gdi.addButton("Prewarning", "Förvarningsröst", AutomaticCB, "tooltip:voice"); + gdi.addButton("SaveBackup", "Säkerhetskopiering", AutomaticCB); + gdi.addButton("InfoService", "Informationsserver", AutomaticCB); gdi.addButton("Punches", "Stämplingstest", AutomaticCB, "Simulera inläsning av stämplar"); gdi.popX(); gdi.dropLine(2.5); - gdi.addButton("SaveBackup", "Säkerhetskopiering", AutomaticCB); + gdi.addButton("Splits", "Sträcktider (WinSplits)", AutomaticCB, "Spara sträcktider till en fil för automatisk synkronisering med WinSplits"); + gdi.addButton("Prewarning", "Förvarningsröst", AutomaticCB, "tooltip:voice"); + gdi.fillDown(); gdi.dropLine(3); @@ -682,7 +690,7 @@ void AutoMachine::startCancelInterval(gdioutput &gdi, char *startCommand, bool c void PrintResultMachine::settings(gdioutput &gdi, oEvent &oe, bool created) { settingsTitle(gdi, "Resultatutskrift / export"); - wstring time=created ? L"10:00" : getTimeMSW(interval); + wstring time=created ? L"10:00" : getTimeMS(interval); startCancelInterval(gdi, "StartResult", created, IntervalMinute, time); if (created) { @@ -1097,7 +1105,7 @@ void SaveMachine::process(gdioutput &gdi, oEvent *oe, AutoSyncType ast) { void SaveMachine::settings(gdioutput &gdi, oEvent &oe, bool created) { settingsTitle(gdi, "Säkerhetskopiering"); - wstring time=created ? L"10:00" : getTimeMSW(interval); + wstring time=created ? L"10:00" : getTimeMS(interval); startCancelInterval(gdi, "StartBackup", created, IntervalMinute, time); int cx = gdi.getCX(); diff --git a/code/TabAuto.h b/code/TabAuto.h index ea5a051..cd39160 100644 --- a/code/TabAuto.h +++ b/code/TabAuto.h @@ -43,6 +43,7 @@ enum Machines { mOnlineResults, mOnlineInput, mSaveBackup, + mInfoService, }; class AutoMachine diff --git a/code/TabClass.cpp b/code/TabClass.cpp index 7110706..dc55ead 100644 --- a/code/TabClass.cpp +++ b/code/TabClass.cpp @@ -69,6 +69,7 @@ void TabClass::clearCompetitionData() { storedPredefined = oEvent::PredefinedTypes(-1); cInfoCache.clear(); hasWarnedDirect = false; + hasWarnedStartTime = false; lastSeedMethod = -1; lastSeedPreventClubNb = true; @@ -159,8 +160,12 @@ int TabClass::multiCB(gdioutput &gdi, int type, void *data) gdi.setInputStatus("CommonStartTime", gdi.isChecked(bi.id)); } else if (bi.id == "CoursePool") { - string strId = "StageCourses_expl"; + string strId = "StageCourses_label"; gdi.setTextTranslate(strId, getCourseLabel(gdi.isChecked(bi.id)), true); + setLockForkingState(gdi, gdi.isChecked("CoursePool"), gdi.isChecked("LockForking")); + } + else if (bi.id == "LockForking") { + setLockForkingState(gdi, gdi.isChecked("CoursePool"), gdi.isChecked(bi.id)); } else if (bi.id == "DefineForking") { if (!checkClassSelected(gdi)) @@ -390,7 +395,13 @@ int TabClass::multiCB(gdioutput &gdi, int type, void *data) if (nstages>0 && nstages<41) { wstring st=gdi.getText("StartTime"); - if (oe->convertAbsoluteTime(st)>0) + + int nst = oe->convertAbsoluteTime(st); + if (warnDrawStartTime(gdi, nst)) { + nst = 3600; + st = oe->getAbsTime(nst); + } + if (nst>0) storedStart = st; save(gdi, false); //Clears and reloads @@ -568,7 +579,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) bool checked = gdi.isChecked("UseAdvanced"); oe->setProperty("AdvancedClassSettings", checked); save(gdi, true); - PostMessage(gdi.getTarget(), WM_USER + 2, TClassTab, 0); + PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TClassTab, 0); } else if (bi.id=="SwitchMode") { if (!tableMode) @@ -578,7 +589,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) } else if (bi.id=="Restart") { save(gdi, true); - gdi.clearPage(true); + clearPage(gdi, true); gdi.addString("", 2, "Omstart i stafettklasser"); gdi.addString("", 10, "help:31661"); gdi.addListBox("RestartClasses", 200, 250, 0, L"Stafettklasser", L"", true); @@ -662,13 +673,16 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) bool drawCoursebased = drawInfo.coursesTogether; + int maxST = 0; map > specs; for(size_t k=0; ksynchronize(false); - ci.pc->setDrawFirstStart(drawInfo.firstStart + drawInfo.baseInterval * ci.firstStart); + int stloc = drawInfo.firstStart + drawInfo.baseInterval * ci.firstStart; + maxST = max(maxST, stloc); + ci.pc->setDrawFirstStart(stloc); ci.pc->setDrawInterval(ci.interval * drawInfo.baseInterval); ci.pc->setDrawVacant(ci.nVacant); ci.pc->setDrawNumReserved(ci.nExtra); @@ -685,6 +699,9 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) specs[ci.classId].push_back(cds); } + if (warnDrawStartTime(gdi, maxST)) + return 0; + for (map >::iterator it = specs.begin(); it != specs.end(); ++it) { oe->drawList(it->second, soft, pairSize, oEvent::drawAll); @@ -692,7 +709,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) oe->addAutoBib(); - gdi.clearPage(false); + clearPage(gdi, false); gdi.addButton("Cancel", "Återgå", ClassesCB); oListParam par; @@ -750,8 +767,8 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) ClassId = 0; EditChanged=false; - gdi.clearPage(true); - + clearPage(gdi, true); + gdi.addString("", boldLarge, "Lotta flera klasser"); gdi.dropLine(); @@ -877,6 +894,9 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) } else if (bi.id == "AutomaticDraw") { wstring firstStart = gdi.getText("FirstStart"); + + if (warnDrawStartTime(gdi, firstStart)) + return 0; wstring minInterval = gdi.getText("MinInterval"); wstring vacances = gdi.getText("Vacances"); bool lateBefore = false; @@ -888,7 +908,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) int method = gdi.getSelectedItem("Method").first; bool useSoft = method == DMSOFT; - gdi.clearPage(true); + clearPage(gdi, true); oe->automaticDrawAll(gdi, firstStart, minInterval, vacances, lateBefore, useSoft, pairSize); oe->addAutoBib(); @@ -909,7 +929,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) wstring firstStart; firstStart = gdi.getText("FirstStart"); - gdi.clearPage(false); + clearPage(gdi, false); gdi.addString("", boldLarge, "Gemensam start"); gdi.dropLine(); int by = 0; @@ -938,6 +958,9 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) } wstring time = gdi.getText("FirstStart"); + if (warnDrawStartTime(gdi, time)) + return 0; + for (set::iterator it = classes.begin(); it!=classes.end();++it) { simultaneous(*it, time); } @@ -973,8 +996,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) } } - gdi.clearPage(false); - + clearPage(gdi, false); gdi.addString("", boldLarge, "Lotta flera klasser"); gdi.dropLine(0.5); @@ -1331,13 +1353,17 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) if (t<=0) throw std::exception("Ogiltig första starttid. Måste vara efter nolltid."); - + + oEvent::DrawType dtype(oEvent::drawAll); if (bi.id=="DoDrawAfter") dtype = oEvent::remainingAfter; else if (bi.id=="DoDrawBefore") dtype = oEvent::remainingBefore; - + else { + if (warnDrawStartTime(gdi, t)) + return 0; + } //bool pairwise = false; // if (gdi.hasField("Pairwise")) @@ -1427,6 +1453,8 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) oe->generateList(gdi, false, info, true); gdi.refresh(); + gdi.setData("ClassPageLoaded", 1); + return 0; } else if (bi.id=="HandleBibs") { @@ -1469,7 +1497,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) if (EditChanged) gdi.sendCtrlMessage("Save"); - gdi.clearPage(false); + clearPage(gdi, false); gdi.addString("", boldLarge, L"Lotta klassen X#"+pc->getName()); gdi.dropLine(); @@ -1519,7 +1547,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) if (!pc) throw std::exception("Class not found"); - gdi.clearPage(false); + clearPage(gdi, false); gdi.addString("", boldLarge, L"Nummerlappar i X#" + pc->getName()); gdi.dropLine(); gdi.setRestorePoint("bib"); @@ -1632,7 +1660,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) if (!pc) throw std::exception("Class not found"); - gdi.clearPage(true); + clearPage(gdi, true); gdi.addString("", boldLarge, L"Dela klass: X#" + pc->getName()); gdi.dropLine(); int tot, fin, dns; @@ -1682,7 +1710,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) pc->splitClass(ClassSplitMethod(lbi.data), parts, outClass); - gdi.clearPage(true); + clearPage(gdi, true); gdi.addButton("Cancel", "Återgå", ClassesCB); oListParam par; @@ -1692,6 +1720,21 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) oe->generateListInfo(par, gdi.getLineHeight(), info); oe->generateList(gdi, false, info, true); } + else if (bi.id == "LockAllForks" || bi.id == "UnLockAllForks") { + bool lock = bi.id == "LockAllForks"; + vector allCls; + oe->getClasses(allCls, true); + for (pClass c : allCls) { + if (c->isRemoved()) + continue; + if (!c->hasCoursePool() && c->hasMultiCourse()) { + c->lockedForking(lock); + c->synchronize(true); + } + } + loadPage(gdi); + return 0; + } else if (bi.id=="Merge") { save(gdi, true); if (!checkClassSelected(gdi)) @@ -1720,7 +1763,8 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) if (cls.empty()) throw std::exception("En klass kan inte slås ihop med sig själv."); - gdi.clearPage(true); + + clearPage(gdi, true); gdi.addString("", boldLarge, L"Slå ihop klass: X (denna klass behålls)#" + pc->getName()); gdi.dropLine(); gdi.addString("", 10, "help:12138"); @@ -1781,7 +1825,8 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) throw std::exception("En klass kan inte slås ihop med sig själv."); pc->mergeClass(lbi.data); - gdi.clearPage(true); + + clearPage(gdi, true); gdi.addButton("Cancel", "Återgå", ClassesCB); oListParam par; @@ -2078,7 +2123,7 @@ void TabClass::showClassSettings(gdioutput &gdi) gdi.addString("C" + itos(id), 0, L"X platser. Startar Y#" + wstring(bf1) + L"#" + bf2); y = gdi.getCY(); gdi.addInput(xp+300, y, "S"+itos(id), first, 7, DrawClassesCB); - gdi.addInput(xp+300+width, y, "I"+itos(id), formatTimeW(ci.interval*drawInfo.baseInterval), 7, DrawClassesCB); + gdi.addInput(xp+300+width, y, "I"+itos(id), formatTime(ci.interval*drawInfo.baseInterval), 7, DrawClassesCB); gdi.addInput(xp+300+width*2, y, "V"+itos(id), itow(ci.nVacant), 7, DrawClassesCB); gdi.addInput(xp+300+width*3, y, "R"+itos(id), itow(ci.nExtra), 7, DrawClassesCB); @@ -2245,9 +2290,14 @@ void TabClass::selectClass(gdioutput &gdi, int cid) if (gdi.hasField("Unordered")) gdi.check("Unordered", pc->hasUnorderedLegs()); + if (gdi.hasField("LockForking")) { + gdi.check("LockForking", pc->lockedForking()); + setLockForkingState(gdi, pc->hasCoursePool(), pc->lockedForking()); + } + if (gdi.hasField("MCourses")) { oe->fillCourses(gdi, "MCourses", true); - string strId = "StageCourses_expl"; + string strId = "StageCourses_label"; gdi.setTextTranslate(strId, getCourseLabel(pc->hasCoursePool()), true); } @@ -2510,6 +2560,9 @@ void TabClass::multiCourse(gdioutput &gdi, int nLeg) { "Knyt löparna till banor från en pool vid målgång."); gdi.addCheckbox("Unordered", "Oordnade parallella sträckor", MultiCB, false, "Tillåt löpare inom en parallell grupp att springa gruppens banor i godtycklig ordning."); + gdi.addCheckbox("LockForking", "Lås gafflingar", MultiCB, false, + "Markera för att förhindra oavsiktlig ändring av gafflingsnycklar."); + gdi.popX(); gdi.fillRight(); gdi.dropLine(1.7); @@ -2594,6 +2647,9 @@ void TabClass::save(gdioutput &gdi, bool skipReload) if (gdi.hasField("Unordered")) pc->setUnorderedLegs(gdi.isChecked("Unordered")); + if (gdi.hasField("LockForking")) + pc->lockedForking(gdi.isChecked("LockForking")); + pc->setAllowQuickEntry(gdi.isChecked("AllowQuickEntry")); pc->setNoTiming(gdi.isChecked("NoTiming")); @@ -2636,7 +2692,8 @@ void TabClass::save(gdioutput &gdi, bool skipReload) bool sim = gdi.isChecked("CommonStart"); if (sim) { pc->setStartType(0, STTime, true); - pc->setStartData(0, gdi.getText("CommonStartTime")); + if (!warnDrawStartTime(gdi, gdi.getText("CommonStartTime"))) + pc->setStartData(0, gdi.getText("CommonStartTime")); } else { pc->setStartType(0, STDrawn, true); @@ -2728,12 +2785,14 @@ struct ButtonData { bool TabClass::loadPage(gdioutput &gdi) { + if (!gdi.hasData("ClassPageLoaded")) + hasWarnedStartTime = false; oe->checkDB(); oe->checkNecessaryFeatures(); gdi.selectTab(tabId); - gdi.clearPage(false); + clearPage(gdi, false); int xp=gdi.getCX(); - + const int button_w=gdi.scaleLength(90); string switchMode; switchMode=tableMode ? "Formulärläge" : "Tabelläge"; @@ -2839,24 +2898,51 @@ bool TabClass::loadPage(gdioutput &gdi) gdi.dropLine(2); gdi.popX(); - gdi.fillDown(); - gdi.addString("", 1, "Funktioner"); - vector func; if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::DrawStartList)) func.push_back(ButtonData("Draw", "Lotta / starttider...", false)); + if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Bib)) func.push_back(ButtonData("Bibs", "Nummerlappar...", false)); - if (cnf.hasTeamClass()) + + if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::DrawStartList)) + func.push_back(ButtonData("DrawMode", "Lotta flera klasser", true)); + + if (cnf.hasTeamClass()) { func.push_back(ButtonData("Restart", "Omstart...", true)); + + vector allCls; + oe->getClasses(allCls, false); + bool unlockedClass = false; + bool lockedClass = false; + + if (showAdvanced) { + for (pClass c : allCls) { + if (c->isRemoved()) + continue; + + if (!c->hasCoursePool() && c->hasMultiCourse()) { + if (c->lockedForking()) + lockedClass = true; + else + unlockedClass = true; + } + } + + if (unlockedClass) { + func.push_back(ButtonData("LockAllForks", "Lås gafflingar", true)); + } + if (lockedClass) { + func.push_back(ButtonData("UnLockAllForks", "Tillåt gafflingsändringar", true)); + } + } + } + if (showAdvanced) { func.push_back(ButtonData("Merge", "Slå ihop klasser...", false)); func.push_back(ButtonData("Split", "Dela klassen...", false)); } - if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::DrawStartList)) - func.push_back(ButtonData("DrawMode", "Lotta flera klasser", true)); - func.push_back(ButtonData("QuickSettings", "Snabbinställningar", true)); if (showAdvanced && oe->getMeOSFeatures().hasFeature(MeOSFeatures::Vacancy)) { vector rr; @@ -2872,22 +2958,40 @@ bool TabClass::loadPage(gdioutput &gdi) func.push_back(ButtonData("RemoveVacant", "Radera vakanser", true)); } + func.push_back(ButtonData("QuickSettings", "Snabbinställningar", true)); + + RECT funRect; + funRect.right = gdi.getCX() - 7; + funRect.top = gdi.getCY() - 2; + funRect.left = 0; + + gdi.dropLine(0.5); + gdi.fillDown(); + gdi.addString("", fontMediumPlus, "Funktioner"); + gdi.dropLine(0.3); gdi.pushX(); gdi.fillRight(); + int xlimit = gdi.getWidth() - button_w/2; + for (size_t k = 0; k < func.size(); k++) { ButtonInfo &bi = gdi.addButton(func[k].id, func[k].label, ClassesCB); if (!func[k].global) bi.isEdit(true); - if ( k % 2 == 1) { + funRect.left = max(funRect.left, gdi.getCX() + 7); + if ( gdi.getCX() > xlimit && k+1 < func.size()) { gdi.popX(); gdi.dropLine(2.5); } } + gdi.dropLine(2.5); + + funRect.bottom = gdi.getCY(); + gdi.addRectangle(funRect, colorLightGreen); gdi.popX(); - gdi.dropLine(3); + gdi.dropLine(0.5); gdi.fillRight(); gdi.addButton("Save", "Spara", ClassesCB).setDefault(); gdi.disableInput("Save"); @@ -2988,7 +3092,7 @@ void TabClass::saveClassSettingsTable(gdioutput &gdi) { } void TabClass::prepareForDrawing(gdioutput &gdi) { - gdi.clearPage(false); + clearPage(gdi, false); gdi.addString("", 2, "Klassinställningar"); int baseLine = gdi.getCY(); gdi.addString("", 10, "help:59395"); @@ -3110,7 +3214,7 @@ void TabClass::drawDialog(gdioutput &gdi, DrawMethod method, const oClass &pc) { } if (method != DMSimultaneous) - gdi.addInput("Interval", formatTimeW(interval), 10, 0, L"Startintervall (min):").setSynchData(&lastInterval); + gdi.addInput("Interval", formatTime(interval), 10, 0, L"Startintervall (min):").setSynchData(&lastInterval); if (method == DMRandom || method == DMSOFT || method == DMClumped) gdi.addInput("Vacanses", itow(vac), 10, 0, L"Antal vakanser:").setSynchData(&lastNumVac); @@ -3204,7 +3308,7 @@ void TabClass::setMultiDayClass(gdioutput &gdi, bool hasMulti, DrawMethod defaul } void TabClass::pursuitDialog(gdioutput &gdi) { - gdi.clearPage(false); + clearPage(gdi, false); gdi.addString("", boldLarge, "Jaktstart"); gdi.dropLine(); vector cls; @@ -3216,9 +3320,9 @@ void TabClass::pursuitDialog(gdioutput &gdi) { gdi.fillRight(); - gdi.addInput("MaxAfter", formatTimeW(pSavedDepth), 10, 0, L"Maxtid efter:", L"Maximal tid efter ledaren för att delta i jaktstart"); - gdi.addInput("TimeRestart", L"+" + formatTimeW(pFirstRestart), 8, 0, L"Första omstartstid:", L"Ange tiden relativt klassens första start"); - gdi.addInput("Interval", formatTimeW(pInterval), 8, 0, L"Startintervall:", L"Ange startintervall för minutstart"); + gdi.addInput("MaxAfter", formatTime(pSavedDepth), 10, 0, L"Maxtid efter:", L"Maximal tid efter ledaren för att delta i jaktstart"); + gdi.addInput("TimeRestart", L"+" + formatTime(pFirstRestart), 8, 0, L"Första omstartstid:", L"Ange tiden relativt klassens första start"); + gdi.addInput("Interval", formatTime(pInterval), 8, 0, L"Startintervall:", L"Ange startintervall för minutstart"); wchar_t bf[32]; swprintf_s(bf, L"%f", pTimeScaling); gdi.addInput("ScaleFactor", bf, 8, 0, L"Tidsskalning:"); @@ -3917,12 +4021,45 @@ void TabClass::writeDrawInfo(gdioutput &gdi, const DrawInfo &drawInfoIn) { gdi.setText("Vacances", itow(int(drawInfoIn.vacancyFactor *100.0)) + L"%"); gdi.setText("Extra", itow(int(drawInfoIn.extraFactor * 100.0) ) + L"%"); - gdi.setText("BaseInterval", formatTimeW(drawInfoIn.baseInterval)); + gdi.setText("BaseInterval", formatTime(drawInfoIn.baseInterval)); gdi.check("AllowNeighbours", drawInfoIn.allowNeighbourSameCourse); gdi.check("CoursesTogether", drawInfoIn.coursesTogether); - gdi.setText("MinInterval", formatTimeW(drawInfoIn.minClassInterval)); - gdi.setText("MaxInterval", formatTimeW(drawInfoIn.maxClassInterval)); + gdi.setText("MinInterval", formatTime(drawInfoIn.minClassInterval)); + gdi.setText("MaxInterval", formatTime(drawInfoIn.maxClassInterval)); gdi.setText("nFields", drawInfoIn.nFields); gdi.setText("FirstStart", oe->getAbsTime(drawInfoIn.firstStart)); } + +void TabClass::setLockForkingState(gdioutput &gdi, bool poolState, bool lockState) { + if (gdi.hasField("DefineForking")) + gdi.setInputStatus("DefineForking", !lockState && !poolState); + + if (gdi.hasField("LockForking")) + gdi.setInputStatus("LockForking", !poolState); + + int legno = 0; + while (gdi.hasField("@Course" + itos(legno))) { + gdi.setInputStatus("@Course" + itos(legno++), !lockState || poolState); + } +} + +bool TabClass::warnDrawStartTime(gdioutput &gdi, const wstring &firstStart) { + int st = oe->getRelativeTime(firstStart); + return warnDrawStartTime(gdi, st); +} + +bool TabClass::warnDrawStartTime(gdioutput &gdi, int time) { + if (!hasWarnedStartTime && time > 3600 * 8 && !oe->useLongTimes()) { + bool res = gdi.ask(L"warn:latestarttime#" + itow(time/3600)); + if (res) + hasWarnedStartTime = true; + return !res; + } + return false; +} + +void TabClass::clearPage(gdioutput &gdi, bool autoRefresh) { + gdi.clearPage(autoRefresh); + gdi.setData("ClassPageLoaded", 1); +} diff --git a/code/TabClass.h b/code/TabClass.h index 9b627ae..fcca150 100644 --- a/code/TabClass.h +++ b/code/TabClass.h @@ -79,6 +79,12 @@ class TabClass : void pursuitDialog(gdioutput &gdi); + bool warnDrawStartTime(gdioutput &gdi, int time); + bool warnDrawStartTime(gdioutput &gdi, const wstring &firstStart); + + void static clearPage(gdioutput &gdi, bool autoRefresh); + + bool hasWarnedStartTime; bool hasWarnedDirect; bool tableMode; DrawMethod lastDrawMethod; @@ -137,6 +143,9 @@ class TabClass : void writeDrawInfo(gdioutput &gdi, const DrawInfo &drawInfo); static vector< pair > getPairOptions(); + + void setLockForkingState(gdioutput &gdi, bool poolState, bool lockState); + public: void clearCompetitionData(); diff --git a/code/TabCompetition.cpp b/code/TabCompetition.cpp index 06d76ee..a98d155 100644 --- a/code/TabCompetition.cpp +++ b/code/TabCompetition.cpp @@ -119,6 +119,12 @@ bool TabCompetition::save(gdioutput &gdi, bool write) gdi.setText("Date", oe->getDate()); return 0; } + bool updateTimes = newZT != oldZT && oe->getNumRunners() > 0 && gdi.ask(L"ask:updatetimes"); + + if (updateTimes) { + int delta = oldZT - newZT; + oe->updateStartTimes(delta); + } } oe->setDate(date); oe->useLongTimes(longTimes); @@ -380,7 +386,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) if (bi.id == "CopyLink") { string url = gdi.narrow(gdi.getText("link")); - if (OpenClipboard(gdi.getHWND())) { + if (OpenClipboard(gdi.getHWNDMain())) { EmptyClipboard(); HGLOBAL hClipboardData; hClipboardData = GlobalAlloc(GMEM_DDESHARE, @@ -603,7 +609,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) loadPage(gdi); } else if (bi.id == "Exit") { - PostMessage(gdi.getMain(), WM_CLOSE, 0, 0); + PostMessage(gdi.getHWNDMain(), WM_CLOSE, 0, 0); } else if (bi.id == "Help") { wchar_t fn[MAX_PATH]; @@ -757,7 +763,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) gdi.setWindowTitle(oe->getTitleName()); if (oe->isClient() && oe->getPropertyInt("UseDirectSocket", true) != 0) { - oe->getDirectSocket().startUDPSocketThread(gdi.getMain()); + oe->getDirectSocket().startUDPSocketThread(gdi.getHWNDMain()); } loadConnectionPage(gdi); @@ -986,7 +992,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) oe->setProperty("UseEventor", 1); else oe->setProperty("UseEventor", 2); - PostMessage(gdi.getTarget(), WM_USER + 2, TCmpTab, 0); + PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TCmpTab, 0); } else if (bi.id == "EventorAPI") { assert(!eventorOrigin.empty()); @@ -1124,7 +1130,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) wstring zipped = getTempFile(); zip(zipped.c_str(), 0, fileList); - ProgressWindow pw(gdi.getTarget()); + ProgressWindow pw(gdi.getHWNDTarget()); pw.init(); vector > key; getAPIKey(key); @@ -1192,6 +1198,8 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) return loadPage(gdi); } + checkReadyForResultExport(gdi, set()); + gdi.clearPage(true); gdi.fillDown(); gdi.dropLine(); @@ -1216,7 +1224,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) wstring zipped = getTempFile(); zip(zipped.c_str(), 0, fileList); - ProgressWindow pw(gdi.getTarget()); + ProgressWindow pw(gdi.getHWNDTarget()); pw.init(); vector > key; getAPIKey(key); @@ -1282,7 +1290,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) SYSTEMTIME st; GetLocalTime(&st); st.wYear--; // Include last years competitions - getEventorCompetitions(gdi, convertSystemDateW(st), events); + getEventorCompetitions(gdi, convertSystemDate(st), events); gdi.clearPage(true); gdi.addString("", boldLarge, "Hämta data från Eventor"); @@ -1296,7 +1304,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) gdi.addSelection("EventorSel", 300, 200); sort(events.begin(), events.end()); st.wYear++; // Restore current time - wstring now = convertSystemDateW(st); + wstring now = convertSystemDate(st); int selected = 0; // Select next event by default for (int k = events.size()-1; k>=0; k--) { @@ -1500,7 +1508,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) gdi.addString("", 1, "Skapar ny tävling"); oe->newCompetition(L"New"); oe->importXML_EntryData(gdi, tEvent, false, false, noFilter); - oe->setZeroTime(formatTimeHMSW(zeroTime)); + oe->setZeroTime(formatTimeHMS(zeroTime)); oe->getDI().setDate("OrdinaryEntry", lastEntry); if (ci) { if (!ci->account.empty()) @@ -1540,7 +1548,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) switch (startType) { case SMCommon: - oe->automaticDrawAll(gdi, formatTimeHMSW(firstStart), L"0", L"0", false, false, 1); + oe->automaticDrawAll(gdi, formatTimeHMS(firstStart), L"0", L"0", false, false, 1); drawn = true; break; @@ -1565,7 +1573,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) } } if (!skip) - oe->automaticDrawAll(gdi, formatTimeHMSW(firstStart), L"2:00", L"2", true, true, 1); + oe->automaticDrawAll(gdi, formatTimeHMS(firstStart), L"2:00", L"2", true, true, 1); drawn = true; break; } @@ -1712,23 +1720,14 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) int filterIndex = gdi.getSelectedItem("Type").first; vector< pair > ext; ImportFormats::getExportFilters(bi.id!="BrowseExport", ext); - /*if (bi.id=="BrowseExport") { - ext.push_back(make_pair("IOF Startlista (xml)", "*.xml")); - ext.push_back(make_pair("OE Semikolonseparerad (csv)", "*.csv")); - ext.push_back(make_pair("Webbdokument (html)", "*.html;*.htm")); - } - else { - ext.push_back(make_pair("IOF Resultat (xml)", "*.xml")); - ext.push_back(make_pair("OE Semikolonseparerad (csv)", "*.csv")); - ext.push_back(make_pair("Webbdokument (html)", "*.html")); - }*/ wstring save = gdi.browseForSave(ext, L"xml", filterIndex); if (save.length() > 0) { gdi.setText("Filename", save); gdi.selectItemByData("Type", filterIndex); - if (gdi.getExtra("Filename")) { - gdi.enableInput((char *)gdi.getExtra("Filename")); + wchar_t *fn = gdi.getExtra("Filename"); + if (fn) { + gdi.enableInput(gdi.narrow(fn).c_str()); } } } @@ -1784,6 +1783,8 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) //bool individual = !gdi.hasField("ExportTeam") || gdi.isChecked("ExportTeam"); gdi.getSelection("ClassNewEntries", allTransfer); + checkReadyForResultExport(gdi, allTransfer); + ImportFormats::ExportFormats filterIndex = ImportFormats::setExportFormat(*oe, gdi.getSelectedItem("Type").first); int cSVLanguageHeaderIndex = gdi.getSelectedItem("LanguageType").first; bool includeSplits = gdi.isChecked("ExportSplitTimes"); @@ -1954,7 +1955,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) } if (oe->isClient() && oe->getPropertyInt("UseDirectSocket", true) != 0) { - oe->getDirectSocket().startUDPSocketThread(gdi.getMain()); + oe->getDirectSocket().startUDPSocketThread(gdi.getHWNDMain()); } return 0; } @@ -2152,7 +2153,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) int textSize = lbi.data; oe->setProperty("TextSize", textSize); gdi.setFont(textSize, oe->getPropertyString("TextFont", L"Arial")); - PostMessage(gdi.getTarget(), WM_USER + 2, TCmpTab, 0); + PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TCmpTab, 0); } else if (lbi.id == "Language") { lang.get().loadLangResource(lbi.text); @@ -2160,7 +2161,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) oe->setProperty("Language", lbi.text); //gdi.setEncoding(interpetEncoding(lang.tl("encoding"))); gdi.setFont(oe->getPropertyInt("TextSize", 0), oe->getPropertyString("TextFont", L"Arial")); - PostMessage(gdi.getTarget(), WM_USER + 2, TCmpTab, 0); + PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TCmpTab, 0); } else if (lbi.id == "PreEvent") { gdi.setInputStatus("OpenPre", int(lbi.data)>0); @@ -2183,8 +2184,9 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) else if (type== GUI_INPUT) { InputInfo ii=*(InputInfo *)data; if (ii.id == "Filename") { - if (ii.getExtra()) { - gdi.setInputStatus((char *)ii.getExtra(), !ii.text.empty()); + const wchar_t *fn = ii.getExtra(); + if (fn) { + gdi.setInputStatus(gdi.narrow(fn).c_str(), !ii.text.empty()); } } else if (ii.id == "NumStages") { @@ -2196,8 +2198,9 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data) else if (type == GUI_INPUTCHANGE) { InputInfo ii=*(InputInfo *)data; if (ii.id == "Filename") { - if (ii.getExtra()) { - gdi.setInputStatus((char *)ii.getExtra(), !ii.text.empty()); + const wchar_t *fn = ii.getExtra(); + if (fn) { + gdi.setInputStatus(gdi.narrow(fn).c_str(), !ii.text.empty()); } } } @@ -2537,7 +2540,6 @@ bool TabCompetition::loadPage(gdioutput &gdi) } gdi.addButton("Settings", "Tävlingsinställningar", CompetitionCB); - gdi.addButton("Report", "Tävlingsrapport", CompetitionCB); gdi.addButton("Features", "MeOS Funktioner", CompetitionCB); #ifdef _DEBUG @@ -2849,7 +2851,7 @@ void TabCompetition::getEventorCompetitions(gdioutput &gdi, nt -= 24*3600; dayOffset = 1; } - ci.firstStart = formatTimeHMSW(nt); + ci.firstStart = formatTimeHMS(nt); //TODO: Take dayoffset into account } @@ -2890,7 +2892,7 @@ void TabCompetition::getEventorCompetitions(gdioutput &gdi, SYSTEMTIME st; convertDateYMS(breakDate, st, false); __int64 time = SystemTimeToInt64Second(st) - 1; - breakDate = convertSystemDateW(Int64SecondToSystemTime(time)); + breakDate = convertSystemDate(Int64SecondToSystemTime(time)); if (ci.lastNormalEntryDate.empty() || ci.lastNormalEntryDate >= breakDate) ci.lastNormalEntryDate = breakDate; @@ -2917,7 +2919,7 @@ void TabCompetition::getEventorCmpData(gdioutput &gdi, int id, const wstring &entryFile, const wstring &dbFile) const { - ProgressWindow pw(gdi.getHWND()); + ProgressWindow pw(gdi.getHWNDTarget()); pw.init(); gdi.fillDown(); gdi.addString("", 1, "Ansluter till Internet").setColor(colorGreen); @@ -3981,3 +3983,38 @@ void TabCompetition::saveSettings(gdioutput &gdi) { else if (changedCardFee) oe->applyEventFees(false, false, true, dummy); } + +void TabCompetition::checkReadyForResultExport(gdioutput &gdi, const set &classFilter) { + vector runners; + oe->getRunners(0, 0, runners, true); + int numNoResult = 0; + int numVacant = 0; + + for (pRunner r : runners) { + if (!classFilter.empty() && !classFilter.count(r->getClassId())) + continue; + + if (r->isVacant()) + numVacant++; + else if (r->getStatus() == StatusUnknown && !r->needNoCard()) + numNoResult++; + } + + if (numVacant > 0) { + if (gdi.ask(L"ask:hasVacant")) { + if (gdi.ask(L"Vill du radera alla vakanser från tävlingen?")) { + if (classFilter.empty()) + oe->removeVacanies(0); + else { + for (int c : classFilter) + oe->removeVacanies(c); + } + } + } + } + + if (numNoResult > 0) { + gdi.alert(L"warn:missingResult#" + itow(numNoResult)); + } +} + diff --git a/code/TabCompetition.h b/code/TabCompetition.h index f1f2299..1823762 100644 --- a/code/TabCompetition.h +++ b/code/TabCompetition.h @@ -116,6 +116,9 @@ class TabCompetition : set allTransfer; + + void checkReadyForResultExport(gdioutput &gdi, const set &classFilter); + void displayRunners(gdioutput &gdi, const vector &changedClass) const; void meosFeatures(gdioutput &gdi, bool newGuide); diff --git a/code/TabControl.cpp b/code/TabControl.cpp index 62ee074..d44a69e 100644 --- a/code/TabControl.cpp +++ b/code/TabControl.cpp @@ -79,18 +79,19 @@ void TabControl::selectControl(gdioutput &gdi, pControl pc) const int numVisit = pc->getNumVisitors(true); const int numVisitExp = pc->getNumVisitors(false); - string info; + wstring info; if (numVisit > 0) { - info = "Antal besökare X, genomsnittlig bomtid Y, största bomtid Z#" + - itos(numVisit) + " (" + itos(numVisitExp) + ")#" + getTimeMS(pc->getMissedTimeTotal() / numVisit) + - "#" + getTimeMS(pc->getMissedTimeMax()); + info = L"Antal besökare X, genomsnittlig bomtid Y, största bomtid Z#" + + itow(numVisit) + L" (" + itow(numVisitExp) + L")#" + getTimeMS(pc->getMissedTimeTotal() / numVisit) + + L"#" + getTimeMS(pc->getMissedTimeMax()); } else if (numVisitExp > 0) { - info = "Förväntat antal besökare: X#" + itos(numVisitExp); + info = L"Förväntat antal besökare: X#" + itow(numVisitExp); } gdi.setText("ControlID", itow(pc->getId()), true); - gdi.setText("Info", lang.tl(info), true); + wstring winfo = lang.tl(info); + gdi.setText("Info", winfo, true); gdi.setText("Code", pc->codeNumbers()); gdi.setText("Name", pc->getName()); gdi.setText("TimeAdjust", pc->getTimeAdjustS()); diff --git a/code/TabCourse.cpp b/code/TabCourse.cpp index 46b621e..488b532 100644 --- a/code/TabCourse.cpp +++ b/code/TabCourse.cpp @@ -95,7 +95,7 @@ void TabCourse::selectCourse(gdioutput &gdi, pCourse pc) if ( rt > 0 ) { gdi.selectItemByData("Rogaining", 1); gdi.enableInput("TimeLimit"); - gdi.setText("TimeLimit", formatTimeHMSW(rt)); + gdi.setText("TimeLimit", formatTimeHMS(rt)); gdi.enableInput("PointReduction"); gdi.setText("PointReduction", itow(pc->getRogainingPointsPerMinute())); gdi.enableInput("ReductionPerMinute"); @@ -506,7 +506,7 @@ int TabCourse::courseCB(gdioutput &gdi, int type, void *data) int interval = 2*60; int vac = 1; gdi.addInput("FirstStart", oe->getAbsTime(firstStart), 10, 0, L"Första start:"); - gdi.addInput("Interval", formatTimeW(interval), 10, 0, L"Startintervall (min):"); + gdi.addInput("Interval", formatTime(interval), 10, 0, L"Startintervall (min):"); gdi.addInput("Vacances", itow(vac), 10, 0, L"Antal vakanser:"); gdi.fillDown(); gdi.popX(); diff --git a/code/TabList.cpp b/code/TabList.cpp index 14022ae..3e6e86c 100644 --- a/code/TabList.cpp +++ b/code/TabList.cpp @@ -49,9 +49,11 @@ #include "methodeditor.h" #include "MeOSFeatures.h" #include "liveresult.h" +#include "animationdata.h" #include const static int CUSTOM_OFFSET = 10; +const static int NUMTEXTSAMPLE = 13; TabList::TabList(oEvent *poe):TabBase(poe) { @@ -90,7 +92,7 @@ int ListsEventCB(gdioutput *gdi, int type, void *data) void TabList::rebuildList(gdioutput &gdi) { - if (!SelectedList.empty()) { + if (!SelectedList.empty()) { ButtonInfo bi; bi.id=SelectedList; noReEvaluate = true; @@ -127,7 +129,7 @@ int NoStartRunnerCB(gdioutput *gdi, int type, void *data) TabList &tc = dynamic_cast(*gdi->getTabs().get(TListTab)); pRunner p = tc.getEvent()->getRunner(id, 0); - if (p){ + if (p) { p->setStatus(StatusDNS, true, false); p->synchronize(); ti->callBack=0; @@ -172,34 +174,33 @@ int TabList::baseButtons(gdioutput &gdi, int extraButtons) { return ypos; } -void TabList::generateList(gdioutput &gdi) +void TabList::generateList(gdioutput &gdi, bool forceUpdate) { if (currentList.getListCode() == EFixedLiveResult) { liveResult(gdi, currentList); - + int baseY = 15; - if (!gdi.isFullScreen()) { - gdi.addButton(gdi.getWidth()+20, baseY, gdi.scaleLength(120), - "Cancel", ownWindow ? "Stäng" : "Återgå", ListsCB, "", true, false); + if (!gdi.isFullScreen()) { + gdi.addButton(gdi.getWidth() + 20, baseY, gdi.scaleLength(120), + "Cancel", ownWindow ? "Stäng" : "Återgå", ListsCB, "", true, false); baseY += 3 + gdi.getButtonHeight(); - gdi.addButton(gdi.getWidth()+20, baseY, gdi.scaleLength(120), - "FullScreenLive", "Fullskärm", ListsCB, "Visa listan i fullskärm", true, false); + gdi.addButton(gdi.getWidth() + 20, baseY, gdi.scaleLength(120), + "FullScreenLive", "Fullskärm", ListsCB, "Visa listan i fullskärm", true, false); } - SelectedList="GeneralList"; - + SelectedList = "GeneralList"; return; } - + DWORD storedWidth = 0; int oX = 0; int oY = 0; if (gdi.hasData("GeneralList")) { - if (!currentList.needRegenerate(*oe)) + if (!forceUpdate && !currentList.needRegenerate(*oe)) return; gdi.takeShownStringsSnapshot(); - oX=gdi.GetOffsetX(); - oY=gdi.GetOffsetY(); + oX = gdi.GetOffsetX(); + oY = gdi.GetOffsetY(); gdi.getData("GeneralList", storedWidth); gdi.restoreNoUpdate("GeneralList"); } @@ -209,6 +210,17 @@ void TabList::generateList(gdioutput &gdi) gdi.setRestorePoint("GeneralList"); currentList.setCallback(ownWindow ? 0 : openRunnerTeamCB); + const auto &par = currentList.getParam(); + int bgColor = par.bgColor; + + if (bgColor == -1 && par.screenMode == 1) { + bgColor = RGB(255, 255, 255); + } + + gdi.setColorMode(bgColor, + -1, + par.fgColor, + par.bgImage); try { oe->generateList(gdi, !noReEvaluate, currentList, false); } @@ -216,8 +228,15 @@ void TabList::generateList(gdioutput &gdi) wstring err = lang.tl(ex.wwhat()); gdi.addString("", 1, L"List Error: X#" + err).setColor(colorRed); } + bool wasAnimation = false; + if (par.screenMode == 1 && !par.lockUpdate) { + setAnimationMode(gdi); + wasAnimation = true; + } + else { + gdi.setOffset(oX, oY, false); + } - gdi.setOffset(oX, oY, false); int currentWidth = gdi.getWidth(); gdi.setData("GeneralList", currentWidth); @@ -237,13 +256,17 @@ void TabList::generateList(gdioutput &gdi) baseY += 2*(3+gdi.getButtonHeight()); } - baseY += 3 + gdi.getButtonHeight(); + /*baseY += 3 + gdi.getButtonHeight(); gdi.addButton(gdi.getWidth()+20, baseY, gdi.scaleLength(120), "AutoScroll", "Automatisk skroll", ListsCB, "Rulla upp och ner automatiskt", true, false); baseY += 3 + gdi.getButtonHeight(); gdi.addButton(gdi.getWidth()+20, baseY, gdi.scaleLength(120), "FullScreen", "Fullskärm", ListsCB, "Visa listan i fullskärm", true, false); + */ + baseY += 3 + gdi.getButtonHeight(); + gdi.addButton(gdi.getWidth() + 20, baseY, gdi.scaleLength(120), + "ListDesign", "Utseende...", ListsCB, "Justera visningsinställningar", true, false); if (!currentList.getParam().saved && !oe->isReadOnly()) { baseY += 3 + gdi.getButtonHeight(); @@ -260,11 +283,13 @@ void TabList::generateList(gdioutput &gdi) gdi.setOnClearCb(ListsCB); SelectedList="GeneralList"; - if (abs(int(currentWidth - storedWidth)) < 5) { - gdi.refreshSmartFromSnapshot(true); + if (!wasAnimation) { + if (abs(int(currentWidth - storedWidth)) < 5) { + gdi.refreshSmartFromSnapshot(true); + } + else + gdi.refresh(); } - else - gdi.refresh(); } int TabList::listCB(gdioutput &gdi, int type, void *data) @@ -335,22 +360,24 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) gdi.openDoc(file.c_str()); } } + else if (bi.id == "ListDesign") { + gdioutput *gdi_settings = getExtraWindow("list_settings", true); + if (!gdi_settings) { + gdi_settings = createExtraWindow("list_settings", lang.tl("Inställningar"), gdi.scaleLength(600), gdi.scaleLength(400)); + } + if (gdi_settings) { + loadSettings(*gdi_settings, gdi.getTag()); + } + } else if (bi.id == "Window" || bi.id == "AutoScroll" || bi.id == "FullScreen" || bi.id == "FullScreenLive") { gdioutput *gdi_new; TabList *tl_new = this; if (!ownWindow) { - gdi_new = createExtraWindow(uniqueTag("list"), makeDash(L"MeOS - ") + currentList.getName(), gdi.getWidth() + 64 + gdi.scaleLength(120)); - if (gdi_new) { - TabList &tl = dynamic_cast(*gdi_new->getTabs().get(TListTab)); - tl.currentList = currentList; - tl.SelectedList = SelectedList; - tl.ownWindow = true; - tl.loadPage(*gdi_new); - tl_new = &tl; - SelectedList = ""; - currentList = oListInfo(); - loadPage(gdi); + auto nw = makeOwnWindow(gdi); + if (nw.first) { + tl_new = nw.second; + gdi_new = nw.first; } } else @@ -1129,6 +1156,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) else if (type==GUI_CLEAR) { offsetY=gdi.GetOffsetY(); offsetX=gdi.GetOffsetX(); + leavingList(gdi.getTag()); return true; } else if (type == GUI_LINK) { @@ -1192,6 +1220,24 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) return 0; } +pair TabList::makeOwnWindow(gdioutput &gdi) { + gdioutput *gdi_new = createExtraWindow(uniqueTag("list"), makeDash(L"MeOS - ") + currentList.getName(), gdi.getWidth() + 64 + gdi.scaleLength(120)); + TabList *tl_new = 0; + if (gdi_new) { + TabList &tl = dynamic_cast(*gdi_new->getTabs().get(TListTab)); + tl.currentList = currentList; + tl.SelectedList = SelectedList; + tl.ownWindow = true; + tl.loadPage(*gdi_new); + tl_new = &tl; + changeListSettingsTarget(gdi, *gdi_new); + SelectedList = ""; + currentList = oListInfo(); + loadPage(gdi); + } + return make_pair(gdi_new, tl_new); +} + void TabList::enableFromTo(oEvent &oe, gdioutput &gdi, bool from, bool to) { vector< pair > d; oe.fillControls(d, oEvent::CTCourseControl); @@ -1376,6 +1422,272 @@ void TabList::makeFromTo(gdioutput &gdi) { gdi.dropLine(3); } +class ListSettings : public GuiHandler { + void handle(gdioutput &gdi, BaseInfo &info, GuiEventType type) { + string target; + if (!gdi.getData("target", target)) + return; + gdioutput *dest_gdi = getExtraWindow(target, false); + if (!dest_gdi) + return; + + TabBase *tb = dest_gdi->getTabs().get(TabType::TListTab); + if (tb) { + TabList *list = dynamic_cast(tb); + list->handleListSettings(gdi, info, type, *dest_gdi); + } + } +}; + +ListSettings settingsClass; + +void TabList::changeListSettingsTarget(gdioutput &oldWindow, gdioutput &newWindow) { + gdioutput *gdi_settings = getExtraWindow("list_settings", true); + if (gdi_settings) { + string oldTag; + gdi_settings->getData("target", oldTag); + if (oldWindow.getTag() == oldTag) + gdi_settings->setData("target", newWindow.getTag()); + } +} + +void TabList::leavingList(const string &wnd) { + gdioutput *gdi_settings = getExtraWindow("list_settings", true); + if (gdi_settings) { + string oldTag; + gdi_settings->getData("target", oldTag); + if (wnd == oldTag) + gdi_settings->closeWindow(); + } +} + +static void addAnimationSettings(gdioutput &gdi, oListParam &dst) { + DWORD cx, cy; + gdi.getData("xmode", cx); + gdi.getData("ymode", cy); + + gdi.setCX(cx); + gdi.setCY(cy); + gdi.pushX(); + gdi.fillRight(); + gdi.addInput("Time", itow(dst.timePerPage), 5, 0, L"Visningstid:"); + gdi.addSelection("NPage", 70, 200, 0, L"Sidor per skärm:"); + for (int i = 1; i <= 8; i++) + gdi.addItem("NPage", itow(i), i); + if (dst.nColumns == 0) + dst.nColumns = 1; + gdi.selectItemByData("NPage", dst.nColumns); + gdi.addInput("Margin", itow(dst.margin) + L" %", 5, 0, L"Marginal:"); + gdi.dropLine(1); + gdi.addCheckbox("Animate", "Animation", 0, dst.animate); +} + +static void saveAnimationSettings(gdioutput &gdi, oListParam &dst) { + dst.timePerPage = gdi.getTextNo("Time"); + dst.nColumns = gdi.getSelectedItem("NPage").first; + dst.animate = gdi.isChecked("Animate"); + dst.margin = gdi.getTextNo("Margin"); +} + +void TabList::loadSettings(gdioutput &gdi, string targetTag) { + gdi.clearPage(false); + gdi.setCX(10); + gdi.setCY(15); + gdi.setColorMode(RGB(242, 240, 250)); + gdi.setData("target", targetTag); + settingsTarget = targetTag; + gdi.addString("", fontMediumPlus, L"Visningsinställningar för 'X'#" + currentList.getName()); + + gdi.dropLine(0.5); + gdi.addSelection("Background", 200, 100, 0, L"Bakgrund:").setHandler(&settingsClass); + gdi.addItem("Background", lang.tl("Standard"), 0); + gdi.addItem("Background", lang.tl("Färg"), 1); + //gdi.addItem("Background", lang.tl("Bild"), 2); + tmpSettingsParam = currentList.getParam(); + int bgColor = currentList.getParam().bgColor; + int fgColor = currentList.getParam().fgColor; + bool useColor = bgColor != -1; + gdi.selectItemByData("Background", useColor ? 1 : 0); + gdi.pushX(); + gdi.fillRight(); + gdi.addButton("BGColor", "Bakgrundsfärg...").setHandler(&settingsClass).setExtra(bgColor); + gdi.setInputStatus("BGColor", useColor); + gdi.addButton("FGColor", "Textfärg...").setHandler(&settingsClass).setExtra(fgColor); + + gdi.popX(); + + gdi.dropLine(3); + gdi.addSelection("Mode", 200, 100, 0, L"Visning:").setHandler(&settingsClass); + gdi.addItem("Mode", lang.tl("Fönster"), 0); + gdi.addItem("Mode", lang.tl("Fönster (rullande)"), 3); + gdi.addItem("Mode", lang.tl("Fullskärm (sidvis)"), 1); + gdi.addItem("Mode", lang.tl("Fullskärm (rullande)"), 2); + gdi.selectItemByData("Mode", tmpSettingsParam.screenMode); + gdi.popX(); + gdi.dropLine(3); + + gdi.setData("xmode", gdi.getCX()); + gdi.setData("ymode", gdi.getCY()); + gdi.dropLine(3); + + gdi.addButton("ApplyList", "Verkställ").setHandler(&settingsClass); + + if (tmpSettingsParam.screenMode == 1) + addAnimationSettings(gdi, tmpSettingsParam); + + RECT rc; + rc.left = gdi.getWidth() + gdi.scaleLength(80); + rc.right = rc.left + gdi.scaleLength(150); + rc.top = 20; + + gdi.addString("", rc.top, rc.left, 1, "Exempel"); + rc.top += (gdi.getLineHeight() * 3) / 2; + + rc.bottom = rc.top + gdi.scaleLength(200); + gdi.addRectangle(rc, bgColor != -1 ? GDICOLOR(bgColor) : GDICOLOR(colorTransparent)).id = "Background"; + string val = "123. Abc MeOS"; + int key = rand()%12; + for (int i = 0; i < NUMTEXTSAMPLE; i++) { + gdi.addString("Sample" + itos(i), rc.top + 3 + gdi.getLineHeight()*i, + rc.left + 3 + 5*i, i == 0 ? boldText : normalText, "#" + val).setColor(GDICOLOR(fgColor)); + string val2 = val; + for (int j = 0; j < 13; j++) { + val2[j] = val[((j+1)*(key+1)) % 13]; + } + val = val2; + } + gdi.refresh(); +} + +void TabList::handleListSettings(gdioutput &gdi, BaseInfo &info, GuiEventType type, gdioutput &dest_gdi) { + if (type == GUI_BUTTON) { + ButtonInfo bi = static_cast(info); + if (bi.id == "BGColor") { + wstring c = oe->getPropertyString("Colors", L""); + int res = gdi.selectColor(c, bi.getExtraInt()); + if (res > -1) { + info.setExtra(res); + oe->setProperty("Colors", c); + RectangleInfo &rc = gdi.getRectangle("Background"); + rc.setColor(GDICOLOR(res)); + gdi.refreshFast(); + } + } + else if (bi.id == "FGColor") { + wstring c = oe->getPropertyString("Colors", L""); + int inC = bi.getExtraInt(); + if (inC == -1) + inC = RGB(255,255,255); + int res = gdi.selectColor(c, inC); + if (res > -1) { + info.setExtra(res); + oe->setProperty("Colors", c); + for (int i = 0; i < NUMTEXTSAMPLE; i++) { + BaseInfo &bi = gdi.getBaseInfo(("Sample" + itos(i)).c_str()); + dynamic_cast(bi).setColor(GDICOLOR(res)); + } + gdi.refreshFast(); + } + } + else if (bi.id == "ApplyList") { + oListParam ¶m = currentList.getParam(); + param.lockUpdate = true; + int type = gdi.getSelectedItem("Background").first; + if (type == 1) + param.bgColor = gdi.getExtraInt("BGColor"); + else + param.bgColor = -1; + + param.fgColor = gdi.getExtraInt("FGColor"); + param.screenMode = gdi.getSelectedItem("Mode").first; + if (param.screenMode == 1) { + saveAnimationSettings(gdi, param); + } + TabList *dest = this; + gdioutput *dgdi = &dest_gdi; + int mode = param.screenMode; + if (param.screenMode == 2 || param.screenMode == 3) { + dgdi->alert("help:fullscreen"); + } + + if ((mode==1 || mode==2) && !dest_gdi.isFullScreen()) { + // Require fullscreen + if (!ownWindow) { + auto nw = makeOwnWindow(dest_gdi); + dest = nw.second; + dgdi = nw.first; + } + dgdi->setFullScreen(true); + dest->hideButtons = true; + } + else if ((mode == 0 || mode == 3) && dest_gdi.isFullScreen()) { + dest_gdi.setFullScreen(false); + hideButtons = false; + } + + if (mode == 2 || mode == 3) { + if (!dest->ownWindow) { + auto nw = makeOwnWindow(dest_gdi); + dest = nw.second; + dgdi = nw.first; + } + dest->hideButtons = true; + int h = dgdi->setHighContrastMaxWidth(); + dest->loadPage(*dgdi); + double sec = 6.0; + double delta = h * 20. / (1000. * sec); + dgdi->setAutoScroll(delta); + } + else { + dest->loadPage(*dgdi); + } + dest->currentList.getParam().lockUpdate = false; + param.lockUpdate = false; + + SetForegroundWindow(dgdi->getHWNDMain()); + SetWindowPos(dgdi->getHWNDMain(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); + + if (mode == 1) { + dest->setAnimationMode(*dgdi); + dgdi->refresh(); + dest->generateList(*dgdi, true); + } + + if (param.screenMode == 2 || param.screenMode == 3) { + gdi.closeWindow(); + } + } + } + else if (type == GUI_LISTBOX) { + ListBoxInfo lbi = static_cast(info); + if (lbi.id == "Background") { + gdi.setInputStatus("BGColor", lbi.data == 1); + BaseInfo &bi = gdi.getBaseInfo("BGColor"); + if (lbi.data == 1 && bi.getExtraInt() == -1) + bi.setExtra(int(RGB(255,255,255))); + + RectangleInfo &rc = gdi.getRectangle("Background"); + rc.setColor(GDICOLOR(lbi.data == 1 ? bi.getExtraInt() : colorTransparent)); + gdi.refreshFast(); + } + else if (lbi.id == "Mode") { + if (lbi.data == 1) { + addAnimationSettings(gdi, tmpSettingsParam); + } + else { + if (gdi.hasField("Time")) + saveAnimationSettings(gdi, tmpSettingsParam); + + gdi.removeControl("Time"); + gdi.removeControl("NPage"); + gdi.removeControl("Margin"); + gdi.removeControl("Animate"); + } + gdi.refresh(); + } + } +} + void TabList::settingsResultList(gdioutput &gdi) { lastFilledResultClassType = -1; @@ -1569,7 +1881,8 @@ bool TabList::loadPage(gdioutput &gdi) if (cnf.hasIndividual()) { gdi.addButton("StartIndividual", "Individuell", ListsCB); checkWidth(gdi); - gdi.addButton("StartClub", "Klubbstartlista", ListsCB); + if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Clubs)) + gdi.addButton("StartClub", "Klubbstartlista", ListsCB); } for (size_t k = 0; kgetMeOSFeatures().hasFeature(MeOSFeatures::Clubs)) { + gdi.addButton("ResultClub", "Klubbresultat", ListsCB); + checkWidth(gdi); + } + gdi.addButton("ResultIndSplit", "Sträcktider", ListsCB); checkWidth(gdi); @@ -2098,3 +2414,9 @@ void TabList::clearCompetitionData() { listEditor = 0; methodEditor = 0; } + +void TabList::setAnimationMode(gdioutput &gdi) { + auto par = currentList.getParam(); + gdi.setAnimationMode(make_shared(gdi, par.timePerPage, par.nColumns, + par.margin, par.animate)); +} diff --git a/code/TabList.h b/code/TabList.h index 4c1b9ec..5542724 100644 --- a/code/TabList.h +++ b/code/TabList.h @@ -48,7 +48,7 @@ protected: static void createListButtons(gdioutput &gdi); - void generateList(gdioutput &gdi); + void generateList(gdioutput &gdi, bool forceUpdate = false); void selectGeneralList(gdioutput &gdi, EStdListType type); int offsetY; @@ -78,6 +78,15 @@ private: TabList(const TabList &); const TabList &operator = (const TabList &); + string settingsTarget; + oListParam tmpSettingsParam; + void changeListSettingsTarget(gdioutput &oldWindow, gdioutput &newWindow); + void leavingList(const string &wnd); + + pair makeOwnWindow(gdioutput &gdi); + + /** Set animation mode*/ + void setAnimationMode(gdioutput &gdi); public: bool loadPage(gdioutput &gdi); bool loadPage(gdioutput &gdi, const string &command); @@ -94,6 +103,8 @@ public: void rebuildList(gdioutput &gdi); void settingsResultList(gdioutput &gdi); + void loadSettings(gdioutput &gdi, string targetTag); + void handleListSettings(gdioutput &gdi, BaseInfo &info, GuiEventType type, gdioutput &dest_gdi); enum PrintSettingsSelection { Splits = 0, StartInfo = 1, diff --git a/code/TabRunner.cpp b/code/TabRunner.cpp index f434553..5655253 100644 --- a/code/TabRunner.cpp +++ b/code/TabRunner.cpp @@ -199,7 +199,7 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { gdi.setText("Team", L""); } - gdi.setText("TimeAdjust", getTimeMSW(r->getTimeAdjustment())); + gdi.setText("TimeAdjust", getTimeMS(r->getTimeAdjustment())); gdi.setText("PointAdjust", -r->getPointAdjustment()); #ifdef _DEBUG @@ -216,22 +216,22 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { r->getLegTimeAfterAcc(afterAcc); r->getLegPlacesAcc(placeAcc); - string out; + wstring out; for (size_t k = 0; k0) - out+= " +" + getTimeMS(after[k]); + out+= L" +" + getTimeMS(after[k]); if (k0) - out+= " (+" + getTimeMS(afterAcc[k]) + ")"; + out+= L" (+" + getTimeMS(afterAcc[k]) + L")"; if (delta[k]>0) - out+= " B: " + getTimeMS(delta[k]); + out+= L" B: " + getTimeMS(delta[k]); - out += " | "; + out += L" | "; } gdi.restore("fantom", false); @@ -805,6 +805,7 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) for (size_t k=0; kgetStatus()==StatusUnknown) { unknown[k]->setStatus(StatusDNS, true, false); + unknown[k]->setFlag(oAbstractRunner::FlagAutoDNS, true); unknown[k]->synchronize(true); } } @@ -813,6 +814,21 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) clearInForestData(); showInForestList(gdi); } + else if (bi.id == "UndoSetDNS") { + vector runners; + oe->getRunners(0, 0, runners, true); + for (pRunner r : runners) { + if (r->getStatus() == StatusDNS && r->hasFlag(oAbstractRunner::FlagAutoDNS)) { + r->setStatus(StatusUnknown, true, false); + r->setFlag(oAbstractRunner::FlagAutoDNS, false); + r->synchronize(true); + } + } + //Reevaluate and synchronize all + oe->reEvaluateAll(set(), true); + clearInForestData(); + showInForestList(gdi); + } else if (bi.id=="SetUnknown") { for (size_t k=0; kgetStatus()==StatusDNS) { @@ -842,7 +858,7 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) pRunner r=oe->getRunner(runnerId, 0); if (!r) return 0; - gdioutput gdiprint(2.0, gdi.getHWND(), splitPrinter, gdi.getCP()); + gdioutput gdiprint(2.0, gdi.getHWNDTarget(), splitPrinter, gdi.getCP()); if (bi.getExtraInt() == 0) r->printSplits(gdiprint); else @@ -917,6 +933,14 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) selectRunner(gdi, 0); } } + } + else if (bi.id == "Economy") { + if (!runnerId) + return 0; + pRunner r = oe->getRunner(runnerId, 0); + gdioutput *settings = createExtraWindow("ecosettings", L"Economy for X#" + r->getName(), 400, 200); + + } else if (bi.id=="NoStart") { if (!runnerId) @@ -938,8 +962,8 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) pRunner rr = r->getMultiRunner(k); if (rr) { rr->setStartTime(0, true, false); - rr->setStartNo(0, false); - rr->setStatus(StatusDNS, true, false); + //rr->setStartNo(0, false); + rr->setStatus(StatusCANCEL, true, false); rr->setCardNo(0, false); } } @@ -1051,7 +1075,7 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) } runnerId = bi.data; //loadPage(gdi); - PostMessage(gdi.getTarget(), WM_USER + 2, TRunnerTab, 0); + PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TRunnerTab, 0); } else if (bi.id=="RClass") { gdi.selectItemByData("RCourse", 0); @@ -1318,7 +1342,8 @@ int TabRunner::vacancyCB(gdioutput &gdi, int type, void *data) } } - r->getDI().setDate("EntryDate", getLocalDateW()); + r->getDI().setDate("EntryDate", getLocalDate()); + r->getDI().setInt("EntryTime", getLocalAbsTime()); r->addClassDefaultFee(false); int cardFee = 0; @@ -1328,8 +1353,6 @@ int TabRunner::vacancyCB(gdioutput &gdi, int type, void *data) } else r->getDI().setInt("CardFee", 0); - - if (cardFee < 0) cardFee = 0; @@ -1482,7 +1505,7 @@ void TabRunner::showRunnerReport(gdioutput &gdi) if (t->statusOK()) { tInfo += L", " + t->getRunningTimeS() + lang.tl(".S Placering: ") + t->getPlaceS(); if (t->getTimeAfter(-1) > 0) - tInfo += L", +" + formatTimeW(t->getTimeAfter(-1)); + tInfo += L", +" + formatTime(t->getTimeAfter(-1)); } else if (t->getStatus() != StatusUnknown) { tInfo += L" " + t->getStatusS(); @@ -1640,7 +1663,7 @@ void TabRunner::runnerReport(gdioutput &gdi, int id, bool compact) { GDICOLOR color = colorDefault; if (k < int(after.size()) ) { if (after[k] > 0) - split += L" (" + itow(place[k]) + L", +" + getTimeMSW(after[k]) + L")"; + split += L" (" + itow(place[k]) + L", +" + getTimeMS(after[k]) + L")"; else if (place[k] == 1) split += lang.tl(" (sträckseger)"); else if (place[k] > 0) @@ -1656,7 +1679,7 @@ void TabRunner::runnerReport(gdioutput &gdi, int id, bool compact) { wstring pl = placeAcc[k] > 0 ? itow(placeAcc[k]) : L"-"; if (k < int(afterAcc.size()) ) { if (afterAcc[k] > 0) - split += L" (" + pl + L", +" + getTimeMSW(afterAcc[k]) + L")"; + split += L" (" + pl + L", +" + getTimeMS(afterAcc[k]) + L")"; else if (placeAcc[k] == 1) split += lang.tl(" (ledare)"); else if (placeAcc[k] > 0) @@ -1666,7 +1689,7 @@ void TabRunner::runnerReport(gdioutput &gdi, int id, bool compact) { } if (k < int(delta.size()) && delta[k] > 0 ) { - gdi.addString("", yp + 3*lh, cx, fontMedium, "Bomtid: X#" + getTimeMS(delta[k])); + gdi.addString("", yp + 3*lh, cx, fontMedium, L"Bomtid: X#" + getTimeMS(delta[k])); color = (delta[k] > bestTime * 0.5 && delta[k]>60 ) ? colorMediumDarkRed : colorMediumRed; @@ -1724,9 +1747,9 @@ void TabRunner::runnerReport(gdioutput &gdi, int id, bool compact) { int st = r->getStartTime(); gdi.addString("", yp + lh, cx, normalText, L"Klocktid: X#" + oe->getAbsTime(t), limit); if (st > 0 && t > st) { - string split = formatTimeHMS(t-st); + wstring split = formatTimeHMS(t-st); if (lastT>0 && st != lastT && lastT < t) - split += " (" + getTimeMS(t-lastT) + ")"; + split += L" (" + getTimeMS(t-lastT) + L")"; gdi.addStringUT(yp + 2*lh, cx, normalText, split, limit); } } @@ -1925,7 +1948,8 @@ void TabRunner::showInForestList(gdioutput &gdi) gdi.popX(); clearInForestData(); - oe->analyseDNS(unknown_dns, known_dns, known, unknown); + bool hasDNS; + oe->analyseDNS(unknown_dns, known_dns, known, unknown, hasDNS); oe->setupCardHash(false); if (!unknown.empty()) { gdi.dropLine(); @@ -1934,7 +1958,14 @@ void TabRunner::showInForestList(gdioutput &gdi) listRunners(gdi, unknown, true); } else { - gdi.disableInput("SetDNS"); + if (hasDNS) { + BaseInfo &bi = gdi.getBaseInfo("SetDNS"); + bi.id = "UndoSetDNS"; + gdi.setTextTranslate(bi.id, L"Återställ till "); + } + else { + gdi.disableInput("SetDNS"); + } } if (!known.empty()) { @@ -2262,10 +2293,10 @@ bool TabRunner::loadPage(gdioutput &gdi) gdi.selectTab(tabId); } gdi.clearPage(false); - int basex=gdi.getCX(); + int basex = gdi.getCX(); if (currentMode == 1) { - Table *tbl=oe->getRunnersTB(); + Table *tbl = oe->getRunnersTB(); addToolbar(gdi); gdi.dropLine(1); gdi.addTable(tbl, basex, gdi.getCY()); @@ -2298,8 +2329,8 @@ bool TabRunner::loadPage(gdioutput &gdi) gdi.registerEvent("SearchRunnerBack", runnerSearchCB).setKeyCommand(KC_FINDBACK); gdi.addInput("SearchText", getSearchString(), 13, runnerSearchCB, L"", - L"Sök på namn, bricka eller startnummer.").isEdit(false) - .setBgColor(colorLightCyan).ignore(true); + L"Sök på namn, bricka eller startnummer.").isEdit(false) + .setBgColor(colorLightCyan).ignore(true); gdi.dropLine(-0.2); //gdi.addButton("Search", "Sök", RunnerCB, "Sök på namn, bricka eller startnummer."); gdi.addButton("ShowAll", "Visa alla", RunnerCB).isEdit(false); @@ -2349,16 +2380,19 @@ bool TabRunner::loadPage(gdioutput &gdi) } gdi.fillRight(); - gdi.addSelection("RClass", 150, 300, RunnerCB, L"Klass:"); + gdi.addSelection("RClass", 130, 300, RunnerCB, L"Klass:"); oe->fillClasses(gdi, "RClass", oEvent::extraNone, oEvent::filterNone); gdi.addItem("RClass", lang.tl("Ingen klass"), 0); - gdi.fillDown(); - - if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Economy)) - gdi.addInput("Fee", L"", 6, 0, L"Avgift:"); - else + if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Economy)) { + gdi.fillDown(); + gdi.addInput("Fee", L"", 4, 0, L"Avgift:"); + //gdi.addButton("Economy", "@" + itos(131), RunnerCB, "Ekonomi..."); + } + else { + gdi.fillDown(); gdi.dropLine(3); + } gdi.dropLine(0.4); diff --git a/code/TabSI.cpp b/code/TabSI.cpp index 6b9b487..c0c8ced 100644 --- a/code/TabSI.cpp +++ b/code/TabSI.cpp @@ -948,7 +948,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) throw meosException("Löparen hittades inte"); bool useNow = gdi.getExtraInt("FinishTime") == 1; - wstring time = useNow ? getLocalTimeOnlyW() : gdi.getText("FinishTime"); + wstring time = useNow ? getLocalTimeOnly() : gdi.getText("FinishTime"); int relTime = oe->getRelativeTime(time); if (relTime <= 0) { @@ -1097,7 +1097,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) } else if (bi.id == "NC") { NC = bi.data; - PostMessage(gdi.getTarget(), WM_USER + 2, TSITab, 0); + PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TSITab, 0); } } else if (type == GUI_LINK) { @@ -1473,7 +1473,7 @@ void TabSI::showReadCards(gdioutput &gdi, vector &cards) SportIdent &TabSI::getSI(const gdioutput &gdi) { if (!gSI) { - HWND hWnd=gdi.getMain(); + HWND hWnd=gdi.getHWNDMain(); gSI = new SportIdent(hWnd, 0); gSI->setZeroTime(gEvent->getZeroTimeNum()); } @@ -2681,7 +2681,7 @@ void TabSI::generateSplits(const pRunner r, gdioutput &gdi) while(checkpPrintQueue(gdi)); } else { - gdioutput gdiprint(2.0, gdi.getHWND(), splitPrinter, gdi.getCP()); + gdioutput gdiprint(2.0, gdi.getHWNDTarget(), splitPrinter, gdi.getCP()); vector mp; r->evaluateCard(true, mp); r->printSplits(gdiprint); @@ -2692,7 +2692,7 @@ void TabSI::generateSplits(const pRunner r, gdioutput &gdi) void TabSI::generateStartInfo(gdioutput &gdi, const oRunner &r) { if (printStartInfo) { - gdioutput gdiprint(2.0, gdi.getHWND(), splitPrinter, gdi.getCP()); + gdioutput gdiprint(2.0, gdi.getHWNDTarget(), splitPrinter, gdi.getCP()); r.printStartInfo(gdiprint); printProtected(gdi, gdiprint); //gdiprint.print(splitPrinter, oe, false, true); @@ -3023,7 +3023,7 @@ void TabSI::EditCardData::handle(gdioutput &gdi, BaseInfo &info, GuiEventType ty void TabSI::printCard(gdioutput &gdi, int cardId, bool forPrinter) const { SICard &c = getCard(cardId); if (c.readOutTime[0] == 0) - strcpy_s(c.readOutTime, getLocalTime().c_str()); + strcpy_s(c.readOutTime, getLocalTimeN().c_str()); gdi.pushX(); gdi.fillRight(); @@ -3066,10 +3066,10 @@ void TabSI::printCard(gdioutput &gdi, int cardId, bool forPrinter) const { int start = NOTIME; if (c.CheckPunch.Code != -1) - gdi.addString("", 0, "Check: X#" + formatTimeHMS(c.CheckPunch.Time)); + gdi.addString("", 0, L"Check: X#" + formatTimeHMS(c.CheckPunch.Time)); if (c.StartPunch.Code != -1) { - gdi.addString("", 0, "Start: X#" + formatTimeHMS(c.StartPunch.Time)); + gdi.addString("", 0, L"Start: X#" + formatTimeHMS(c.StartPunch.Time)); start = c.StartPunch.Time; } int xp = gdi.getCX(); @@ -3088,9 +3088,9 @@ void TabSI::printCard(gdioutput &gdi, int cardId, bool forPrinter) const { if (start != NOTIME) { int legTime = analyzePunch(c.Punch[k], start, accTime, days); if (legTime > 0) - gdi.addStringUT(cy, xp5-gdi.scaleLength(10), textRight, formatTimeW(legTime)); + gdi.addStringUT(cy, xp5-gdi.scaleLength(10), textRight, formatTime(legTime)); - gdi.addStringUT(cy, xp5 + gdi.scaleLength(40), textRight, formatTimeW(days*3600*24 + accTime)); + gdi.addStringUT(cy, xp5 + gdi.scaleLength(40), textRight, formatTime(days*3600*24 + accTime)); } else { start = c.Punch[k].Time; @@ -3104,11 +3104,11 @@ void TabSI::printCard(gdioutput &gdi, int cardId, bool forPrinter) const { if (start != NOTIME) { int legTime = analyzePunch(c.FinishPunch, start, accTime, days); if (legTime > 0) - gdi.addStringUT(cy, xp5-gdi.scaleLength(10), textRight, formatTimeW(legTime)); + gdi.addStringUT(cy, xp5-gdi.scaleLength(10), textRight, formatTime(legTime)); - gdi.addStringUT(cy, xp5 + gdi.scaleLength(40), textRight, formatTimeW(days*3600*24 + accTime)); + gdi.addStringUT(cy, xp5 + gdi.scaleLength(40), textRight, formatTime(days*3600*24 + accTime)); } - gdi.addString("", 1, L"Time: X#" + formatTimeW(days*3600*24 + accTime)); + gdi.addString("", 1, L"Time: X#" + formatTime(days*3600*24 + accTime)); } if (forPrinter) { @@ -3143,7 +3143,7 @@ int TabSI::analyzePunch(SIPunch &p, int &start, int &accTime, int &days) { } void TabSI::generateSplits(int cardId, gdioutput &gdi) { - gdioutput gdiprint(2.0, gdi.getHWND(), splitPrinter, gdi.getCP()); + gdioutput gdiprint(2.0, gdi.getHWNDTarget(), splitPrinter, gdi.getCP()); printCard(gdiprint, cardId, true); printProtected(gdi, gdiprint); } @@ -3196,7 +3196,7 @@ void TabSI::createCompetitionFromCards(gdioutput &gdi) { if (zeroTime < 0) zeroTime += 3600 * 24; zeroTime -= zeroTime % 1800; - oe->setZeroTime(formatTimeW(zeroTime)); + oe->setZeroTime(formatTime(zeroTime)); int course = 0; for (size_t k = 0; k < cards.size(); k++) { @@ -3570,7 +3570,7 @@ bool TabSI::checkpPrintQueue(gdioutput &gdi) { return false; // Wait a little longer } - gdioutput gdiprint(2.0, gdi.getHWND(), splitPrinter, gdi.getCP()); + gdioutput gdiprint(2.0, gdi.getHWNDTarget(), splitPrinter, gdi.getCP()); vector mp; for (size_t m = 0; m < printLen && !printPunchRunnerIdQueue.empty(); m++) { int rid = printPunchRunnerIdQueue.front().second; diff --git a/code/TabSpeaker.cpp b/code/TabSpeaker.cpp index 4e5d678..33e39f0 100644 --- a/code/TabSpeaker.cpp +++ b/code/TabSpeaker.cpp @@ -662,7 +662,7 @@ void TabSpeaker::splitAnalysis(gdioutput &gdi, int xp, int yp, pRunner r) else first = false; - timeloss += pc->getControlOrdinal(j) + L". " + formatTimeW(delta[j]); + timeloss += pc->getControlOrdinal(j) + L". " + formatTime(delta[j]); } if (timeloss.length() > charlimit || (!timeloss.empty() && !first && j+1 == delta.size())) { gdi.addStringUT(yp, xp, 0, timeloss).setColor(colorDarkRed); @@ -876,7 +876,7 @@ int TabSpeaker::processListBox(gdioutput &gdi, const ListBoxInfo &bu) } } else if (bu.id == "MultiStage") { - getSpeakerMonitor()->useTotalResults(gdi.isChecked(bu.id)); + getSpeakerMonitor()->useTotalResults(bu.data != 0); updateTimeLine(gdi); } else if (bu.id == "DetailLevel") { @@ -1087,7 +1087,7 @@ void TabSpeaker::storeManualTime(gdioutput &gdi) wstring time=gdi.getText("Time"); if (time.empty()) - time=getLocalTimeOnlyW(); + time=getLocalTimeOnly(); int itime=oe->getRelativeTime(time); @@ -1240,7 +1240,7 @@ void TabSpeaker::importSettings(gdioutput &gdi, multimap &setti selectedControl.clear(); int ctrl = 0, leg = 0, total = 0; - for (auto s : settings) { + for (auto &s : settings) { if (s.first == "currentClass") { if (s.second == L"@Events") { classId = -1; @@ -1312,11 +1312,11 @@ void TabSpeaker::loadSettings(vector< multimap > &settings) { xmlList xmlsettings; sp.getObjects(xmlsettings); - for (auto s : xmlsettings) { + for (auto &s : xmlsettings) { settings.push_back(multimap()); xmlList allS; s.getObjects(allS); - for (auto prop : allS) { + for (auto &prop : allS) { settings.back().insert(make_pair(prop.getName(), prop.getw())); } } @@ -1326,9 +1326,9 @@ void TabSpeaker::saveSettings(const vector< multimap > &setting xmlparser d; d.openOutput(getSpeakerSettingsFile().c_str(), false); d.startTag("Speaker"); - for (auto s : settings) { + for (auto &s : settings) { d.startTag("SpeakerWindow"); - for (auto prop : s) { + for (auto &prop : s) { d.write(prop.first.c_str(), prop.second); } d.endTag(); diff --git a/code/TabTeam.cpp b/code/TabTeam.cpp index 5f1e26b..dfc8aa7 100644 --- a/code/TabTeam.cpp +++ b/code/TabTeam.cpp @@ -270,7 +270,7 @@ void TabTeam::updateTeamStatus(gdioutput &gdi, pTeam t) gdi.setText("Start", t->getStartTimeS()); gdi.setText("Finish",t->getFinishTimeS()); gdi.setText("Time", t->getRunningTimeS()); - gdi.setText("TimeAdjust", getTimeMSW(t->getTimeAdjustment())); + gdi.setText("TimeAdjust", getTimeMS(t->getTimeAdjustment())); gdi.setText("PointAdjust", -t->getPointAdjustment()); gdi.selectItemByData("Status", t->getStatus()); } @@ -342,11 +342,11 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) { RunnerStatus sIn = (RunnerStatus)lbi.data; // Must be done AFTER all runners are set. But setting runner can modify status, so decide here. - bool setDNS = (sIn == StatusDNS) && (t->getStatus()!=StatusDNS); + bool setDNS = (sIn == StatusDNS || sIn == StatusCANCEL) && (t->getStatus() != sIn); bool checkStatus = (sIn != t->getStatus()); - if (sIn == StatusUnknown && t->getStatus() == StatusDNS) - t->setTeamNoStart(false); + if (sIn == StatusUnknown && (t->getStatus() == StatusDNS || t->getStatus() == StatusCANCEL)) + t->setTeamNoStart(false, StatusDNS); else if ((RunnerStatus)lbi.data != t->getStatus()) t->setStatus((RunnerStatus)lbi.data, true, false); @@ -464,7 +464,7 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) { } if (setDNS) - t->setTeamNoStart(true); + t->setTeamNoStart(true, sIn); if (t->checkValdParSetup()) { gdi.alert("Laguppställningen hade fel, som har rättats"); @@ -662,7 +662,7 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data) selectTeam(gdi, t); } else if (bi.id == "Browse") { - const char *target = (const char *)bi.getExtra(); + const wchar_t *target = bi.getExtra(); vector< pair > ext; ext.push_back(make_pair(L"Laguppställning", L"*.csv;*.txt")); wstring fileName = gdi.browseForOpen(ext, L"csv"); diff --git a/code/Table.cpp b/code/Table.cpp index b6234f7..5fef724 100644 --- a/code/Table.cpp +++ b/code/Table.cpp @@ -33,6 +33,7 @@ #include "gdiconstants.h" #include "meosexception.h" #include "recorder.h" +#include "oDataContainer.h" extern HINSTANCE hInst; const char *tId="_TABLE_SEL"; @@ -103,9 +104,9 @@ void Table::clearCellSelection(gdioutput *gdi) { upperCol = -1; lowerCol = -1; if (gdi) { - HDC hDC = GetDC(gdi->getTarget()); + HDC hDC = GetDC(gdi->getHWNDTarget()); clearSelectionBitmap(gdi, hDC); - ReleaseDC(gdi->getTarget(), hDC); + ReleaseDC(gdi->getHWNDTarget(), hDC); } } @@ -567,7 +568,7 @@ bool Table::mouseMove(gdioutput &gdi, int x, int y) if (row!=-1) col=getColumn(x); - HWND hWnd=gdi.getTarget(); + HWND hWnd=gdi.getHWNDTarget(); if (colSelected!=-1) { TableCell &cell=Data[0].cells[colSelected]; @@ -673,7 +674,7 @@ bool Table::mouseLeftUp(gdioutput &gdi, int x, int y) if (hdcCompatible) { TableCell &cell=Data[0].cells[colSelected]; - HWND hWnd=gdi.getTarget(); + HWND hWnd=gdi.getHWNDTarget(); HDC hDC=GetDC(hWnd); stopMoveCell(hDC, cell, x-startX, y-startY); ReleaseDC(hWnd, hDC); @@ -691,7 +692,7 @@ bool Table::mouseLeftUp(gdioutput &gdi, int x, int y) } else { moveColumn(colSelected, highCol); - InvalidateRect(gdi.getTarget(), 0, false); + InvalidateRect(gdi.getHWNDTarget(), 0, false); colSelected=-1; return true; } @@ -726,7 +727,7 @@ void Table::selection(gdioutput &gdi, const wstring &text, int data) { reloadRow(id); RECT rc; getRowRect(selectionRow, rc); - InvalidateRect(gdi.getTarget(), &rc, false); + InvalidateRect(gdi.getHWNDTarget(), &rc, false); } #ifndef MEOSDB @@ -868,7 +869,7 @@ bool Table::mouseLeftDown(gdioutput &gdi, int x, int y) { hEdit=CreateWindowEx(0, L"EDIT", Titles[highCol].filter.c_str(), WS_TABSTOP|WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL|WS_BORDER, - rc.left+105, rc.top, tableWidth-105, (rc.bottom-rc.top-1), gdi.getTarget(), + rc.left+105, rc.top, tableWidth-105, (rc.bottom-rc.top-1), gdi.getHWNDTarget(), 0, hInst, 0); drawFilterLabel=true; SendMessage(hEdit, EM_SETSEL, 0, -1); @@ -877,8 +878,8 @@ bool Table::mouseLeftDown(gdioutput &gdi, int x, int y) { gdi.refresh(); } else { - SetFocus(gdi.getHWND()); - SetCapture(gdi.getTarget()); + SetFocus(gdi.getHWNDTarget()); + SetCapture(gdi.getHWNDTarget()); lowerCol = getColumn(x); lowerRow = getRow(y); startSelect = true; @@ -954,7 +955,7 @@ bool Table::editCell(gdioutput &gdi, int row, int col) { else if (cell.type==cellEdit) { hEdit=CreateWindowEx(0, L"EDIT", cell.contents.c_str(), WS_TABSTOP|WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL|WS_BORDER, - rc.left, rc.top, rc.right-rc.left+1, (rc.bottom-rc.top), gdi.getTarget(), + rc.left, rc.top, rc.right-rc.left+1, (rc.bottom-rc.top), gdi.getHWNDTarget(), 0, hInst, 0); SendMessage(hEdit, EM_SETSEL, 0, -1); SetFocus(hEdit); @@ -1276,7 +1277,7 @@ void Table::draw(gdioutput &gdi, HDC hDC, int dx, int dy, const RECT &screen) RECT desktop, target; GetClientRect(GetDesktopWindow(), &desktop); - GetClientRect(gdi.getTarget(), &target); + GetClientRect(gdi.getHWNDTarget(), &target); int marginPixel = (desktop.bottom - desktop.top) - (target.bottom - target.top); int margin = max(5, (marginPixel + rowHeight) / rowHeight); @@ -1398,7 +1399,7 @@ void Table::restoreSelection(gdioutput &gdi, HDC hDC) { rc.right -= gdi.OffsetX; rc.top -= gdi.OffsetY; rc.bottom -= gdi.OffsetY; - InvalidateRect(gdi.getTarget(), &rc, false); + InvalidateRect(gdi.getHWNDTarget(), &rc, false); partialCell = false; } else if (hdcCompatibleCell) { @@ -1598,7 +1599,7 @@ void Table::setTableText(gdioutput &gdi, int editRow, int editCol, const wstring reloadRow(Data[editRow].id); RECT rc; getRowRect(editRow, rc); - InvalidateRect(gdi.getTarget(), &rc, false); + InvalidateRect(gdi.getHWNDTarget(), &rc, false); } const wstring &Table::getTableText(gdioutput &gdi, int editRow, int editCol) { @@ -1802,6 +1803,8 @@ void Table::resetColumns() void Table::update() { + for (auto &dd : dataDefiners) + dd.second->prepare(oe); int oldSort = PrevSort; Data.clear(); sortIndex.clear(); @@ -1821,7 +1824,6 @@ void Table::update() for (size_t k=0;k dataDefiners; public: + void addDataDefiner(const string &key, const oDataDefiner *definer); void setTableText(gdioutput &gdi, int editRow, int editCol, const wstring &bf); const wstring &getTableText(gdioutput &gdi, int editRow, int editCol); diff --git a/code/animationdata.cpp b/code/animationdata.cpp new file mode 100644 index 0000000..8404129 --- /dev/null +++ b/code/animationdata.cpp @@ -0,0 +1,228 @@ +/************************************************************************ +MeOS - Orienteering Software +Copyright (C) 2009-2017 Melin Software HB + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Melin Software HB - software@melin.nu - www.melin.nu +Eksoppsvägen 16, SE-75646 UPPSALA, Sweden + +************************************************************************/ + +#include "StdAfx.h" +#include "animationdata.h" +#include "meos_util.h" +#include "Printer.h" + +AnimationData::AnimationData(gdioutput &gdi, int timePerPage, int nCol, + int marginPercent, bool animate) : + nCol(nCol), animate(animate), page(-1), gdiRef(0) { + + lastTime = 0; + nextTime = 0; + timeOut = timePerPage; + doAnimation = true; + PageInfo pageInfo; + errorState = false; + + gdi.getTargetDimension(width, height); + + margin = (width * marginPercent) / 100; + double w = (gdi.getWidth() + 20) *nCol + margin; + double s = width / w; + if ((fabs(s - 1.0) > 1e-3)) { + gdi.scaleSize(s, true, false); + } + pageInfo.topMargin = 20; + pageInfo.scaleX = 1.0f; + pageInfo.scaleY = 1.0f; + pageInfo.leftMargin = 20; + pageInfo.bottomMargin = 30; + pageInfo.pageY = float(height-margin); + pageInfo.printHeader = false; + pageInfo.yMM2PrintC = pageInfo.xMM2PrintC = 1; + pageInfo.xMM2PrintK = 0; + pageInfo.yMM2PrintK = 0; + + list rectangles; + pageInfo.renderPages(gdi.getTL(), rectangles, false, pages); +} + +AnimationData::~AnimationData() { + if (animationThread && animationThread->joinable()) { + animationThread->join(); + animationThread.reset(); + } + + if (gdiRef) { + gdiRef->removeHandler(this); + } +} + +bool AnimationData::takeOver(shared_ptr &other) { + delayedTakeOver = other; + return true; +} + +void AnimationData::takeOverInternal(shared_ptr &other) { + pages.swap(other->pages); + width = other->width; + height = other->height; + nCol = other->nCol; + margin = other->margin; + animate = other->animate; +} + +void AnimationData::renderPage(HDC hDC, gdioutput &gdi, DWORD time) { + + bool addTextAnimation = false; + if (doAnimation) { + if (page == -1) + page = 0; + else + page++; + + addTextAnimation = true; + nextTime = time + timeOut; + lastTime = time; + gdiRef = &gdi; + gdi.addTimeoutMilli(timeOut, "AnimationData", 0).setHandler(this); + doAnimation = false; + } + + if (animationThread && animationThread->joinable()) { + if (!addTextAnimation) + return; // Ignore repaint + + if (animationThread) { + animationThread->join(); + animationThread.reset(); + } + } + + if (delayedTakeOver && addTextAnimation) { + takeOverInternal(delayedTakeOver); + delayedTakeOver.reset(); + } + + size_t sp = nCol * page; + if (sp >= pages.size()) { + sp = 0; + page = 0; + } + + int count = 1; + for (size_t i = sp; i < sp + nCol && i < pages.size(); i++) { + int currentRow = 0; + for (auto &text : pages[i].text) { + if (text.ti.yp != currentRow) { + currentRow = text.ti.yp; + count++; + } + } + } + + int atime = 400; + if (count < 30) + atime = 800; + else if (count < 50) + atime = 500; + + int delay = addTextAnimation && animate ? atime / count : 0; + if (delay == 0 || errorState == true) { + doRender(hDC, gdi, sp, delay); + errorState = false; + } + else { + animationThread = make_shared(&AnimationData::threadRender, this, &gdi, sp, delay); + } +} + +void AnimationData::threadRender(gdioutput *gdi, size_t sp, int delay) { + HWND hWnd = gdi->getHWNDTarget(); + HDC hDC = GetDC(hWnd); + int x, y; + gdi->getTargetDimension(x, y); + RECT rc; + rc.left = 0; + rc.right = x; + rc.top = 0; + rc.bottom = y; + gdi->drawBackground(hDC, rc); + try { + doRender(hDC, *gdi, sp, delay); + } + catch (...) { + errorState = true; + } + ReleaseDC(hWnd, hDC); + // End thread and notify that it has ended +} + + void AnimationData::doRender(HDC hDC, gdioutput &gdi, size_t sp, int delay) { + for (size_t i = sp; i < sp + nCol && i < pages.size(); i++) { + renderSubPage(hDC, gdi, pages[i], margin / 2 + ((i - sp) * width) / nCol, 0, delay); + + if (i + 1 < sp + nCol) { + int x = margin / 2 + ((i + 1 - sp) * width) / nCol - 10; + + SelectObject(hDC, GetStockObject(DC_BRUSH)); + HLS fg, bg; + fg.RGBtoHLS(gdi.getFGColor()); + bg.RGBtoHLS(gdi.getBGColor()); + + if (bg.lightness > fg.lightness) + fg.lighten(1.3); + else + fg.lighten(1.0 / 1.3); + + SetDCBrushColor(hDC, fg.HLStoRGB()); + Rectangle(hDC, x - 1, 20, x + 1, height - 20); + } + } +} + +void AnimationData::renderSubPage(HDC hDC, gdioutput &gdi, RenderedPage &page, int x, int y, int animateDelay) { + int ox = gdi.GetOffsetX(); + int oy = gdi.GetOffsetY(); + + int top = 10000; + for (const auto &text : page.text) { + if (!text.ti.isFormatInfo()) { + top = min(top, text.ti.yp); + } + } + gdi.SetOffsetY(-y+top-margin/2); + gdi.SetOffsetX(-x); + + int currentRow = 0; + for (auto &text : page.text) { + if (animateDelay>0 && text.ti.yp != currentRow) { + Sleep(animateDelay); + currentRow = text.ti.yp; + } + gdi.RenderString(text.ti, hDC); + } + gdi.SetOffsetY(ox); + gdi.SetOffsetX(oy); +} + +void AnimationData::handle(gdioutput &gdi, BaseInfo &info, GuiEventType type) { + if (pages.size() > size_t(nCol) || delayedTakeOver) { + doAnimation = true; + gdi.refreshFast(); + } + gdiRef = &gdi; + gdi.addTimeoutMilli(timeOut, "AnimationData", 0).setHandler(this); +} diff --git a/code/animationdata.h b/code/animationdata.h new file mode 100644 index 0000000..cc3bfd7 --- /dev/null +++ b/code/animationdata.h @@ -0,0 +1,64 @@ +#pragma once + +/************************************************************************ +MeOS - Orienteering Software +Copyright (C) 2009-2017 Melin Software HB + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Melin Software HB - software@melin.nu - www.melin.nu +Eksoppsvägen 16, SE-75646 UPPSALA, Sweden + +************************************************************************/ + +#include "gdioutput.h" +#include +#include + +class AnimationData : public GuiHandler { + vector pages; + int width; + int height; + int nCol; + int margin; + bool animate; + + int page; + DWORD lastTime; + DWORD nextTime; + DWORD timeOut; + bool doAnimation; + atomic_bool errorState; + gdioutput *gdiRef; + + void renderSubPage(HDC hDC, gdioutput &gdi, RenderedPage &page, int x, int y, int animationDelay); + + void doRender(HDC hDC, gdioutput &gdi, size_t sp, int delay); + + shared_ptr animationThread; + shared_ptr delayedTakeOver; + void takeOverInternal(shared_ptr &other); + + void threadRender(gdioutput *gdi, size_t sp, int delay); +public: + + AnimationData(gdioutput &gdi, int timePerPage, int nCol, + int marginPercent, bool animate); + ~AnimationData(); + + void handle(gdioutput &gdi, BaseInfo &info, GuiEventType type); + bool takeOver(shared_ptr &other); + + void renderPage(HDC hDC, gdioutput &gdi, DWORD time); +}; diff --git a/code/csvparser.cpp b/code/csvparser.cpp index f5c9238..4522e6c 100644 --- a/code/csvparser.cpp +++ b/code/csvparser.cpp @@ -416,7 +416,7 @@ bool csvparser::importOE_CSV(oEvent &event, const wstring &file) { bool csvparser::openOutput(const wstring &filename) { - //Startnr;Bricka;Databas nr.;Efternamn;Förnamn;År;K;Block;ut;Start;Mål;Tid;Status;Klubb nr.;Namn;Ort;Land;Klass nr.;Kort;Lång;Num1;Num2;Num3;Text1;Text2;Text3;Adr. namn;Gata;Rad 2;Post nr.;Ort;Tel;Fax;E-post;Id/Club;Hyrd;Startavgift;Betalt;Bana nr.;Bana;km;Hm;Bana kontroller + checkWriteAccess(filename); fout.open(filename); if (fout.bad()) diff --git a/code/english.lng b/code/english.lng index da0baaf..ce2e830 100644 --- a/code/english.lng +++ b/code/english.lng @@ -65,7 +65,7 @@ Antal: %d = Number: %d Antal: X = Number: X Antalet rader i urklipp fÃ¥r inte plats i selektionen = The number of rows on the clipboard does not fit the selection Använd Eventor = Use Eventor -Använd banpool = Use course pooli +Använd banpool = Use course pool Använd funktioner för fleretappsklass = Use functions for multi stage class Använd första kontrollen som start = Use first control as start Använd löpardatabasen = Use runner database @@ -350,8 +350,8 @@ Hantera brickor = Manage Cards Hantera flera etapper = Manage Several Stages Hantera jaktstart = Handle Pursuit Hantera klubbar och ekonomi = Manage clubs and economy -Hantera kvar-i-skogen = Handle Remaining Runners -Hantera löparbrickor = Handle Runner Cards +Hantera kvar-i-skogen = Manage Remaining Runners +Hantera löparbrickor = Manage Runner Cards Hemsida = Homepage Hjälp = Help Hoppar över stafettklass: X = Skipping relay class: X @@ -1172,7 +1172,7 @@ Varning: Inget kontonummer angivet (Se tävlingsinställningar) = Warning: No ac Varning: Inget sista betalningsdatum angivet (Se tävlingsinställningar) = Warning: payment due not given (See Competition Settings) Varning: deltagare med blankt namn pÃ¥träffad. MeOS kräver att alla deltagare har ett namn, och tilldelar namnet 'N.N.' = Warning: A competitor without name was found. MeOS requires a name, and has assigned the name 'N.N.' Varning: lag utan namn pÃ¥träffat. MeOS kräver att alla lag har ett namn, och tilldelar namnet 'N.N.' = Warning: A team without name was found. MeOS requires a name and has assigned the name 'N.N.' -Verkställ = Confirm +Verkställ = Apply Version X = Version X Vi stöder MeOS = We support MeOS Viktiga händelser = Important events @@ -1870,7 +1870,7 @@ Byt till rätt klass (behÃ¥ll eventuell starttid) = Switch to the right class (k Byt till vakansplats i rätt klass (om möjligt) = Switch to a vacant position in the right class (if possible) TillÃ¥t ny klass, behÃ¥ll resultat frÃ¥n annan klass = Allow new class and keep results from other class TillÃ¥t ny klass, inget totalresultat = Allow new class but without total result -tooltip_explain_status = - = Unknown Status (No result yet)\nOK = Valid result\nDNS = Did Not Start\nMP = Missing Punch\nDNF = Did Not Finish\nDISQ = Disqualified\nOMT = Over Maximum Time\nNTP = Not Taking Part +tooltip_explain_status = - = Unknown Status (No result yet)\nOK = Valid result\nDNS = Did Not Start\nCancelled = Cancelled entry (shown in start list)\nMP = Missing Punch\nDNF = Did Not Finish\nDISQ = Disqualified\nOMT = Over Maximum Time\nNTP = Not Taking Part Placering = Place Resultat frÃ¥n tidigare etapper = Results from Earlier Stages Input Results = Input Results @@ -2252,3 +2252,35 @@ Spara fönster- och speakerinställningar pÃ¥ datorn = Save windows and settings ask:loadspeaker = Do you wish to re-create previously saved windows on this computer? Ã…terskapa = Re-create Ã…terskapa tidigare sparade fönster- och speakerinställningar = Re-create previously saved windows and settings +Inkludera resultat frÃ¥n tidigare etapper = Include results from all stages +Animation = Animation +Bakgrund = Background +Bakgrundsfärg = Background color +Fullskärm (rullande) = Full screen (rolling) +Fullskärm (sidvis) = Full screen (page by page) +Fönster = Window +Fönster (rullande) = Window (rolling) +Justera visningsinställningar = Adjust view settings +Marginal = Margin +Sidor per skärm = Pages per screen +Textfärg = Text color +Utseende = Appearance +Visningsinställningar för 'X' = View settings for 'X' +Visningstid = Show time +Visning = Display mode +ask:hasVacant = There are still vacancies.\n\nDo you wish to remove all vacancies before exporting results? +warn:missingResult = X competitors are still missing results and are therefore excluded.\n\nYou should manage competitors in forest to assign status to remaining runners. +Ã…terställ till = Reset to +Ã…terbud[status] = Cancelled +LÃ¥s gafflingar = Lock forkings +Markera för att förhindra oavsiktlig ändring av gafflingsnycklar = Check to prevent unintentional altering of forking keys +TillÃ¥t gafflingsändringar = Allow forking modification +ask:updatetimes = Do you wish to keep all currently assigned start times, if possible? Answer no to move the competition in time. +X har en tid (Y) som inte är kompatibel med förändringen = X has a time (Y) that is incompatible with this change +warn:latestarttime = Using start times more than X hours after the zero time is not recommended, since older SI cards only has a 12 hour clock.\n\nDo you wish to proceed anyway? +Anm. tid = Entry time +RunnerEntryDate = Competitor's entry date +RunnerEntryTime = Competitor's entry time +RunnerPaid = Paid amount +RunnerPayMethod = Payment method +EntryTime = Entry Time diff --git a/code/gdifonts.h b/code/gdifonts.h index a5557de..c86ae05 100644 --- a/code/gdifonts.h +++ b/code/gdifonts.h @@ -74,5 +74,6 @@ enum GDICOLOR {colorBlack = RGB(0,0,0), colorMediumRed = RGB(255,200,200), colorMediumDarkRed = RGB(240,120,120), colorWindowBar = -2, - colorDefault = -1}; + colorDefault = -1, + colorTransparent = -3}; diff --git a/code/gdioutput.cpp b/code/gdioutput.cpp index 956f579..6888459 100644 --- a/code/gdioutput.cpp +++ b/code/gdioutput.cpp @@ -55,6 +55,7 @@ #include "gdiimpl.h" #include "Printer.h" #include "recorder.h" +#include "animationdata.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction @@ -133,6 +134,10 @@ void gdioutput::constructor(double _scale) lockUpDown = false; Background = 0; + backgroundColor1 = -1; + backgroundColor2 = -1; + foregroundColor = -1; + backgroundImage = -1; toolbar = 0; initCommon(_scale, L"Arial"); @@ -190,12 +195,12 @@ int transformX(int x, double scale) { return int((x-40) * scale + 0.5) + 40; } -void gdioutput::scaleSize(double scale_) { +void gdioutput::scaleSize(double scale_, bool allowSmallScale, bool doRefresh) { if (fabs(scale_ - 1.0) < 1e-4) return; // No scaling double ns = scale*scale_; - if (ns + 1e-6 < 1.0 ) { + if (!allowSmallScale && ns + 1e-6 < 1.0 ) { ns = 1.0; scale_ = 1.0; } @@ -273,7 +278,16 @@ void gdioutput::scaleSize(double scale_) { r.sOY = int (r.sOY * scale_ + 0.5); } - refresh(); + if (doRefresh) { + refresh(); + } + else { + HDC hDC = GetDC(hWndTarget); + for (auto &ti : TL) { + calcStringSize(ti, hDC); + } + ReleaseDC(hWndTarget, hDC); + } } void gdioutput::initCommon(double _scale, const wstring &font) @@ -317,16 +331,7 @@ const GDIImplFontSet & gdioutput::loadFont(const wstring &font) { relScale = enumeratedFonts[fontIx[k].second].getRelScale(); } } - /*vector res; - split(font, ";", res); - double locScale = 1.0; - if (res.empty() || res.size() > 2) - throw meosException("Cannot load font: " + font); - if (res.size() == 2) { - locScale = atof(res[1].c_str()); - if (!(locScale>0.001 && locScale < 100)) - throw meosException("Cannot scale font with factor: " + res[1]); - }*/ + wstring faceName; double locScale = getLocalScale(font, faceName); @@ -351,9 +356,11 @@ gdioutput::~gdioutput() { while(!timers.empty()) { KillTimer(hWndTarget, (UINT_PTR)&timers.back()); + timers.back().setWnd = 0; timers.back().parent = 0; timers.pop_back(); } + animationData.reset(); deleteFonts(); @@ -397,6 +404,18 @@ void gdioutput::fetchPrinterSettings(PrinterObject &po) const { void gdioutput::drawBackground(HDC hDC, RECT &rc) { + if (backgroundColor1 != -1) { + SelectObject(hDC, GetStockObject(NULL_PEN)); + SelectObject(hDC, GetStockObject(DC_BRUSH)); + SetDCBrushColor(hDC, backgroundColor1); + Rectangle(hDC, -1, -1, rc.right + 1, rc.bottom + 1); + return; + } + else if (!backgroundImage.empty()) { + // TODO + + } + GRADIENT_RECT gr[1]; SelectObject(hDC, GetStockObject(NULL_PEN)); @@ -575,21 +594,17 @@ void gdioutput::draw(HDC hDC, RECT &rc, RECT &drawArea) return; } - list::iterator rit; - SelectObject(hDC,GetStockObject(DC_BRUSH)); - - for(rit=Rectangles.begin();rit!=Rectangles.end(); ++rit){ - if (rit->drawBorder) - SelectObject(hDC, GetStockObject(BLACK_PEN)); - else - SelectObject(hDC, GetStockObject(NULL_PEN)); - SetDCBrushColor(hDC, rit->color); - - RECT rect_rc=rit->rc; - OffsetRect(&rect_rc, -OffsetX, -OffsetY); - Rectangle(hDC, rect_rc.left, rect_rc.top, rect_rc.right, rect_rc.bottom); + if (animationData) { + int page = 0; + animationData->renderPage(hDC, *this, GetTickCount()); + return; } + SelectObject(hDC,GetStockObject(DC_BRUSH)); + + for (auto &rit : Rectangles) + renderRectangle(hDC, 0, rit); + if (useTables) for(list::iterator tit=Tables.begin();tit!=Tables.end(); ++tit){ tit->table->draw(*this, hDC, tit->xp, tit->yp, rc); @@ -641,11 +656,17 @@ void gdioutput::renderRectangle(HDC hDC, RECT *clipRegion, const RectangleInfo & SelectObject(hDC, GetStockObject(BLACK_PEN)); else SelectObject(hDC, GetStockObject(NULL_PEN)); - SetDCBrushColor(hDC, ri.color); - + + if (ri.color == colorTransparent) + SelectObject(hDC, GetStockObject(NULL_BRUSH)); + else { + SetDCBrushColor(hDC, ri.color); + } RECT rect_rc=ri.rc; OffsetRect(&rect_rc, -OffsetX, -OffsetY); Rectangle(hDC, rect_rc.left, rect_rc.top, rect_rc.right, rect_rc.bottom); + if (ri.color == colorTransparent) + SelectObject(hDC, GetStockObject(DC_BRUSH)); } void gdioutput::updateStringPosCache() { @@ -723,6 +744,7 @@ void CALLBACK gdiTimerProc(HWND hWnd, UINT a, UINT_PTR ptr, DWORD b) { wstring msg; KillTimer(hWnd, ptr); TimerInfo *it = (TimerInfo *)ptr; + it->setWnd = 0; try { if (it->parent) { it->parent->timerProc(*it, b); @@ -745,25 +767,49 @@ void CALLBACK gdiTimerProc(HWND hWnd, UINT a, UINT_PTR ptr, DWORD b) { } } +int TimerInfo::globalTimerId = 0; + void gdioutput::timerProc(TimerInfo &timer, DWORD timeout) { + int timerId = timer.timerId; if (timer.handler) timer.handler->handle(*this, timer, GUI_TIMER); else if (timer.callBack) timer.callBack(this, GUI_TIMER, &timer); - for (list::iterator it = timers.begin(); it != timers.end(); ++it) { - if (&*it == &timer) { - timers.erase(it); - return; - } + remove_if(timers.begin(), timers.end(), [timerId](TimerInfo &x) {return x.getId() == timerId; }); +} + +void gdioutput::removeHandler(GuiHandler *h) { + for (auto &it : timers) { + if (it.handler == h) + it.handler = 0; + } + + for (auto &it : BI) { + if (it.handler == h) + it.handler = 0; + } + + + for (auto &it : II) { + if (it.handler == h) + it.handler = 0; + } + + for (auto &it : TL) { + if (it.handler == h) + it.handler = 0; + } + + for (auto &it : LBI) { + if (it.handler == h) + it.handler = 0; } } void gdioutput::removeTimeoutMilli(const string &id) { for (list::iterator it = timers.begin(); it != timers.end(); ++it) { if (it->id == id) { - UINT_PTR ptr = (UINT_PTR)&*it; - KillTimer(hWndTarget, ptr); timers.erase(it); return; } @@ -775,12 +821,17 @@ TimerInfo &gdioutput::addTimeoutMilli(int timeOut, const string &id, GUICALLBACK removeTimeoutMilli(id); timers.push_back(TimerInfo(this, cb)); timers.back().id = id; - //timers.back().data = 0; SetTimer(hWndTarget, (UINT_PTR)&timers.back(), timeOut, gdiTimerProc); + timers.back().setWnd = hWndTarget; return timers.back(); } - +TimerInfo:: ~TimerInfo() { + handler = 0; + callBack = 0; + if (setWnd) + KillTimer(setWnd, (UINT_PTR)this); +} TextInfo &gdioutput::addStringUT(int yp, int xp, int format, const string &text, int xlimit, GUICALLBACK cb, const wchar_t *fontFace) { return addStringUT(yp, xp, format, widen(text), xlimit, cb, fontFace); @@ -951,22 +1002,41 @@ ButtonInfo &gdioutput::addButton(int x, int y, const string &id, const string &t } ButtonInfo &gdioutput::addButton(int x, int y, const string &id, const wstring &text, GUICALLBACK cb, - const wstring &tooltip) + const wstring &tooltip) { - SIZE size; + HANDLE bm = 0; + int width = 0; + if (text[0] == '@') { + HINSTANCE hInst = GetModuleHandle(0);//(HINSTANCE)GetWindowLong(hWndTarget, GWL_HINSTANCE); + int ir = _wtoi(text.c_str() + 1); + // bm = LoadImage(hInst, MAKEINTRESOURCE(ir), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR); + bm = LoadBitmap(hInst, MAKEINTRESOURCE(ir));// , IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR); + + SIZE size; + size.cx = 24; + //GetBitmapDimensionEx(bm, &size); + width = size.cx+4; + } + else { + SIZE size; + HDC hDC = GetDC(hWndTarget); + SelectObject(hDC, getGUIFont()); + wstring ttext = lang.tl(text); + if (lang.capitalizeWords()) + capitalizeWords(ttext); + GetTextExtentPoint32(hDC, ttext.c_str(), ttext.length(), &size); + ReleaseDC(hWndTarget, hDC); + width = size.cx + scaleLength(30); + if (text != L"...") + width = max(width, scaleLength(75)); + } - HDC hDC=GetDC(hWndTarget); - SelectObject(hDC, getGUIFont()); - wstring ttext = lang.tl(text); - if (lang.capitalizeWords()) - capitalizeWords(ttext); - GetTextExtentPoint32(hDC, ttext.c_str(), ttext.length(), &size); - ReleaseDC(hWndTarget, hDC); - int width = size.cx+scaleLength(30); - if (text != L"...") - width = max(width, scaleLength(75)); ButtonInfo &bi=addButton(x, y, width, id, text, cb, tooltip, false, false); + if (bm != 0) { + SendMessage(bi.hWnd, BM_SETIMAGE, IMAGE_BITMAP, LPARAM(bm)); + } + return bi; } @@ -1015,6 +1085,10 @@ ButtonInfo &gdioutput::addButton(int x, int y, int w, const string &id, bool AbsPos, bool hasState) { int style = hasState ? BS_CHECKBOX|BS_PUSHLIKE : BS_PUSHBUTTON; + + if (text[0] == '@') + style |= BS_BITMAP; + ButtonInfo bi; wstring ttext = lang.tl(text); if (lang.capitalizeWords()) @@ -1369,7 +1443,7 @@ void gdioutput::synchronizeListScroll(const string &id1, const string &id2) ListBoxInfo &gdioutput::addListBox(int x, int y, const string &id, int width, int height, GUICALLBACK cb, const wstring &explanation, const wstring &tooltip, bool multiple) { if (explanation.length()>0) { - addString(id+"_expl", y, x, 0, explanation); + addString(id+"_label", y, x, 0, explanation); y+=lineHeight; } ListBoxInfo lbi; @@ -1461,7 +1535,7 @@ ListBoxInfo &gdioutput::addSelection(int x, int y, const string &id, int width, GUICALLBACK cb, const wstring &explanation, const wstring &tooltip) { if (explanation.length()>0) { - addString("", y, x, 0, explanation); + addString(id + "_label", y, x, 0, explanation); y+=lineHeight; } @@ -1504,7 +1578,7 @@ ListBoxInfo &gdioutput::addCombo(const string &id, int width, int height, GUICAL ListBoxInfo &gdioutput::addCombo(int x, int y, const string &id, int width, int height, GUICALLBACK cb, const wstring &explanation, const wstring &tooltip) { if (explanation.length()>0) { - addString("", y, x, 0, explanation); + addString(id + "_label", y, x, 0, explanation); y+=lineHeight; } @@ -1697,18 +1771,19 @@ bool gdioutput::getSelectedItem(ListBoxInfo &lbi) { } int gdioutput::getItemDataByName(const char *id, const char *name) const{ + wstring wname = recodeToWide(name); list::const_iterator it; for(it = LBI.begin(); it != LBI.end(); ++it){ if (it->id==id) { if (it->IsCombo) { - int ix = SendMessage(it->hWnd, CB_FINDSTRING, -1, LPARAM(name)); + int ix = SendMessage(it->hWnd, CB_FINDSTRING, -1, LPARAM(wname.c_str())); if (ix >= 0) { return SendMessage(it->hWnd, CB_GETITEMDATA, ix, 0); } return -1; } else { - int ix = SendMessage(it->hWnd, LB_FINDSTRING, -1, LPARAM(name)); + int ix = SendMessage(it->hWnd, LB_FINDSTRING, -1, LPARAM(wname.c_str())); if (ix >= 0) { return SendMessage(it->hWnd, LB_GETITEMDATA, ix, 0); } @@ -2441,8 +2516,6 @@ LRESULT gdioutput::ProcessMsgWrp(UINT iMessage, LPARAM lParam, WPARAM wParam) return 0;*/ } else if (iMessage == WM_CTLCOLOREDIT) { - //for (list::const_iterator it = II.begin(); it != II.end(); ++it) { - // if (it->hWnd == HWND(lParam)) { unordered_map::iterator it = iiByHwnd.find(HWND(lParam)); if (it != iiByHwnd.end()) { InputInfo &ii = *it->second; @@ -2460,9 +2533,11 @@ LRESULT gdioutput::ProcessMsgWrp(UINT iMessage, LPARAM lParam, WPARAM wParam) return LRESULT(GetStockObject(DC_BRUSH)); } } - return 0; } + else if (iMessage == WM_DESTROY) { + canClear();// Ignore return value + } return 0; } @@ -2747,6 +2822,7 @@ void gdioutput::doEscape() } void gdioutput::clearPage(bool autoRefresh, bool keepToolbar) { + animationData.reset(); lockUpDown = false; hasAnyTimer = false; enableTables(); @@ -2757,6 +2833,7 @@ void gdioutput::clearPage(bool autoRefresh, bool keepToolbar) { while(!timers.empty()) { KillTimer(hWndTarget, (UINT_PTR)&timers.back()); + timers.back().setWnd = 0; timers.back().parent = 0; timers.pop_back(); } @@ -2845,6 +2922,12 @@ void gdioutput::clearPage(bool autoRefresh, bool keepToolbar) { renderOptimize=true; + backgroundColor1 = -1; + backgroundColor2 = -1; + foregroundColor = -1; + backgroundImage = -1; + + setRestorePoint(); if (autoRefresh) @@ -3152,6 +3235,32 @@ bool gdioutput::getData(const string &id, DWORD &data) const return false; } +void gdioutput::setData(const string &id, const string &data) { + for (auto &it : DataInfo) { + if (it.id == id) { + it.sdata = data; + return; + } + } + + DataStore ds; + ds.id = id; + ds.sdata = data; + DataInfo.push_front(ds); + return; +} + +bool gdioutput::getData(const string &id, string &out) const { + for (auto &it : DataInfo) { + if (it.id == id) { + out = it.sdata; + return true; + } + } + out.clear(); + return false; +} + void *gdioutput::getData(const string &id) const { list::const_iterator it; for (it = DataInfo.begin(); it != DataInfo.end(); ++it){ @@ -4095,6 +4204,8 @@ void gdioutput::formatString(const TextInfo &ti, HDC hDC) const SetTextColor(hDC, RGB(255,0,0)); else if (ti.highlight) SetTextColor(hDC, RGB(64,64,128)); + else if (ti.color == 0 && foregroundColor != -1) + SetTextColor(hDC, foregroundColor); else SetTextColor(hDC, ti.color); } @@ -4488,6 +4599,9 @@ bool gdioutput::removeControl(const string &id) if (it->id==id) { DestroyWindow(it->hWnd); biByHwnd.erase(it->hWnd); + + if (it->isCheckbox) + removeString("T" + id); BI.erase(it); return true; } @@ -4500,6 +4614,7 @@ bool gdioutput::removeControl(const string &id) if (lit->id==id) { DestroyWindow(lit->hWnd); lbiByHwnd.erase(lit->hWnd); + removeString(id + "_label"); if (lit->writeLock) hasCleared = true; LBI.erase(lit); @@ -4515,6 +4630,7 @@ bool gdioutput::removeControl(const string &id) DestroyWindow(iit->hWnd); iiByHwnd.erase(iit->hWnd); II.erase(iit); + removeString(id + "_label"); return true; } ++iit; @@ -4843,7 +4959,11 @@ RectangleInfo &RectangleInfo::changeDimension(gdioutput &gdi, int dx, int dy) { RectangleInfo &gdioutput::addRectangle(RECT &rc, GDICOLOR color, bool drawBorder, bool addFirst) { RectangleInfo ri; - ri.rc = rc; + ri.rc.left = min(rc.left, rc.right); + ri.rc.right = max(rc.left, rc.right); + ri.rc.top = min(rc.top, rc.bottom); + ri.rc.bottom = max(rc.top, rc.bottom); + if (color==colorDefault) ri.color = GetSysColor(COLOR_INFOBK); else if (color == colorWindowBar) { @@ -4861,7 +4981,7 @@ RectangleInfo &gdioutput::addRectangle(RECT &rc, GDICOLOR color, bool drawBorder } int ex = scaleLength(5); - updatePos(rc.left, rc.top, rc.right-rc.left+ex, rc.bottom-rc.top+ex); + updatePos(ri.rc.left, ri.rc.top, ri.rc.right-ri.rc.left+ex, ri.rc.bottom-ri.rc.top+ex); if (addFirst) { Rectangles.push_front(ri); return Rectangles.front(); @@ -5022,6 +5142,12 @@ void gdioutput::scrollToBottom() bool gdioutput::clipOffset(int PageX, int PageY, int &MaxOffsetX, int &MaxOffsetY) { + if (animationData) { + MaxOffsetX = 0; + MaxOffsetY = 0; + return false; + } + if (highContrast) setHighContrastMaxWidth(); @@ -5732,10 +5858,38 @@ void gdioutput::storeAutoPos(double pos) { } void gdioutput::setFullScreen(bool useFullScreen) { - SetWindowLong(hWndTarget, GWL_STYLE, WS_POPUP|WS_BORDER); - ShowWindow(hWndTarget, SW_MAXIMIZE); - UpdateWindow(hWndTarget); - fullScreen = true; + if (useFullScreen && !fullScreen) { + SetWindowLong(hWndTarget, GWL_STYLE, WS_POPUP | WS_BORDER); + ShowWindow(hWndTarget, SW_MAXIMIZE); + UpdateWindow(hWndTarget); + } + else if (fullScreen) { + SetWindowLong(hWndTarget, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); + ShowWindow(hWndTarget, SW_NORMAL); + UpdateWindow(hWndTarget); + } + fullScreen = useFullScreen; +} + +void gdioutput::setColorMode(DWORD bgColor1, DWORD bgColor2, + DWORD fgColor, const wstring &bgImage) { + backgroundColor1 = bgColor1; + backgroundColor2 = bgColor2; + foregroundColor = fgColor; + backgroundImage = bgImage; +} + +DWORD gdioutput::getFGColor() const { + return foregroundColor != -1 ? foregroundColor : 0; +} +DWORD gdioutput::getBGColor() const { + return backgroundColor1 != -1 ? backgroundColor1 : RGB(255,255,255); +} +DWORD gdioutput::getBGColor2() const { + return backgroundColor2; +} +const wstring &gdioutput::getBGImage() const { + return backgroundImage; } bool gdioutput::hasCommandLock() const { @@ -6249,7 +6403,7 @@ InputInfo &InputInfo::setFont(gdioutput &gdi, gdiFonts font) { void gdioutput::copyToClipboard(const string &html, const wstring &txt) const { - if (OpenClipboard(getHWND()) != false) { + if (OpenClipboard(getHWNDMain()) != false) { EmptyClipboard(); size_t len = html.length() + 1; @@ -6658,4 +6812,43 @@ void gdioutput::getVirtualScreenSize(RECT &rc) { rc.right = px; rc.top = 0; rc.bottom = py; -} \ No newline at end of file +} + +DWORD gdioutput::selectColor(wstring &def, DWORD input) { + CHOOSECOLOR cc; + memset(&cc, 0, sizeof(cc)); + cc.lStructSize = sizeof(cc); + cc.hwndOwner = getHWNDMain(); + cc.rgbResult = COLORREF(input); + if (GDICOLOR(input) != colorDefault) + cc.Flags |= CC_RGBINIT; + + COLORREF staticColor[16]; + memset(staticColor, 0, 16 * sizeof(COLORREF)); + + const wchar_t *end = def.c_str() + def.length(); + const wchar_t * pEnd = def.c_str(); + int pix = 0; + while (pEnd < end && pix < 16) { + staticColor[pix++] = wcstol(pEnd, (wchar_t **)&pEnd, 16); + } + + cc.lpCustColors = staticColor; + if (ChooseColor(&cc)) { + wstring co; + for (int ix = 0; ix < 16; ix++) { + wchar_t bf[16]; + swprintf_s(bf, L"%x ", staticColor[ix]); + co += bf; + } + swap(def,co); + return cc.rgbResult; + } + return -1; +} + +void gdioutput::setAnimationMode(shared_ptr &data) { + if (animationData && animationData->takeOver(data)) + return; + animationData = data; +} diff --git a/code/gdioutput.h b/code/gdioutput.h index 4775009..3f7b9d1 100644 --- a/code/gdioutput.h +++ b/code/gdioutput.h @@ -63,6 +63,7 @@ class FixedTabs; struct PageInfo; struct RenderedPage; +class AnimationData; typedef int (*GUICALLBACK)(gdioutput *gdi, int type, void *data); @@ -70,7 +71,7 @@ enum GDICOLOR; enum KeyCommandCode; enum gdiFonts; #include "gdistructures.h" - +#include #define START_YP 30 #define NOTIMEOUT 0x0AAAAAAA @@ -238,6 +239,12 @@ protected: bool lockRefresh; bool fullScreen; bool hideBG; + + DWORD backgroundColor1; + DWORD backgroundColor2; + wstring backgroundImage; + DWORD foregroundColor; + mutable bool commandLock; mutable DWORD commandUnlockTime; @@ -271,6 +278,8 @@ protected: pair recorder; list< pair > subCommands; + shared_ptr animationData; + int defaultCodePage; public: // Return the bounding dimension of the desktop @@ -279,6 +288,7 @@ public: void getWindowsPosition(RECT &rc) const; void setWindowsPosition(const RECT &rc); + void initRecorder(Recorder *rec); Recorder &getRecorder(); string dbPress(const string &id, int extra); @@ -332,6 +342,16 @@ public: bool isFullScreen() const {return fullScreen;} void setFullScreen(bool useFullScreen); + void setColorMode(DWORD bgColor1, DWORD bgColor2 = -1, + DWORD fgColor = -1, const wstring &bgImage = L""); + + DWORD getFGColor() const; + DWORD getBGColor() const; + DWORD getBGColor2() const; + const wstring &getBGImage() const; + + void setAnimationMode(shared_ptr &mode); + void setAutoScroll(double speed); void getAutoScroll(double &speed, double &pos) const; void storeAutoPos(double pos); @@ -394,8 +414,8 @@ public: void getSelection(const string &id, set &selection); - HWND getTarget() const {return hWndTarget;} - HWND getMain() const {return hWndAppMain;} + HWND getHWNDTarget() const {return hWndTarget;} + HWND getHWNDMain() const {return hWndAppMain;} void scrollToBottom(); void scrollTo(int x, int y); @@ -456,7 +476,6 @@ public: void drawBoxes(HDC hDC, RECT &rc); void drawBox(HDC hDC, InfoBox &Box, RECT &pos); void addInfoBox(string id, wstring text, int TimeOut=0, GUICALLBACK cb=0); - HWND getHWND() const {return hWndTarget;} void updateObjectPositions(); void drawBackground(HDC hDC, RECT &rc); void renderRectangle(HDC hDC, RECT *clipRegion, const RectangleInfo &ri); @@ -516,7 +535,13 @@ public: void setTabStops(const string &Name, int t1, int t2=-1); void setData(const string &id, DWORD data); void setData(const string &id, void *data); + void setData(const string &id, const string &data); + void *getData(const string &id) const; + bool getData(const string &id, string &out) const; + + + DWORD selectColor(wstring &def, DWORD input); void autoRefresh(bool flag) {manualUpdate = !flag;} @@ -618,7 +643,7 @@ public: LRESULT ProcessMsg(UINT iMessage, LPARAM lParam, WPARAM wParam); void setWindow(HWND hWnd){hWndTarget=hWnd;} - void scaleSize(double scale); + void scaleSize(double scale, bool allowSmallScale = false, bool doRefresh = true); ButtonInfo &addButton(const string &id, const wstring &text, GUICALLBACK cb = 0, const wstring &tooltip = L""); @@ -713,6 +738,8 @@ public: TimerInfo &addTimeoutMilli(int timeOut, const string &id, GUICALLBACK cb); void timerProc(TimerInfo &timer, DWORD timeout); + void removeHandler(GuiHandler *h); + void draw(HDC hDC, RECT &windowArea, RECT &drawArea); void closeWindow(); diff --git a/code/gdistructures.h b/code/gdistructures.h index fd182cf..18aa5b7 100644 --- a/code/gdistructures.h +++ b/code/gdistructures.h @@ -25,6 +25,7 @@ #include #include "guihandler.h" +#include "gdifonts.h" class BaseInfo { @@ -151,6 +152,8 @@ public: TextInfo &setColor(GDICOLOR c) {color = c; return *this;} TextInfo &changeFont(const wstring &fnt) {font = fnt; return *this;} //Note: size not updated + bool isFormatInfo() const { return format == pageNewPage || format == pagePageInfo; } + int getHeight() {return int(textRect.bottom-textRect.top);} gdiFonts getGdiFont() const {return gdiFonts(format & 0xFF);} // Sets absolute print coordinates in [mm] @@ -183,6 +186,8 @@ public: HWND getControlWindow() const {throw std::exception("Unsupported");} + + friend class gdioutput; }; class ButtonInfo : public BaseInfo @@ -330,8 +335,12 @@ private: class DataStore { public: + DataStore() { + data = 0; + } string id; void *data; + string sdata; }; class EventInfo : public BaseInfo @@ -355,12 +364,17 @@ public: class TimerInfo : public BaseInfo { private: + static int globalTimerId; + int timerId; DWORD dataInt; wstring dataString; gdioutput *parent; - TimerInfo(gdioutput *gdi, GUICALLBACK cb) : parent(gdi), callBack(cb) {} - + TimerInfo(gdioutput *gdi, GUICALLBACK cb) : parent(gdi), callBack(cb), setWnd(0), timerId(++globalTimerId) {} + HWND setWnd; public: + ~TimerInfo(); + + int getId() const { return timerId; } BaseInfo &setExtra(const wchar_t *e) {return BaseInfo::setExtra(e);} BaseInfo &setExtra(int e) {return BaseInfo::setExtra(e);} diff --git a/code/generalresult.cpp b/code/generalresult.cpp index d4d3412..2cf22b2 100644 --- a/code/generalresult.cpp +++ b/code/generalresult.cpp @@ -699,6 +699,8 @@ RunnerStatus DynamicResult::toStatus(int status) const { return StatusDNF; case StatusDNS: return StatusDNS; + case StatusCANCEL: + return StatusCANCEL; case StatusNotCompetiting: return StatusNotCompetiting; case StatusDQ: @@ -798,9 +800,9 @@ void DynamicResult::save(xmlparser &xml) const { xml.write("Tag", gdioutput::widen(tag)); xml.write("Description", description); if (origin.empty()) - origin = gEvent->getName() + L" (" + getLocalDateW() + L")"; + origin = gEvent->getName() + L" (" + getLocalDate() + L")"; xml.write("Origin", origin); - xml.write("Date", getLocalTimeW()); + xml.write("Date", getLocalTime()); // xml.write("Tag", tag); // xml.write("UID", getUniqueId()); @@ -975,6 +977,7 @@ void DynamicResult::declareSymbols(DynamicMethods m, bool clear) const { parser.declareSymbol("StatusMP", "Status code for a missing punch", false); parser.declareSymbol("StatusDNF", "Status code for not finishing", false); parser.declareSymbol("StatusDNS", "Status code for not starting", false); + parser.declareSymbol("StatusCANCEL", "Status code for cancelled entry", false); parser.declareSymbol("StatusMAX", "Status code for a time over the maximum", false); parser.declareSymbol("StatusDQ", "Status code for disqualification", false); parser.declareSymbol("StatusNotCompetiting", "Status code for not competing", false); @@ -1016,6 +1019,7 @@ void DynamicResult::prepareCalculations(oEvent &oe, bool prepareForTeam, int inp parser.addSymbol("StatusOK", StatusOK); parser.addSymbol("StatusMP", StatusMP); parser.addSymbol("StatusDNF", StatusDNF); + parser.addSymbol("StatusCANCEL", StatusCANCEL); parser.addSymbol("StatusDNS", StatusDNS); parser.addSymbol("StatusMAX", StatusMAX); parser.addSymbol("StatusDQ", StatusDQ); diff --git a/code/infoserver.cpp b/code/infoserver.cpp index a6f604a..a449dd1 100644 --- a/code/infoserver.cpp +++ b/code/infoserver.cpp @@ -65,6 +65,7 @@ int InfoBase::convertRelativeTime(const oBase &elem, int t) { InfoCompetition::InfoCompetition(int id) : InfoBase(id) { forceComplete = true; + includeTotal = false; } InfoRadioControl::InfoRadioControl(int id) : InfoBase(id) { @@ -93,7 +94,7 @@ InfoTeam::InfoTeam(int id) : InfoBaseCompetitor(id) { } -bool InfoCompetition::synchronize(oEvent &oe, const set &includeCls, const set &ctrls) { +bool InfoCompetition::synchronize(oEvent &oe, bool onlyCmp, const set &includeCls, const set &ctrls) { bool changed = false; if (oe.getName() != name) { name = oe.getName(); @@ -117,6 +118,9 @@ bool InfoCompetition::synchronize(oEvent &oe, const set &includeCls, const if (changed) needCommit(*this); + + if (onlyCmp) + return changed; vector ctrl; oe.getControls(ctrl, true); @@ -422,23 +426,41 @@ bool InfoBaseCompetitor::synchronizeBase(oAbstractRunner &bc) { return ch; } -bool InfoCompetitor::synchronize(const InfoCompetition &cmp, oRunner &r) { +bool InfoCompetitor::synchronize(bool useTotalResults, oRunner &r) { bool ch = synchronizeBase(r); - changeTotalSt = r.getEvent()->hasPrevStage() || r.getLegNumber()>0; // Always write full attributes - int s = r.getTotalStatusInput(); + + int s = StatusOK; + int legInput = 0; + + pTeam t = r.getTeam(); + if (useTotalResults) { + legInput = r.getTotalTimeInput() * 10; + s = r.getTotalStatusInput(); + } + else if (t && r.getLegNumber() > 0) { + legInput = t->getLegRunningTime(r.getLegNumber() - 1, false); + s = t->getLegStatus(r.getLegNumber() - 1, false); + } + if (totalStatus != s) { totalStatus = s; ch = true; changeTotalSt = true; } - int legInput = r.getTotalTimeInput() * 10; if (legInput != inputTime) { inputTime = legInput; ch = true; changeTotalSt = true; } + + return ch; +} + +bool InfoCompetitor::synchronize(const InfoCompetition &cmp, oRunner &r) { + bool useTotalResults = cmp.includeTotalResults(); + bool ch = synchronize(useTotalResults, r); vector newRT; if (r.getClassId() > 0) { diff --git a/code/infoserver.h b/code/infoserver.h index 3b1f290..6084e63 100644 --- a/code/infoserver.h +++ b/code/infoserver.h @@ -114,9 +114,10 @@ class InfoClass : public InfoBase { int sortOrder; vector< vector > radioControls; vector linearLegNumberToActual; + public: bool synchronize(oClass &c, const set &ctrls); void serialize(xmlbuffer &xml, bool diffOnly) const; - public: + InfoClass(int id); virtual ~InfoClass() {} @@ -166,10 +167,12 @@ class InfoCompetitor : public InfoBaseCompetitor { int inputTime; int totalStatus; bool synchronize(const InfoCompetition &cmp, oRunner &c); - void serialize(xmlbuffer &xml, bool diffOnly) const; bool changeTotalSt; bool changeRadio; public: + bool synchronize(bool useTotalResults, oRunner &c); + void serialize(xmlbuffer &xml, bool diffOnly) const; + InfoCompetitor(int id); virtual ~InfoCompetitor() {} @@ -197,6 +200,8 @@ private: protected: bool forceComplete; + bool includeTotal; + list toCommit; map controls; @@ -206,13 +211,20 @@ protected: map teams; void needCommit(InfoBase &obj); + + public: void serialize(xmlbuffer &xml, bool diffOnly) const; - public: - const vector &getControls(int classId, int legNumber) const; - bool synchronize(oEvent &oe, const set &classes, const set &ctrls); + bool includeTotalResults() const {return includeTotal;} + void includeTotalResults(bool inc) {includeTotal = inc;} + const vector &getControls(int classId, int legNumber) const; + bool synchronize(oEvent &oe, bool onlyCmp, const set &classes, const set &ctrls); + bool synchronize(oEvent &oe) { + set dmy; + return synchronize(oe, true, dmy, dmy); + } void getCompleteXML(xmlbuffer &xml); void getDiffXML(xmlbuffer &xml); diff --git a/code/iof30interface.cpp b/code/iof30interface.cpp index 10f2718..6441b28 100644 --- a/code/iof30interface.cpp +++ b/code/iof30interface.cpp @@ -1150,7 +1150,7 @@ void IOF30Interface::readEvent(gdioutput &gdi, const xmlobject &xo, wstring dateStr; date.getObjectString("Date", dateStr); oe.setDate(dateStr); - string timeStr; + wstring timeStr; date.getObjectString("Time", timeStr); if (!timeStr.empty()) { int t = convertAbsoluteTimeISO(timeStr); @@ -1158,7 +1158,7 @@ void IOF30Interface::readEvent(gdioutput &gdi, const xmlobject &xo, int zt = t - 3600; if (zt < 0) zt += 3600*24; - oe.setZeroTime(formatTimeHMSW(zt)); + oe.setZeroTime(formatTimeHMS(zt)); } } //oe.setZeroTime(...); @@ -1347,6 +1347,14 @@ pTeam IOF30Interface::readTeamEntry(gdioutput &gdi, xmlobject &xTeam, wstring entryTime; xTeam.getObjectString("EntryTime", entryTime); di.setDate("EntryDate", entryTime); + + size_t tpos = entryTime.find_first_of(L"T"); + if (tpos != -1) { + wstring timeString = entryTime.substr(tpos+1); + int t = convertAbsoluteTimeISO(timeString); + if (t >= 0) + di.setInt("EntryTime", t); + } } double fee = 0, paid = 0, taxable = 0, percentage = 0; @@ -1485,7 +1493,7 @@ void IOF30Interface::prescanEntry(xmlobject &xo, set &stages) { if (races.empty()) stages.insert(-1);// All else { - for (auto race : races) { + for (auto &race : races) { int r = race.getInt(); if (r > 0) stages.insert(r); @@ -1497,7 +1505,7 @@ bool IOF30Interface::matchStageFilter(const set &stageFilter, const xmlList if (stageFilter.empty() || races.empty()) return true; - for (auto r : races) { + for (auto &r : races) { if (stageFilter.count(r.getInt())) return true; } @@ -1580,6 +1588,14 @@ pRunner IOF30Interface::readPersonEntry(gdioutput &gdi, xmlobject &xo, pTeam tea wstring entryTime; xo.getObjectString("EntryTime", entryTime); di.setDate("EntryDate", entryTime); + size_t tpos = entryTime.find_first_of(L"T"); + if (tpos != -1) { + wstring timeString = entryTime.substr(tpos+1); + int t = convertAbsoluteTimeISO(timeString); + if (t >= 0) + di.setInt("EntryTime", t); + } + double fee = 0, paid = 0, taxable = 0, percentage = 0; wstring currency; @@ -2064,7 +2080,7 @@ void IOF30Interface::FeeInfo::add(IOF30Interface::FeeInfo &fi) { convertDateYMS(fi.toTime, st, false); __int64 sec = SystemTimeToInt64Second(st); sec -= 3600; - fi.toTime = convertSystemDateW(Int64SecondToSystemTime(sec)); + fi.toTime = convertSystemDate(Int64SecondToSystemTime(sec)); } } //if (fi.fromTime.empty() || (fi.fromTime < toTime && !toTime.empty())) @@ -2270,7 +2286,7 @@ void IOF30Interface::setupRelayClass(pClass pc, const vector &legs) { wstring IOF30Interface::getCurrentTime() const { // Don't call this method at midnight! - return getLocalDateW() + L"T" + getLocalTimeOnlyW(); + return getLocalDate() + L"T" + getLocalTimeOnly(); } int IOF30Interface::parseISO8601Time(const xmlobject &xo) { @@ -2488,6 +2504,8 @@ wstring formatStatus(RunnerStatus st) { return L"OK"; case StatusDNS: return L"DidNotStart"; + case StatusCANCEL: + return L"Cancelled"; case StatusMP: return L"MissingPunch"; case StatusDNF: @@ -2647,7 +2665,9 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o writeCourse(xml, *crs); const vector &sp = r.getSplitTimes(unrollLoops); - if (r.getStatus()>0 && r.getStatus() != StatusDNS && r.getStatus() != StatusNotCompetiting) { + if (r.getStatus()>0 && r.getStatus() != StatusDNS && + r.getStatus() != StatusCANCEL && + r.getStatus() != StatusNotCompetiting) { int nc = crs->getNumControls(); bool hasRogaining = crs->hasRogaining(); int firstControl = crs->useFirstAsStart() ? 1 : 0; diff --git a/code/listeditor.cpp b/code/listeditor.cpp index 80d21e4..90390ac 100644 --- a/code/listeditor.cpp +++ b/code/listeditor.cpp @@ -298,7 +298,13 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) { ButtonInfo &biSrc = dynamic_cast(data); if (bi.id == "Color") { - CHOOSECOLOR cc; + wstring c = oe->getPropertyString("Colors", L""); + int res = gdi.selectColor(c, bi.getExtraInt()); + if (res >= -1) { + biSrc.setExtra(res); + oe->setProperty("Colors", c); + } + /*CHOOSECOLOR cc; memset(&cc, 0, sizeof(cc)); cc.lStructSize = sizeof(cc); cc.hwndOwner = gdi.getHWND(); @@ -317,8 +323,6 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) { staticColor[pix++] = strtol(pEnd,(char **)&pEnd,16); } - //vector splitvector; - //split(c, ";", splitvector); cc.lpCustColors = staticColor; if (ChooseColor(&cc)) { data.setExtra((int)cc.rgbResult); @@ -330,7 +334,7 @@ int ListEditor::editList(gdioutput &gdi, int type, BaseInfo &data) { co += bf; } oe->setProperty("Colors", co); - } + }*/ } if ( bi.id.substr(0, 8) == "EditPost" ) { int id = atoi(bi.id.substr(8).c_str()); diff --git a/code/liveresult.cpp b/code/liveresult.cpp index 64d29a5..9753067 100644 --- a/code/liveresult.cpp +++ b/code/liveresult.cpp @@ -313,12 +313,12 @@ void LiveResult::handle(gdioutput &gdi, BaseInfo &bu, GuiEventType type) { if (screenSize == 1) { gdi.restore("LiveResult", false); wstring font = getFont(gdi, timerScale); - gdi.addString("", h/2, w/2, boldHuge|textCenter, formatTimeW(rt), 0, 0, font.c_str()).setColor(colorGreen); + gdi.addString("", h/2, w/2, boldHuge|textCenter, formatTime(rt), 0, 0, font.c_str()).setColor(colorGreen); gdi.addTimeout(5, 0).setHandler(this); } else if (screenSize == 2) { string id = "timer" + itos(runner2ScreenPos[rToFinish->getId()]); - BaseInfo *bi = gdi.setText(id, formatTimeW(rt), false); + BaseInfo *bi = gdi.setText(id, formatTime(rt), false); wstring font = getFont(gdi, timerScale * 0.6); if (bi) { @@ -388,7 +388,7 @@ void LiveResult::handle(gdioutput &gdi, BaseInfo &bu, GuiEventType type) { int ht = ti.textRect.bottom - ti.textRect.top; gdi.addStringUT(y, 30 + ht * 2 , fontLarge, r->getName(), 0, 0, font.c_str()); //int w = gdi.getWidth(); - gdi.addStringUT(y, w - 4 * ht, fontLarge, formatTimeW(res.time), 0, 0, font.c_str()); + gdi.addStringUT(y, w - 4 * ht, fontLarge, formatTime(res.time), 0, 0, font.c_str()); gdi.refreshSmartFromSnapshot(false); resYPos += int (ht * 1.1); showResultList++; diff --git a/code/meos.cpp b/code/meos.cpp index 1e39f04..0a57138 100644 --- a/code/meos.cpp +++ b/code/meos.cpp @@ -63,8 +63,7 @@ #include "autotask.h" #include "meosexception.h" #include "parser.h" - -#include "restbed/restbed" +#include "restserver.h" gdioutput *gdi_main=0; oEvent *gEvent=0; @@ -162,6 +161,8 @@ void mainMessageLoop(HACCEL hAccelTable, DWORD time) { while ( (bRet = GetMessage(&msg, NULL, 0, 0)) != 0 ) { if (bRet == -1) return; + if (gEvent != 0) + RestServer::computeRequested(*gEvent); if (hAccelTable == 0 || !TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); @@ -174,19 +175,6 @@ void mainMessageLoop(HACCEL hAccelTable, DWORD time) { } } -void post_method_handler(const shared_ptr< restbed::Session > session) -{ - using namespace restbed; - const auto request = session->get_request(); - - size_t content_length = request->get_header("Content-Length", 0); - - session->fetch(content_length, [request](const shared_ptr< Session > session, const Bytes & body) - { - fprintf(stdout, "%.*s\n", (int)body.size(), body.data()); - session->close(restbed::OK, "Hello, World!", { { "Content-Length", "13" },{ "Connection", "close" } }); - }); -} int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, @@ -195,20 +183,6 @@ int APIENTRY WinMain(HINSTANCE hInstance, { atexit(dumpLeaks); // _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); - /*{ - using namespace restbed; - auto resource = make_shared< Resource >(); - resource->set_path("/resource"); - resource->set_method_handler("GET", post_method_handler); - - Service service; - - auto settings = make_shared< Settings >(); - settings->set_port(1984); - - service.publish(resource); - service.start(settings); - }*/ if (strstr(lpCmdLine, "-s") != 0) { Setup(true, false); @@ -226,9 +200,10 @@ int APIENTRY WinMain(HINSTANCE hInstance, RunnerStatusOrderMap[StatusMP] = 2; RunnerStatusOrderMap[StatusDNF] = 3; RunnerStatusOrderMap[StatusDQ] = 4; - RunnerStatusOrderMap[StatusDNS] = 5; - RunnerStatusOrderMap[StatusUnknown] = 6; - RunnerStatusOrderMap[StatusNotCompetiting] = 7; + RunnerStatusOrderMap[StatusCANCEL] = 5; + RunnerStatusOrderMap[StatusDNS] = 6; + RunnerStatusOrderMap[StatusUnknown] = 7; + RunnerStatusOrderMap[StatusNotCompetiting] = 8; lang.init(); StringCache::getInstance().init(); @@ -413,7 +388,7 @@ int APIENTRY WinMain(HINSTANCE hInstance, for (size_t k = 0; kgetHWND()); + DestroyWindow(gdi_extra[k]->getHWNDMain()); if (k < gdi_extra.size()) { delete gdi_extra[k]; gdi_extra[k] = 0; @@ -555,7 +530,7 @@ LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam) gdi = gdi_main; - HWND hWnd = gdi ? gdi->getHWND() : 0; + HWND hWnd = gdi ? gdi->getHWNDTarget() : 0; bool ctrlPressed = (GetKeyState(VK_CONTROL) & 0x8000) == 0x8000; bool shiftPressed = (GetKeyState(VK_SHIFT) & 0x8000) == 0x8000; @@ -740,7 +715,7 @@ BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) void destroyExtraWindows() { for (size_t k = 1; kgetHWND()); + DestroyWindow(gdi_extra[k]->getHWNDMain()); } } } @@ -768,7 +743,7 @@ gdioutput *getExtraWindow(const string &tag, bool toForeGround) { for (size_t k = 0; khasTag(tag)) { if (toForeGround) - SetForegroundWindow(gdi_extra[k]->getHWND()); + SetForegroundWindow(gdi_extra[k]->getHWNDMain()); return gdi_extra[k]; } } @@ -791,7 +766,7 @@ gdioutput *createExtraWindow(const string &tag, const wstring &title, int max_x, for (size_t k = 0; kgetHWND(); + HWND hWnd = gdi_extra[k]->getHWNDTarget(); RECT rc; if (GetWindowRect(hWnd, &rc)) { xp = max(rc.left + 16, xp); @@ -829,6 +804,9 @@ gdioutput *createExtraWindow(const string &tag, const wstring &title, int max_x, gdi_main->cmdAnswers.pop_front(); } } + else { + gdi->initRecorder(&gdi_main->getRecorder()); + } SetWindowLong(hWnd, GWL_USERDATA, gdi_extra.size()); currentFocusIx = gdi_extra.size(); gdi_extra.push_back(gdi); diff --git a/code/meos_util.cpp b/code/meos_util.cpp index 746a9ba..d14ecff 100644 --- a/code/meos_util.cpp +++ b/code/meos_util.cpp @@ -35,6 +35,11 @@ namespace MeOSUtil { int useHourFormat = true; } +string convertSystemTimeN(const SYSTEMTIME &st); +string convertSystemDateN(const SYSTEMTIME &st); +string convertSystemTimeOnlyN(const SYSTEMTIME &st); + + DWORD mainThreadId = -1; StringCache &StringCache::getInstance() { DWORD id = GetCurrentThreadId(); @@ -45,29 +50,33 @@ StringCache &StringCache::getInstance() { return globalStringCache; } -string getLocalTime() { +string getLocalTimeN() { + SYSTEMTIME st; + GetLocalTime(&st); + return convertSystemTimeN(st); +} + +string getLocalDateN() +{ + SYSTEMTIME st; + GetLocalTime(&st); + return convertSystemDateN(st); +} + +wstring getLocalTime() { SYSTEMTIME st; GetLocalTime(&st); return convertSystemTime(st); } -string getLocalDate() -{ +wstring getLocalDate() { SYSTEMTIME st; GetLocalTime(&st); return convertSystemDate(st); } -wstring getLocalTimeW() { - SYSTEMTIME st; - GetLocalTime(&st); - return convertSystemTimeW(st); -} - -wstring getLocalDateW() { - SYSTEMTIME st; - GetLocalTime(&st); - return convertSystemDateW(st); +int getLocalAbsTime() { + return convertAbsoluteTimeHMS(getLocalTimeOnly(), -1); } int getThisYear() { @@ -114,20 +123,20 @@ wstring getLocalTimeFileName() return bf; } -string getLocalTimeOnly() +string getLocalTimeOnlyN() +{ + SYSTEMTIME st; + GetLocalTime(&st); + return convertSystemTimeOnlyN(st); +} + +wstring getLocalTimeOnly() { SYSTEMTIME st; GetLocalTime(&st); return convertSystemTimeOnly(st); } -wstring getLocalTimeOnlyW() -{ - SYSTEMTIME st; - GetLocalTime(&st); - return convertSystemTimeOnlyW(st); -} - int getRelativeDay() { SYSTEMTIME st; GetLocalTime(&st); @@ -169,7 +178,7 @@ SYSTEMTIME Int64SecondToSystemTime(__int64 time) { return st; } //2014-11-03 07:02:00 -string convertSystemTime(const SYSTEMTIME &st) +string convertSystemTimeN(const SYSTEMTIME &st) { char bf[32]; sprintf_s(bf, "%d-%02d-%02d %02d:%02d:%02d", st.wYear, st.wMonth, st.wDay, @@ -179,7 +188,7 @@ string convertSystemTime(const SYSTEMTIME &st) } //2014-11-03 07:02:00 -wstring convertSystemTimeW(const SYSTEMTIME &st) +wstring convertSystemTime(const SYSTEMTIME &st) { wchar_t bf[32]; swprintf_s(bf, L"%d-%02d-%02d %02d:%02d:%02d", st.wYear, st.wMonth, st.wDay, @@ -188,7 +197,7 @@ wstring convertSystemTimeW(const SYSTEMTIME &st) return bf; } -string convertSystemTimeOnly(const SYSTEMTIME &st) +string convertSystemTimeOnlyN(const SYSTEMTIME &st) { char bf[32]; sprintf_s(bf, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond); @@ -196,14 +205,14 @@ string convertSystemTimeOnly(const SYSTEMTIME &st) return bf; } -wstring convertSystemTimeOnlyW(const SYSTEMTIME &st) { +wstring convertSystemTimeOnly(const SYSTEMTIME &st) { wchar_t bf[32]; swprintf_s(bf, L"%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond); return bf; } -string convertSystemDate(const SYSTEMTIME &st) +string convertSystemDateN(const SYSTEMTIME &st) { char bf[32]; sprintf_s(bf, "%d-%02d-%02d", st.wYear, st.wMonth, st.wDay); @@ -211,7 +220,7 @@ string convertSystemDate(const SYSTEMTIME &st) return bf; } -wstring convertSystemDateW(const SYSTEMTIME &st) +wstring convertSystemDate(const SYSTEMTIME &st) { wchar_t bf[32]; swprintf_s(bf, L"%d-%02d-%02d", st.wYear, st.wMonth, st.wDay); @@ -219,7 +228,7 @@ wstring convertSystemDateW(const SYSTEMTIME &st) return bf; } -string formatDate(int m, bool useIsoFormat) { +string formatDateN(int m, bool useIsoFormat) { char bf[24]; if (m > 0 && m < 30000101) { sprintf_s(bf, 24, "%d-%02d-%02d", m/(100*100), (m/100)%100, m%100); @@ -231,7 +240,7 @@ string formatDate(int m, bool useIsoFormat) { return bf; } -wstring formatDateW(int m, bool useIsoFormat) { +wstring formatDate(int m, bool useIsoFormat) { wchar_t bf[24]; if (m > 0 && m < 30000101) { swprintf_s(bf, 24, L"%d-%02d-%02d", m/(100*100), (m/100)%100, m%100); @@ -320,11 +329,6 @@ bool myIsSpace(wchar_t b) { return iswspace(b) != 0 || b == 0x00A0 || b == 0x2007 || b == 0x202F; } -//Absolute time string to absolute time int -int convertAbsoluteTimeHMS(const wstring &m, int daysZeroTime) { - string sm(m.begin(), m.end()); - return convertAbsoluteTimeHMS(sm, daysZeroTime); -} //Absolute time string to absolute time int int convertAbsoluteTimeHMS(const string &m, int daysZeroTime) { @@ -408,10 +412,10 @@ int convertAbsoluteTimeHMS(const string &m, int daysZeroTime) { return t; } - -int convertAbsoluteTimeISO(const wstring &m) { - string mn(m.begin(), m.end()); - return convertAbsoluteTimeISO(mn); +//Absolute time string to absolute time int +int convertAbsoluteTimeHMS(const wstring &m, int daysZeroTime) { + string sm(m.begin(), m.end()); + return convertAbsoluteTimeHMS(sm, daysZeroTime); } //Absolute time string to absolute time int @@ -450,6 +454,15 @@ int convertAbsoluteTimeISO(const string &m) sStr = tmp.substr(0, 2); + for (int i = 0; i < 2; i++) { + if (hStr[i] < '0' || hStr[i] > '9') + return -1; + if (mStr[i] < '0' || mStr[i] > '9') + return -1; + if (sStr[i] < '0' || sStr[i] > '9') + return -1; + } + int hour = atoi(hStr.c_str()); if (hour<0 || hour>23) return -1; @@ -469,9 +482,9 @@ int convertAbsoluteTimeISO(const string &m) return t; } -int convertAbsoluteTimeMS(const wstring &m) { +int convertAbsoluteTimeISO(const wstring &m) { string mn(m.begin(), m.end()); - return convertAbsoluteTimeMS(mn); + return convertAbsoluteTimeISO(mn); } // Parse +-MM:SS or +-HH:MM:SS @@ -524,29 +537,25 @@ int convertAbsoluteTimeMS(const string &m) return sign*t; } -//Generate +-MM:SS or +-HH:MM:SS -const wstring &getTimeMSW(int m) { - wstring &res = StringCache::getInstance().wget(); - const string tr = getTimeMS(m); - res.clear(); - res.insert(res.begin(), tr.begin(), tr.end()); - return res; +int convertAbsoluteTimeMS(const wstring &m) { + string mn(m.begin(), m.end()); + return convertAbsoluteTimeMS(mn); } //Generate +-MM:SS or +-HH:MM:SS -const string &getTimeMS(int m) { - char bf[32]; +const wstring &getTimeMS(int m) { + wchar_t bf[32]; int am = abs(m); if (am < 3600 || !MeOSUtil::useHourFormat) - sprintf_s(bf, "-%02d:%02d", am/60, am%60); + swprintf_s(bf, L"-%02d:%02d", am/60, am%60); else if (am < 3600*48) - sprintf_s(bf, "-%02d:%02d:%02d", am/3600, (am/60)%60, am%60); + swprintf_s(bf, L"-%02d:%02d:%02d", am/3600, (am/60)%60, am%60); else { m = 0; - bf[0] = BYTE(0x96); + bf[0] = 0x2013; bf[1] = 0; } - string &res = StringCache::getInstance().get(); + wstring &res = StringCache::getInstance().wget(); if (m<0) res = bf; // with minus else @@ -555,7 +564,7 @@ const string &getTimeMS(int m) { return res; } -const wstring &formatTimeW(int rt) { +const wstring &formatTime(int rt) { wstring &res = StringCache::getInstance().wget(); if (rt>0 && rt<3600*999) { wchar_t bf[16]; @@ -588,25 +597,15 @@ const string &formatTimeN(int rt) { return res; } -const wstring &formatTimeHMSW(int m) { +const wstring &formatTimeHMS(int rt) { wstring &res = StringCache::getInstance().wget(); - const string tr = formatTimeHMS(m); - res.clear(); - res.insert(res.begin(), tr.begin(), tr.end()); - return res; -} - -const string &formatTimeHMS(int rt) { - - string &res = StringCache::getInstance().get(); if (rt>=0) { - char bf[32]; - sprintf_s(bf, 16, "%02d:%02d:%02d", rt/3600,(rt/60)%60, rt%60); - + wchar_t bf[32]; + swprintf_s(bf, 16, L"%02d:%02d:%02d", rt/3600,(rt/60)%60, rt%60); res = bf; return res; } - char ret[2] = {char(0x96), 0}; + wchar_t ret[2] = {0x2013, 0}; res = ret; return res; } @@ -2170,3 +2169,17 @@ void wide2String(const wstring &in, string &out) { out.clear(); out.insert(out.begin(), in.begin(), in.end());// XXX Simple extend } + +void checkWriteAccess(const wstring &file) { + if (_waccess(file.c_str(), 4) == 0) + return; + + auto h = CreateFile(file.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); + if (h == INVALID_HANDLE_VALUE) { + wchar_t absPath[260]; + _wfullpath(absPath, file.c_str(), 260); + + throw meosException(wstring(L"Du saknar behörighet att skriva till 'X'.#") + absPath); + } + CloseHandle(h); +} diff --git a/code/meos_util.h b/code/meos_util.h index 7d4eb98..d5a4660 100644 --- a/code/meos_util.h +++ b/code/meos_util.h @@ -52,20 +52,26 @@ public: } }; +string convertSystemTimeN(const SYSTEMTIME &st); + +/* string convertSystemTime(const SYSTEMTIME &st); string convertSystemTimeOnly(const SYSTEMTIME &st); string convertSystemDate(const SYSTEMTIME &st); string getLocalTime(); string getLocalDate(); string getLocalTimeOnly(); +*/ +string getLocalTimeN(); -wstring convertSystemTimeW(const SYSTEMTIME &st); -wstring convertSystemTimeOnlyW(const SYSTEMTIME &st); -wstring convertSystemDateW(const SYSTEMTIME &st); -wstring getLocalTimeW(); -wstring getLocalDateW(); -wstring getLocalTimeOnlyW(); - +wstring convertSystemTime(const SYSTEMTIME &st); +wstring convertSystemTimeOnly(const SYSTEMTIME &st); +wstring convertSystemDate(const SYSTEMTIME &st); +wstring getLocalTime(); +wstring getLocalDate(); +wstring getLocalTimeOnly(); +// Returns time in seconds after midnight +int getLocalAbsTime(); // Get a day number after a fixed day some time ago... int getRelativeDay(); @@ -73,13 +79,11 @@ int getRelativeDay(); /// Get time and date in a format that forms a part of a filename wstring getLocalTimeFileName(); -const wstring &getTimeMSW(int m); -const wstring &formatTimeW(int rt); -const wstring &formatTimeHMSW(int rt); +const wstring &getTimeMS(int m); +const wstring &formatTime(int rt); +const wstring &formatTimeHMS(int rt); -const string &getTimeMS(int m); -const string &formatTimeN(int rt); -const string &formatTimeHMS(int rt); +//const string &formatTimeN(int rt); wstring formatTimeIOF(int rt, int zeroTime); int convertDateYMS(const string &m, bool checkValid); @@ -93,8 +97,8 @@ int convertDateYMS(const wstring &m, SYSTEMTIME &st, bool checkValid); void processGeneralTime(const wstring &generalTime, wstring &meosTime, wstring &meosDate); // Format number date 20160421 -> 2016-04-21 (if iso) or according to a custom format otherwise -string formatDate(int m, bool useIsoFormat); -wstring formatDateW(int m, bool useIsoFormat); +//string formatDate(int m, bool useIsoFormat); +wstring formatDate(int m, bool useIsoFormat); __int64 SystemTimeToInt64Second(const SYSTEMTIME &st); SYSTEMTIME Int64SecondToSystemTime(__int64 time); @@ -107,15 +111,16 @@ int convertAbsoluteTimeMS(const wstring &m); int convertAbsoluteTimeISO(const wstring &m); //Returns a time converted from +/-MM:SS or NOTIME, in seconds -int convertAbsoluteTimeMS(const string &m); +//int convertAbsoluteTimeMS(const string &m); // Parses a time on format HH:MM:SS+01:00Z or HHMMSS+0100Z (but ignores time zone) -int convertAbsoluteTimeISO(const string &m); +//int convertAbsoluteTimeISO(const string &m); /** Returns a time converted from HH:MM:SS or -1, in seconds @param m time to convert @param daysZeroTime -1 do not support days syntax, positive interpret days w.r.t the specified zero time. */ int convertAbsoluteTimeHMS(const string &m, int daysZeroTime); + /** Returns a time converted from HH:MM:SS or -1, in seconds @param m time to convert @param daysZeroTime -1 do not support days syntax, positive interpret days w.r.t the specified zero time. @@ -123,7 +128,7 @@ int convertAbsoluteTimeHMS(const string &m, int daysZeroTime); int convertAbsoluteTimeHMS(const wstring &m, int daysZeroTime); const vector &split(const string &line, const string &separators, vector &split_vector); -const string &unsplit(const vector &split_vector, const string &separators, string &line); +//const string &unsplit(const vector &split_vector, const string &separators, string &line); const vector &split(const wstring &line, const wstring &separators, vector &split_vector); const wstring &unsplit(const vector &split_vector, const wstring &separators, wstring &line); @@ -289,3 +294,5 @@ namespace MeOSUtil { void string2Wide(const string &in, wstring &out); void wide2String(const wstring &in, string &out); + +void checkWriteAccess(const wstring &file); diff --git a/code/meosdb/MeosSQL.cpp b/code/meosdb/MeosSQL.cpp index 3d4377f..dc2c51c 100644 --- a/code/meosdb/MeosSQL.cpp +++ b/code/meosdb/MeosSQL.cpp @@ -840,7 +840,7 @@ OpFailStatus MeosSQL::uploadRunnerDB(oEvent *oe) return opStatusFail; int errorCount = 0; int totErrorCount = 0; - ProgressWindow pw(oe->hWnd()); + ProgressWindow pw(oe->gdiBase().getHWNDTarget()); try { const vector &cdb = oe->runnerDB->getClubDB(true); size_t size = cdb.size(); @@ -1000,7 +1000,7 @@ OpFailStatus MeosSQL::SyncRead(oEvent *oe) { if (!oe->Id) return SyncUpdate(oe); - ProgressWindow pw(oe->hWnd()); + ProgressWindow pw(oe->gdiBase().getHWNDTarget()); try { con.select_db("MeOSMain"); diff --git a/code/meosvc15.vcxproj b/code/meosvc15.vcxproj index 0c5e9ee..0b0fd7f 100644 --- a/code/meosvc15.vcxproj +++ b/code/meosvc15.vcxproj @@ -239,6 +239,7 @@ + @@ -340,6 +341,8 @@ %(PreprocessorDefinitions) + + @@ -386,6 +389,7 @@ + @@ -417,6 +421,8 @@ + + @@ -479,6 +485,9 @@ + + + diff --git a/code/meosversion.cpp b/code/meosversion.cpp index 6d71fef..a91ac30 100644 --- a/code/meosversion.cpp +++ b/code/meosversion.cpp @@ -29,7 +29,7 @@ //V33: abcde //V35: abc int getMeosBuild() { - string revision("$Rev: 611 $"); + string revision("$Rev: 621 $"); return 174 + atoi(revision.substr(5, string::npos).c_str()); } @@ -41,7 +41,7 @@ int getMeosBuild() { //V33: abcdefghij //V34: abcdfg wstring getMeosDate() { - wstring date(L"$Date: 2017-08-28 20:32:01 +0200 (mÃ¥, 28 aug 2017) $"); + wstring date(L"$Date: 2017-10-21 22:43:38 +0200 (lö, 21 okt 2017) $"); return date.substr(7,10); } @@ -176,4 +176,5 @@ void getSupporters(vector &supp) supp.push_back("Timrå SOK"); supp.push_back("Åke Larsson, OK Hedströmmen"); supp.push_back("Avesta OK"); + supp.push_back("Motionsorientering Göteborg"); } diff --git a/code/metalist.cpp b/code/metalist.cpp index 515533a..81c97a5 100644 --- a/code/metalist.cpp +++ b/code/metalist.cpp @@ -43,6 +43,7 @@ const int MAXLISTPARAMID = 10000000; using namespace tr1; oListParam::oListParam() { + lockUpdate = false; listCode = EStdResultList; //Just need a default cb = 0; legNumber = 0; @@ -59,6 +60,15 @@ oListParam::oListParam() { nextList = 0; // No linked list previousList = 0; relayLegIndex = -1; + bgColor = -1; + fgColor = -1; + bgColor2 = -1; + + nColumns = 0; + animate = true; + timePerPage = 8000; + margin = 5; + screenMode = 0; } void oListParam::serialize(xmlparser &xml, @@ -90,6 +100,24 @@ void oListParam::serialize(xmlparser &xml, if (res != idToIndex.end()) xml.write("NextList", res->second); } + if (bgColor != -1) + xml.write("BGColor", itos(bgColor)); + if (bgColor2 != -1) + xml.write("BGColor2", itos(bgColor2)); + + if (fgColor != -1) + xml.write("FGColor", itos(fgColor)); + xml.write("Image", bgImage); + + xml.write("ScreenMode", screenMode); + + if (nColumns != 0) { + xml.write("NumColumns", itos(nColumns)); + xml.writeBool("Animate", animate); + xml.write("TimePerPage", timePerPage); + xml.write("Margin", margin); + } + xml.endTag(); } @@ -120,6 +148,32 @@ void oListParam::deserialize(const xmlobject &xml, const MetaListContainer &cont showInterTitle = xml.getObjectBool("ShowInterTitle"); inputNumber = xml.getObjectInt("InputNumber"); nextList = xml.getObjectInt("NextList"); + + xmlobject bg = xml.getObject("BGColor"); + if (bg) + bgColor = bg.getInt(); + + xmlobject bg2 = xml.getObject("BGColor2"); + if (bg2) + bgColor2 = bg2.getInt(); + + xmlobject fg = xml.getObject("FGColor"); + if (fg) + fgColor = fg.getInt(); + + xml.getObjectString("Image", bgImage); + + int nColumns = xml.getObjectInt("NumColumns"); + + screenMode = xml.getObjectInt("ScreenMode"); + animate = xml.getObjectBool("Animate"); + + if (xml.got("TimePerPage")) + timePerPage = xml.getObjectInt("TimePerPage"); + + if (xml.got("Margin")) + timePerPage = xml.getObjectInt("Margin"); + saved = true; } @@ -961,7 +1015,7 @@ void MetaList::save(xmlparser &xml, const oEvent *oe) const { // xml.write("Title", defaultTitle); xml.write("ListName", listName); if (listOrigin.empty()) - listOrigin = gEvent->getName() + L" (" + getLocalDateW() + L")"; + listOrigin = gEvent->getName() + L" (" + getLocalDate() + L")"; xml.write("ListOrigin", listOrigin); xml.write("Tag", tag); xml.write("UID", getUniqueId()); @@ -1604,6 +1658,10 @@ void MetaList::initSymbols() { typeToSymbol[lRunnerNationality] = L"RunnerNationality"; typeToSymbol[lRunnerPhone] = L"RunnerPhone"; typeToSymbol[lRunnerFee] = L"RunnerFee"; + typeToSymbol[lRunnerPaid] = L"RunnerPaid"; + typeToSymbol[lRunnerPayMethod] = L"RunnerPayMethod"; + typeToSymbol[lRunnerEntryDate] = L"RunnerEntryDate"; + typeToSymbol[lRunnerEntryTime] = L"RunnerEntryTime"; typeToSymbol[lTeamName] = L"TeamName"; typeToSymbol[lTeamStart] = L"TeamStart"; @@ -1719,6 +1777,7 @@ void MetaList::initSymbols() { orderToSymbol[SortByFinishTimeReverse] = "FinishTimeReverse"; orderToSymbol[ClassFinishTime] = "ClassFinishTime"; orderToSymbol[SortByStartTime] = "StartTime"; + orderToSymbol[SortByEntryTime] = "EntryTime"; orderToSymbol[ClassPoints] = "ClassPoints"; orderToSymbol[ClassTotalResult] = "ClassTotalResult"; orderToSymbol[ClassTeamLegResult] = "ClassTeamLegResult"; diff --git a/code/methodeditor.cpp b/code/methodeditor.cpp index 4f1de71..bbcd13f 100644 --- a/code/methodeditor.cpp +++ b/code/methodeditor.cpp @@ -556,7 +556,7 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { wstring str; try { rt = currentResult->deduceTime(*rr[k], rr[k]->getStartTime()); - str = formatTimeW(rt); + str = formatTime(rt); } catch (meosException &ex) { err = ex.wwhat(); @@ -657,7 +657,7 @@ int MethodEditor::methodCb(gdioutput &gdi, int type, BaseInfo &data) { wstring str; try { rt = currentResult->deduceTime(*tr[k]); - str = formatTimeW(rt); + str = formatTime(rt); } catch (meosException &ex) { err = ex.wwhat(); @@ -976,7 +976,7 @@ void MethodEditor::debug(gdioutput &gdi_in, int id, bool isTeam) { try { rt = currentResult->deduceTime(r, r.getStartTime()); - gdi.addStringUT(1, L"ComputedTime: " + formatTimeW(rt)).setColor(colorGreen); + gdi.addStringUT(1, L"ComputedTime: " + formatTime(rt)).setColor(colorGreen); } catch (meosException &ex) { wstring err = lang.tl(ex.wwhat()); @@ -1030,7 +1030,7 @@ void MethodEditor::debug(gdioutput &gdi_in, int id, bool isTeam) { try { rt = currentResult->deduceTime(t); - gdi.addStringUT(1, L"ComputedTime: " + formatTimeW(rt)).setColor(colorGreen); + gdi.addStringUT(1, L"ComputedTime: " + formatTime(rt)).setColor(colorGreen); } catch (meosException &ex) { wstring err = lang.tl(ex.wwhat()); diff --git a/code/mysqldaemon.cpp b/code/mysqldaemon.cpp index 8d4745f..2a9c5e8 100644 --- a/code/mysqldaemon.cpp +++ b/code/mysqldaemon.cpp @@ -31,7 +31,7 @@ MySQLReconnect::MySQLReconnect(const wstring &errorIn) : AutoMachine("MySQL-daemon"), error(errorIn) { - timeError = getLocalTimeW(); + timeError = getLocalTime(); hThread=0; } @@ -110,7 +110,7 @@ void MySQLReconnect::process(gdioutput &gdi, oEvent *oe, AutoSyncType ast) } else { gdi.addInfoBox("", L"Återansluten mot databasen, tävlingen synkroniserad.", 10000); - timeReconnect = getLocalTimeW(); + timeReconnect = getLocalTime(); gdi.setDBErrorState(false); gdi.setWindowTitle(oe->getTitleName()); interval=0; diff --git a/code/newcompetition.cpp b/code/newcompetition.cpp index 73b78bb..801eda6 100644 --- a/code/newcompetition.cpp +++ b/code/newcompetition.cpp @@ -229,7 +229,7 @@ int TabCompetition::newGuideCB(gdioutput &gdi, int type, void *data) long long stopT = absT + 23 * 3600; SYSTEMTIME start = Int64SecondToSystemTime(absT); SYSTEMTIME end = Int64SecondToSystemTime(stopT); - wstring s = L"Tävlingen måste avgöras mellan X och Y.#" + convertSystemTimeW(start) + L"#" + convertSystemTimeW(end); + wstring s = L"Tävlingen måste avgöras mellan X och Y.#" + convertSystemTime(start) + L"#" + convertSystemTime(end); gdi.setTextTranslate("AllowedInterval", s, true); } } @@ -261,7 +261,7 @@ void TabCompetition::newCompetitionGuide(gdioutput &gdi, int step) { gdi.pushX(); gdi.fillRight(); - InputInfo &date = gdi.addInput("Date", getLocalDateW(), 16, NewGuideCB, L"Datum (för första start):"); + InputInfo &date = gdi.addInput("Date", getLocalDate(), 16, NewGuideCB, L"Datum (för första start):"); gdi.addInput("FirstStart", L"07:00:00", 12, NewGuideCB, L"Första tillåtna starttid:"); @@ -301,6 +301,7 @@ void TabCompetition::newCompetitionGuide(gdioutput &gdi, int step) { } else if (step == 2) { rc.top = gdi.getCY(); + oe->updateTabs(true, true); gdi.fillDown(); gdi.dropLine(); gdi.addString("", fontMediumPlus, "Funktioner i MeOS"); @@ -391,7 +392,7 @@ void TabCompetition::createCompetition(gdioutput &gdi) { int t = convertAbsoluteTimeHMS(start, -1); if (t > 0 && t < 3600*24) { t = max(0, t-3600); - oe->setZeroTime(formatTimeHMSW(t)); + oe->setZeroTime(formatTimeHMS(t)); } else throw meosException("Ogiltig tid"); diff --git a/code/oBase.h b/code/oBase.h index 19bda4e..e86e0c8 100644 --- a/code/oBase.h +++ b/code/oBase.h @@ -43,7 +43,7 @@ class oDataContainer; typedef void * pvoid; typedef vector< vector > * pvectorstr; -enum RunnerStatus {StatusOK=1, StatusDNS=20, StatusMP=3, +enum RunnerStatus {StatusOK=1, StatusDNS=20, StatusCANCEL = 21, StatusMP=3, StatusDNF=4, StatusDQ=5, StatusMAX=6, StatusUnknown=0, StatusNotCompetiting=99}; @@ -64,6 +64,7 @@ enum SortOrder {ClassStartTime, SortByFinishTimeReverse, SortByStartTime, CourseResult, + SortByEntryTime, Custom, SortEnumLastItem}; diff --git a/code/oCard.cpp b/code/oCard.cpp index 3a2c24a..951bb29 100644 --- a/code/oCard.cpp +++ b/code/oCard.cpp @@ -668,7 +668,7 @@ wstring oCard::getRogainingSplit(int ix, int startTime) const int t = it->getAdjustedTime(); if (0 == ix--) { if (t > 0 && t > startTime) - return formatTimeW(t - startTime); + return formatTime(t - startTime); } if (it->isUsed) startTime = t; diff --git a/code/oClass.cpp b/code/oClass.cpp index c36b8de..26905da 100644 --- a/code/oClass.cpp +++ b/code/oClass.cpp @@ -1522,7 +1522,7 @@ void oEvent::getNumClassRunners(int id, int leg, int &total, int &finished, int if (it->tStatus != StatusUnknown) finished++; - else if (it->tStatus==StatusDNS) + else if (it->tStatus==StatusDNS || it->tStatus == StatusCANCEL) dns++; } else { @@ -1532,7 +1532,7 @@ void oEvent::getNumClassRunners(int id, int leg, int &total, int &finished, int total++; if (r->tStatus!=StatusUnknown) finished++; - else if (it->tStatus==StatusDNS) + else if (it->tStatus==StatusDNS || it->tStatus == StatusCANCEL) dns++; } } @@ -2698,7 +2698,7 @@ void oClass::getStatistics(const set &feeLock, int &entries, int &started) if (it->getClassId()==Id) { if (feeLock.empty() || feeLock.count(it->getDCI().getInt("Fee"))) { entries++; - if (it->getStatus()!= StatusUnknown && it->getStatus()!= StatusDNS) + if (it->getStatus()!= StatusUnknown && it->getStatus()!= StatusDNS && it->tStatus != StatusCANCEL) started++; } } @@ -4145,3 +4145,11 @@ int oClass::getPreceedingLeg(int leg) const { } return -1; } + +bool oClass::lockedForking() const { + return getDCI().getInt("Locked") != 0; +} + +void oClass::lockedForking(bool locked) { + getDI().setInt("Locked", locked); +} diff --git a/code/oClass.h b/code/oClass.h index a842221..4c176e9 100644 --- a/code/oClass.h +++ b/code/oClass.h @@ -262,6 +262,10 @@ public: static void initClassId(oEvent &oe); + // Return true if forking in the class is locked + bool lockedForking() const; + void lockedForking(bool locked); + // Draw data int getDrawFirstStart() const; void setDrawFirstStart(int st); diff --git a/code/oControl.cpp b/code/oControl.cpp index 1a2c585..0562e82 100644 --- a/code/oControl.cpp +++ b/code/oControl.cpp @@ -488,13 +488,13 @@ int oControl::getTimeAdjust() const wstring oControl::getTimeAdjustS() const { - return getTimeMSW(getTimeAdjust()); + return getTimeMS(getTimeAdjust()); } wstring oControl::getMinTimeS() const { if (getMinTime()>0) - return getTimeMSW(getMinTime()); + return getTimeMS(getMinTime()); else return makeDash(L"-"); } @@ -706,8 +706,8 @@ void oEvent::setupControlStatistics() const { } } - if (!it->isVacant() && it->getStatus() != StatusDNS - && it->getStatus() != StatusNotCompetiting) { + if (!it->isVacant() && it->getStatus() != StatusDNS && it->getStatus() != StatusCANCEL + && it->getStatus() != StatusNotCompetiting) { for (int i = 0; i < nc; i++) { pControl ctrl = pc->getControl(i); @@ -855,9 +855,9 @@ void oControl::addTableRow(Table &table) const { int nv = getNumVisitors(true); table.set(row++, it, 50, itow(nv), false); - table.set(row++, it, 51, nv > 0 ? formatTimeW(getMissedTimeMax()) : L"-", false); - table.set(row++, it, 52, nv > 0 ? formatTimeW(getMissedTimeTotal()/nv) : L"-", false); - table.set(row++, it, 53, nv > 0 ? formatTimeW(getMissedTimeMedian()) : L"-", false); + table.set(row++, it, 51, nv > 0 ? formatTime(getMissedTimeMax()) : L"-", false); + table.set(row++, it, 52, nv > 0 ? formatTime(getMissedTimeTotal()/nv) : L"-", false); + table.set(row++, it, 53, nv > 0 ? formatTime(getMissedTimeMedian()) : L"-", false); oe->oControlData->fillTableCol(it, table, true); } diff --git a/code/oCourse.cpp b/code/oCourse.cpp index ed940e5..b2e8251 100644 --- a/code/oCourse.cpp +++ b/code/oCourse.cpp @@ -693,7 +693,7 @@ void oEvent::calculateNumRemainingMaps() for (oRunnerList::const_iterator it=Runners.begin(); it != Runners.end(); ++it) { - if (!it->isRemoved() && it->getStatus() != StatusDNS) { + if (!it->isRemoved() && it->getStatus() != StatusDNS && it->getStatus() != StatusCANCEL) { pCourse pc = it->getCourse(false); if (pc) { if (pc->tMapsRemaining != numeric_limits::min()) diff --git a/code/oDataContainer.cpp b/code/oDataContainer.cpp index 4f3b6d8..ef0526b 100644 --- a/code/oDataContainer.cpp +++ b/code/oDataContainer.cpp @@ -322,6 +322,20 @@ bool oDataContainer::setString(oBase *ob, const char *name, const wstring &v) } +const wstring &oDataContainer::formatString(const oBase *ob, const char *Name) const { + const oDataInfo *odi = findVariable(Name); + if (odi->dataDefiner) { + return odi->dataDefiner->formatData(ob); + } + else if (odi->Type == oDTString) { + return getString(ob, Name); + } + else if (odi->Type == oDTInt) { + return itow(getInt(ob, Name)); + } + throw std::exception("oDataContainer: Formatting failed."); +} + const wstring &oDataContainer::getString(const oBase *ob, const char *Name) const { const oDataInfo *odi=findVariable(Name); @@ -982,6 +996,7 @@ void oDataContainer::buildTableCol(Table *table) oDataInfo &di=ordered[kk]; if (di.dataDefiner) { + table->addDataDefiner(di.Name, di.dataDefiner); int w = strlen(di.Description)*6; di.tableIndex = di.dataDefiner->addTableColumn(table, di.Description, w); } diff --git a/code/oDataContainer.h b/code/oDataContainer.h index 304845d..b61a420 100644 --- a/code/oDataContainer.h +++ b/code/oDataContainer.h @@ -34,10 +34,11 @@ class Table; class oDataDefiner { public: virtual ~oDataDefiner() {} - virtual const wstring &formatData(oBase *obj) const = 0; + virtual const wstring &formatData(const oBase *obj) const = 0; virtual wstring setData(oBase *obj, const wstring &input) const = 0; /** Used to define/add the table column in the table*/ virtual int addTableColumn(Table *table, const string &description, int minWidth) const = 0; + virtual void prepare(oEvent *oe) const {} }; struct oDataInfo { @@ -160,6 +161,7 @@ public: bool setString(oBase *ob, const char *name, const wstring &v); const wstring &getString(const oBase *ob, const char *name) const; + const wstring &formatString(const oBase *ob, const char *name) const; bool setDate(void *data, const char *Name, const wstring &V); const wstring &getDate(const void *data, const char *Name) const; @@ -236,9 +238,13 @@ public: else return false; } - inline wstring getString(const char *Name) const + inline const wstring &getString(const char *Name) const {return oDC->getString(oB, Name);} + inline const wstring &formatString(const oBase *oB, const char *name) const { + return oDC->formatString(oB, name); + } + inline bool setDate(const char *Name, const wstring &Value) { if (oDC->setDate(Data, Name, Value)){ @@ -327,6 +333,10 @@ public: inline const wstring &getString(const char *Name) const {return oDC->getString(oB, Name);} + inline const wstring &formatString(const oBase *oB, const char *name) const { + return oDC->formatString(oB, name); + } + inline const wstring &getDate(const char *Name) const {return oDC->getDate(Data, Name);} diff --git a/code/oEvent.cpp b/code/oEvent.cpp index 0dd3d95..b698897 100644 --- a/code/oEvent.cpp +++ b/code/oEvent.cpp @@ -71,7 +71,7 @@ class RelativeTimeFormatter : public oDataDefiner { public: RelativeTimeFormatter(const char *n) : name(n) {} - const wstring &formatData(oBase *obj) const { + const wstring &formatData(const oBase *obj) const { int t = obj->getDCI().getInt(name); if (t <= 0) return makeDash(L"-"); @@ -92,9 +92,9 @@ class AbsoluteTimeFormatter : public oDataDefiner { public: AbsoluteTimeFormatter(const char *n) : name(n) {} - const wstring &formatData(oBase *obj) const { + const wstring &formatData(const oBase *obj) const { int t = obj->getDCI().getInt(name); - return formatTimeW(t); + return formatTime(t); } wstring setData(oBase *obj, const wstring &input) const { int t = convertAbsoluteTimeMS(input); @@ -108,6 +108,49 @@ class AbsoluteTimeFormatter : public oDataDefiner { } }; +class PayMethodFormatter : public oDataDefiner { + mutable vector< pair > modes; + mutable map 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 { + 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; + } + } + + wstring setData(oBase *ob, const wstring &input) const { + auto res = setCodes.find(canonizeName(input.c_str())); + if (res != setCodes.end()) { + ob->getDI().setInt("PayMode", res->second); + } + return formatData(ob); + } + + int addTableColumn(Table *table, const string &description, int minWidth) const { + return table->addColumn(description, max(minWidth, 90), true, true); + } +}; + oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) { readOnly = false; @@ -275,11 +318,13 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oClubData->addVariableEnum("Invoice", 1, "Faktura", eInvoice); oClubData->addVariableInt("InvoiceNo", oDataContainer::oIS16U, "Fakturanummer"); + static PayMethodFormatter paymentMethod; + oRunnerData=new oDataContainer(oRunner::dataSize); oRunnerData->addVariableCurrency("Fee", "Anm. avgift"); oRunnerData->addVariableCurrency("CardFee", "Brickhyra"); oRunnerData->addVariableCurrency("Paid", "Betalat"); - oRunnerData->addVariableInt("PayMode", oDataContainer::oIS8U, "Betalsätt"); + oRunnerData->addVariableInt("PayMode", oDataContainer::oIS8U, "Betalsätt", &paymentMethod); oRunnerData->addVariableCurrency("Taxable", "Skattad avgift"); oRunnerData->addVariableInt("BirthYear", oDataContainer::oIS32, "Födelseår"); oRunnerData->addVariableString("Bib", 8, "Nummerlapp").zeroSortPadding = 5; @@ -287,6 +332,8 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) //oRunnerData->addVariableInt("VacRank", oDataContainer::oIS16U, "Vak. ranking"); oRunnerData->addVariableDate("EntryDate", "Anm. datum"); + static AbsoluteTimeFormatter atf("EntryTime"); + oRunnerData->addVariableInt("EntryTime", oDataContainer::oIS32, "Anm. tid", &atf); vector< pair > sex; sex.push_back(make_pair(L"M", L"Man")); @@ -393,6 +440,7 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oClassData->addVariableEnum("BibMode", 1, "Nummerlappshantering", bibMode); oClassData->addVariableInt("Unordered", oDataContainer::oIS8U, "Oordnade parallella"); oClassData->addVariableInt("Heat", oDataContainer::oIS8U, "Heat"); + oClassData->addVariableInt("Locked", oDataContainer::oIS8U, "Låst gaffling"); oTeamData = new oDataContainer(oTeam::dataSize); oTeamData->addVariableCurrency("Fee", "Anm. avgift"); @@ -400,6 +448,7 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oTeamData->addVariableInt("PayMode", oDataContainer::oIS8U, "Betalsätt"); oTeamData->addVariableCurrency("Taxable", "Skattad avgift"); oTeamData->addVariableDate("EntryDate", "Anm. datum"); + oTeamData->addVariableInt("EntryTime", oDataContainer::oIS32, "Anm. tid", &atf); oTeamData->addVariableString("Nationality", 3, "Nationalitet"); oTeamData->addVariableString("Country", 23, "Land"); oTeamData->addVariableString("Bib", 8, "Nummerlapp").zeroSortPadding = 5; @@ -863,7 +912,7 @@ bool oEvent::save(const wstring &fileIn) { const wchar_t *file = fileIn.c_str(); xmlparser xml; - ProgressWindow pw(gdibase.getHWND()); + ProgressWindow pw(gdibase.getHWNDTarget()); if (Runners.size()>200) pw.init(); @@ -1019,7 +1068,7 @@ bool oEvent::open(const wstring &file, bool Import) openFileLock->lockFile(file); xmlparser xml; - xml.setProgress(gdibase.getHWND()); + xml.setProgress(gdibase.getHWNDTarget()); tic(); string log; xml.read(file); @@ -1596,8 +1645,10 @@ pRunner oEvent::addRunner(const wstring &name, int clubId, int classId, r.setBirthYear(birthYear); pRunner pr = addRunner(r, true); - if (pr->getDI().getInt("EntryDate") == 0) - pr->getDI().setDate("EntryDate", getLocalDateW()); + if (pr->getDI().getInt("EntryDate") == 0 && !pr->isVacant()) { + pr->getDI().setDate("EntryDate", getLocalDate()); + pr->getDI().setInt("EntryTime", getLocalAbsTime()); + } if (pr->Class) { int heat = pr->Class->getDCI().getInt("Heat"); if (heat != 0) @@ -1640,9 +1691,10 @@ pRunner oEvent::addRunnerFromDB(const pRunner db_r, memcpy(r.oData, db_r->oData, sizeof(r.oData)); pRunner pr = addRunner(r, true); - if (pr->getDI().getInt("EntryDate") == 0) - pr->getDI().setDate("EntryDate", getLocalDateW()); - + if (pr->getDI().getInt("EntryDate") == 0 && !pr->isVacant()) { + pr->getDI().setDate("EntryDate", getLocalDate()); + pr->getDI().setInt("EntryTime", getLocalAbsTime()); + } if (r.Class) { int heat = r.Class->getDCI().getInt("Heat"); if (heat != 0) @@ -2105,7 +2157,7 @@ void oEvent::setDate(const wstring &m) int d = convertDateYMS(m, true); if (d <= 0) throw meosException(L"Felaktigt datumformat 'X' (Använd ÅÅÅÅ-MM-DD).#" + m); - Date = formatDateW(d, true); + Date = formatDate(d, true); updateChanged(); } } @@ -2116,10 +2168,10 @@ const wstring &oEvent::getAbsTime(DWORD time) const { t = 0; int days = time/(3600*24); if (days <= 0) - return formatTimeHMSW(t % (24*3600)); + return formatTimeHMS(t % (24*3600)); else { wstring &res = StringCache::getInstance().wget(); - res = itow(days) + L"D " + formatTimeHMSW(t % (24*3600)); + res = itow(days) + L"D " + formatTimeHMS(t % (24*3600)); return res; } } @@ -2175,7 +2227,7 @@ wstring oEvent::getAbsDateTimeISO(DWORD time, bool includeDate, bool useGMT) con __int64 sec = SystemTimeToInt64Second(st); sec = sec + (extraDay * 3600 * 24); st = Int64SecondToSystemTime(sec); - dateS = convertSystemDateW(st); + dateS = convertSystemDate(st); } } @@ -2986,7 +3038,7 @@ void oEvent::generateMinuteStartlist(gdioutput &gdi) { continue; if (!it->Class && blocks[k]!=0) continue; - if (it->getStatus() == StatusNotCompetiting) + if (it->getStatus() == StatusNotCompetiting || it->getStatus() == StatusCANCEL) continue; if (LastStartTime!=it->tStartTime) { @@ -3141,7 +3193,7 @@ bool oEvent::enumerateCompetitions(const wchar_t *file, const wchar_t *filetype) SYSTEMTIME st; FileTimeToSystemTime(&fd.ftLastWriteTime, &st); - ci.Modified=convertSystemTime(st); + ci.Modified=convertSystemTimeN(st); xmlparser xp; try { @@ -3345,7 +3397,7 @@ bool oEvent::enumerateBackups(const wstring &file, const wstring &filetype, int FileTimeToLocalFileTime(&fd.ftLastWriteTime, &localTime); FileTimeToSystemTime(&localTime, &st); - ci.Modified=convertSystemTime(st); + ci.Modified=convertSystemTimeN(st); xmlparser xp; try { @@ -3500,6 +3552,7 @@ void oEvent::clear() punchIndex.clear(); punches.clear(); + cachedFirstStart.clear(); updateFreeId(); @@ -3571,7 +3624,7 @@ void oEvent::newCompetition(const wstring &name) SYSTEMTIME st; GetLocalTime(&st); - Date = convertSystemDateW(st); + Date = convertSystemDate(st); ZeroTime = st.wHour*3600; Name = name; @@ -4035,23 +4088,28 @@ void oEvent::convertTimes(SICard &sic) const } } -int oEvent::getFirstStart(int ClassId) -{ - oRunnerList::iterator it=Runners.begin(); - int MinTime=3600*24; +int oEvent::getFirstStart(int classId) const { + auto &cf = cachedFirstStart[classId]; + if (dataRevision == cf.first) + return cf.second; + + oRunnerList::const_iterator it=Runners.begin(); + int minTime=3600*24; while(it!=Runners.end()){ - if (ClassId==0 || it->getClassId()==ClassId) - if (it->tStartTimetStatus==StatusOK && it->tStartTime!=0) - MinTime=it->tStartTime; - + if (!it->isRemoved() && classId==0 || it->getClassId()==classId) + if (it->tStartTime < minTime && it->tStatus!=StatusNotCompetiting && it->tStartTime>0) + minTime = it->tStartTime; ++it; } - if (MinTime==3600*24) - MinTime=0; + if (minTime==3600*24) + minTime=0; - return MinTime; + cf.first = dataRevision; + cf.second = minTime; + + return minTime; } bool oEvent::hasRank() const @@ -4077,7 +4135,7 @@ int oEvent::getMaximalTime() const wstring oEvent::getMaximalTimeS() const { - return formatTimeW(getMaximalTime()); + return formatTime(getMaximalTime()); } @@ -4141,19 +4199,24 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) { oTeamList::iterator it; for (it=Teams.begin(); it != Teams.end(); ++it) it->apply(false, 0, false); - + map teamStartNo; + if (!firstNumber.empty()) { // Clear out start number temporarily, to not use it for sorting for (it=Teams.begin(); it != Teams.end(); ++it) { + if (it->isRemoved()) + continue; if (ClassId==0 || it->getClassId()==ClassId) { if (it->getClassRef() && it->getClassRef()->getBibMode() != BibFree) { for (size_t i = 0; i < it->Runners.size(); i++) { if (it->Runners[i]) { + //runnerStartNo[it->Runners[i]->getId()] = it->Runners[i]->getStartNo(); it->Runners[i]->setStartNo(0, false); it->Runners[i]->setBib(L"", 0, false, false); } } } + teamStartNo[it->getId()] = it->getStartNo(); it->setStartNo(0, false); } } @@ -4166,10 +4229,20 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) { int num = oClass::extractBibPattern(firstNumber, pattern); for (it=Teams.begin(); it != Teams.end(); ++it) { - if (ClassId==0 || it->getClassId()==ClassId) { + if (it->isRemoved()) + continue; + + if (ClassId == 0 || it->getClassId() == ClassId) { wchar_t bib[32]; swprintf_s(bib, pattern, num); - it->setBib(bib, num, true, false); + bool lockedStartNo = it->Class && it->Class->lockedForking(); + if (lockedStartNo) { + it->setBib(bib, num, false, false); + it->setStartNo(teamStartNo[it->getId()], false); + } + else { + it->setBib(bib, num, true, false); + } num++; it->apply(true, 0, false); } @@ -4205,6 +4278,7 @@ void oEvent::addAutoBib() { tit->apply(false, 0, false); } + map teamStartNo; // Clear out start number temporarily, to not use it for sorting for (oTeamList::iterator tit = Teams.begin(); tit != Teams.end(); ++tit) { if (tit->skip()) @@ -4212,6 +4286,8 @@ void oEvent::addAutoBib() { pClass cls = tit->getClassRef(); if (cls == 0) continue; + teamStartNo[tit->getId()] = tit->getStartNo(); + wstring bibInfo = cls->getDCI().getString("Bib"); bool teamAssign = !bibInfo.empty(); @@ -4313,10 +4389,18 @@ void oEvent::addAutoBib() { } } else { + bool lockedForking = cls->lockedForking(); for (size_t k = 0; k < tl.size(); k++) { wchar_t buff[32]; swprintf_s(buff, pattern, number); - tl[k]->setBib(buff, number, true, false); + + if (lockedForking) { + tl[k]->setBib(buff, number, false, false); + tl[k]->setStartNo(teamStartNo[tl[k]->getId()], false); + } + else { + tl[k]->setBib(buff, number, true, false); + } number += interval; tl[k]->apply(true, 0, false); } @@ -4382,6 +4466,7 @@ const vector< pair > &oEvent::fillStatus(vector< pairskip() || it->getCardNo() || it->isVacant() || it->needNoCard()) continue; - if (it->getStatus() == StatusDNS || it->getStatus() == StatusNotCompetiting) + if (it->getStatus() == StatusDNS || it->getStatus() == StatusCANCEL || it->getStatus() == StatusNotCompetiting) continue; if (it->Club!=lastClub) { @@ -4610,12 +4695,15 @@ void oEvent::calcUseStartSeconds() const wstring &oEvent::formatStatus(RunnerStatus status) { - const static wstring stats[8]={L"?", L"Godkänd", L"Ej start", L"Felst.", L"Utg.", L"Disk.", L"Maxtid", L"Deltar ej"}; + const static wstring stats[9]={L"?", L"Godkänd", L"Ej start", L"Felst.", L"Utg.", L"Disk.", + L"Maxtid", L"Deltar ej", L"Återbud[status]"}; switch(status) { case StatusOK: return lang.tl(stats[1]); case StatusDNS: return lang.tl(stats[2]); + case StatusCANCEL: + return lang.tl(stats[8]); case StatusMP: return lang.tl(stats[3]); case StatusDNF: @@ -5501,11 +5589,6 @@ void oEvent::sanityCheck(gdioutput &gdi, bool expectResult, int onlyThisClass) { } } -HWND oEvent::hWnd() const -{ - return gdibase.getHWND(); -} - oTimeLine::~oTimeLine() { } @@ -5640,7 +5723,7 @@ wstring oEvent::cloneCompetition(bool cloneRunners, bool cloneTimes, convertDateYMS(Date, st, false); __int64 absD = SystemTimeToInt64Second(st); absD += 3600*24; - ce.Date = convertSystemDateW(Int64SecondToSystemTime(absD)); + ce.Date = convertSystemDate(Int64SecondToSystemTime(absD)); } int len = Name.length(); if (len > 2 && isdigit(Name[len-1]) && !isdigit(Name[len-2])) { @@ -6361,7 +6444,7 @@ bool oEvent::hasNextStage() const { } bool oEvent::hasPrevStage() const { - return !getDCI().getString("PreEvent").empty(); + return !getDCI().getString("PreEvent").empty() || getStageNumber() > 1; } int oEvent::getNumStages() const { @@ -6396,7 +6479,7 @@ void oEvent::changedObject() { } void oEvent::pushDirectChange() { - PostMessage(gdibase.getMain(), WM_USER + 4, 0, 0); + PostMessage(gdibase.getHWNDMain(), WM_USER + 4, 0, 0); } int oEvent::getBibClassGap() const { @@ -6555,4 +6638,121 @@ void oEvent::useDefaultProperties(bool useDefault) { savedProperties.clear(); } } -} \ No newline at end of file +} + +static void checkValid(oEvent &oe, int &time, int delta, const wstring &name) { + int srcTime = time; + time += delta; + if (time <= 0) + time += 24 * 3600; + if (time > 24 * 3600) + time -= 24 * 3600; + if (time < 0 || time > 22 * 3600) { + throw meosException(L"X har en tid (Y) som inte är kompatibel med förändringen.#" + name + L"#" + oe.getAbsTime(srcTime)); + } +} + + +void oEvent::updateStartTimes(int delta) { + for (int pass = 0; pass <= 1; pass++) { + for (oClass &c : Classes) { + if (c.isRemoved()) + continue; + for (unsigned i = 0; i < c.getNumStages(); i++) { + int st = c.getStartData(i); + if (st > 0) { + checkValid(*oe, st, delta, c.getName()); + if (pass == 1) { + c.setStartData(i, st); + c.synchronize(true); + } + } + } + } + + if (pass == 1) + reEvaluateAll(set(), false); + + for (oRunner &r : Runners) { + if (r.isRemoved()) + continue; + if (r.Class && r.Class->getStartType(r.getLegNumber()) == STDrawn) { + int st = r.getStartTime(); + if (st > 0) { + checkValid(*oe, st, delta, r.getName()); + if (pass == 1) { + r.setStartTime(st, true, false, false); + r.synchronize(true); + } + } + } + int ft = r.getFinishTime(); + if (ft > 0) { + checkValid(*oe, ft, delta, r.getName()); + if (pass == 1) { + r.setFinishTime(ft); + r.synchronize(true); + } + } + } + + for (oCard &c : Cards) { + if (c.isRemoved()) + continue; + wstring desc = L"Bricka X#" + c.getCardNoString(); + for (oPunch &p : c.punches) { + int t = p.Time; + if (t > 0) { + if (c.getOwner() != 0) + checkValid(*oe, t, delta, desc); + else { + // Skip check + t += delta; + if (t <= 0) + t += 24 * 3600; + } + + if (pass == 1) { + p.setTimeInt(t, false); + } + } + } + } + + for (oTeam &t : Teams) { + if (t.isRemoved()) + continue; + if (t.Class && t.Class->getStartType(0) == STDrawn) { + int st = t.getStartTime(); + if (st > 0) { + checkValid(*oe, st, delta, t.getName()); + if (pass == 1) { + t.setStartTime(st, true, false, false); + t.synchronize(true); + } + } + } + int ft = t.getFinishTime(); + if (ft > 0) { + checkValid(*oe, ft, delta, t.getName()); + if (pass == 1) { + t.setFinishTime(ft); + t.synchronize(true); + } + } + } + + for (oFreePunch &p : punches) { + int t = p.Time; + if (t > 0) { + if (pass == 1) { + t += delta; + if (t <= 0) + t += 24 * 3600; + + p.setTimeInt(t, false); // Skip check + } + } + } + } +} diff --git a/code/oEvent.h b/code/oEvent.h index fc346ad..98a0ac2 100644 --- a/code/oEvent.h +++ b/code/oEvent.h @@ -560,9 +560,6 @@ public: void saveProperties(const wchar_t *file); void loadProperties(const wchar_t *file); - // Get window handle - HWND hWnd() const; - /** Get number of classes*/ int getNumClasses() const {return Classes.size();} @@ -799,10 +796,11 @@ protected: int tClubDataRevision; bool readOnly; mutable int tLongTimesCached; - + mutable map > cachedFirstStart; //First start per classid. map, oFreePunch> advanceInformationPunches; public: + void updateStartTimes(int delta); void useDefaultProperties(bool useDefault); @@ -864,7 +862,8 @@ public: void updateRunnerDatabase(); void updateRunnerDatabase(pRunner r, map &clubIdMap); - int getFirstStart(int ClassId=0); + /** Returns the first start in a class */ + int getFirstStart(int classId = 0) const; void convertTimes(SICard &sic) const; pCard getCard(int Id) const; @@ -1273,7 +1272,7 @@ public: bool checkCardUsed(gdioutput &gdi, oRunner &runnerToAssignCard, int CardNo); void analyseDNS(vector &unknown_dns, vector &known_dns, - vector &known, vector &unknown); + vector &known, vector &unknown, bool &hasSetDNS); void importOECSV_Data(const wstring &oecsvfile, bool clear); void importXML_IOF_Data(const wstring &clubfile, const wstring &competitorfile, bool clear); diff --git a/code/oEventSpeaker.cpp b/code/oEventSpeaker.cpp index e3b1e3e..a530560 100644 --- a/code/oEventSpeaker.cpp +++ b/code/oEventSpeaker.cpp @@ -349,10 +349,10 @@ void renderRowSpeakerList(const oSpeakerObject &r, const oSpeakerObject *next_r, row.push_back(SpeakerString(normalText, r.club)); if (r.status == StatusOK) { - row.push_back(SpeakerString(textRight, formatTimeW(r.runningTime.preliminary))); + row.push_back(SpeakerString(textRight, formatTime(r.runningTime.preliminary))); if (r.runningTime.time != r.runningTimeLeg.time) - row.push_back(SpeakerString(textRight, formatTimeW(r.runningTimeLeg.time))); + row.push_back(SpeakerString(textRight, formatTime(r.runningTimeLeg.time))); else row.push_back(SpeakerString()); @@ -1053,7 +1053,7 @@ int oEvent::setupTimeLineEvents(int classId, int currentTime) continue; if (!r.Class ||r.Class->Id != classId) continue; - if (r.tStatus == StatusDNS || r.tStatus == StatusNotCompetiting) + if (r.tStatus == StatusDNS || r.tStatus == StatusCANCEL || r.tStatus == StatusNotCompetiting) continue; // if (r.CardNo == 0) // continue; @@ -1377,7 +1377,7 @@ int oEvent::setupTimeLineEvents(vector &started, const vector< pair 0); r.tTimeAfter = timeAfter; @@ -1461,7 +1461,7 @@ int oEvent::setupTimeLineEvents(vector &started, const vector< pair 0); diff --git a/code/oImportExport.cpp b/code/oImportExport.cpp index 9f5afb3..a72cc88 100644 --- a/code/oImportExport.cpp +++ b/code/oImportExport.cpp @@ -73,6 +73,8 @@ int ConvertStatusToOE(int i) case StatusOK: return 0; case StatusDNS: // Ej start + case StatusCANCEL: + case StatusNotCompetiting: return 1; case StatusDNF: // Utg. return 2; @@ -206,7 +208,7 @@ bool oEvent::exportOECSV(const wchar_t *file, int languageTypeIndex, bool includ // Excel format HH:MM:SS if (it->getRunningTime() > 0) - row[OEtime] = formatTimeHMS(it->getRunningTime()); + row[OEtime] = gdibase.recodeToNarrow(formatTimeHMS(it->getRunningTime())); row[OEstatus] = conv_is(ConvertStatusToOE(it->getStatus())); row[OEclubno] = conv_is(it->getClubId()); @@ -259,7 +261,7 @@ bool oEvent::exportOECSV(const wchar_t *file, int languageTypeIndex, bool includ continue; row.push_back(gdibase.recodeToNarrow(pc->getControl(k)->getIdS())); if (unsigned(k) < sp.size() && sp[k].time > 0) - row.push_back(formatTimeHMS(sp[k].time - it->tStartTime)); + row.push_back(gdibase.recodeToNarrow(formatTimeHMS(sp[k].time - it->tStartTime))); else row.push_back("-----"); } @@ -276,7 +278,7 @@ bool oEvent::exportOECSV(const wchar_t *file, int languageTypeIndex, bool includ int t = punch->getAdjustedTime(); if (it->tStartTime > 0 && t > 0 && t > it->tStartTime) - row.push_back(formatTimeHMS(t - it->tStartTime)); + row.push_back(gdibase.recodeToNarrow(formatTimeHMS(t - it->tStartTime))); else return "-----"; } @@ -1178,7 +1180,7 @@ void oEvent::importXML_IOF_Data(const wstring &clubfile, { if (!clubfile.empty()) { xmlparser xml_club; - xml_club.setProgress(gdibase.getHWND()); + xml_club.setProgress(gdibase.getHWNDTarget()); if (clear) runnerDB->clearClubs(); @@ -1218,7 +1220,7 @@ void oEvent::importXML_IOF_Data(const wstring &clubfile, if (!competitorfile.empty()) { xmlparser xml_cmp; - xml_cmp.setProgress(gdibase.getHWND()); + xml_cmp.setProgress(gdibase.getHWNDTarget()); gdibase.dropLine(); gdibase.addString("",0,"Läser löpare..."); gdibase.refresh(); @@ -2270,7 +2272,8 @@ void oEvent::exportIOFResults(xmlparser &xml, bool selfContained, const set if (pc) xml.write("CourseLength", "unit", L"m", pc->getLengthS()); pCourse pcourse=pc; - if (pcourse && it->getLegStatus(-1, false)>0 && it->getLegStatus(-1, false)!=StatusDNS) { + auto legStatus = it->getLegStatus(-1, false); + if (pcourse && legStatus>0 && legStatus!=StatusDNS && legStatus!=StatusCANCEL) { int no = 1; bool hasRogaining = pcourse->hasRogaining(); int startIx = pcourse->useFirstAsStart() ? 1 : 0; @@ -2375,7 +2378,7 @@ void oEvent::exportIOFResults(xmlparser &xml, bool selfContained, const set pCourse pcourse=it->getCourse(true); if (pcourse && it->getStatus()>0 && it->getStatus()!=StatusDNS - && it->getStatus()!=StatusNotCompetiting) { + && it->getStatus()!=StatusNotCompetiting && it->getStatus() != StatusCANCEL) { bool hasRogaining = pcourse->hasRogaining(); int no = 1; int startIx = pcourse->useFirstAsStart() ? 1 : 0; @@ -2527,7 +2530,7 @@ void oEvent::exportTeamSplits(xmlparser &xml, const set &classes, bool oldS } pCourse pcourse=pc; if (pcourse && r->getStatus()>0 && r->getStatus()!=StatusDNS - && r->getStatus()!=StatusNotCompetiting) { + && r->getStatus()!=StatusNotCompetiting && r->getStatus() != StatusCANCEL) { int no = 1; bool hasRogaining = pcourse->hasRogaining(); int startIx = pcourse->useFirstAsStart() ? 1 : 0; diff --git a/code/oListInfo.cpp b/code/oListInfo.cpp index e36a875..2b437b3 100644 --- a/code/oListInfo.cpp +++ b/code/oListInfo.cpp @@ -232,7 +232,7 @@ public: int w = 0; TextInfo ti; - HDC hDC = GetDC(gdi.getHWND()); + HDC hDC = GetDC(gdi.getHWNDTarget()); for (multimap::iterator it = words.begin(); it != words.end(); ++it) { ti.xp = 0; @@ -247,7 +247,7 @@ public: } } - ReleaseDC(gdi.getHWND(), hDC); + ReleaseDC(gdi.getHWNDTarget(), hDC); return w; } @@ -687,12 +687,12 @@ const wstring &oEvent::formatSpecialStringAux(const oPrintPost &pp, const oListP case lControlMedianLostTime: if (ctrl) - wsptr = &formatTimeW(ctrl->getMissedTimeMedian()); + wsptr = &formatTime(ctrl->getMissedTimeMedian()); break; case lControlMaxLostTime: if (ctrl) - wsptr = &formatTimeW(ctrl->getMissedTimeMax()); + wsptr = &formatTime(ctrl->getMissedTimeMax()); break; case lControlMistakeQuotient: @@ -896,12 +896,15 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara break; // Common start time, skip } } - if (r->startTimeAvailable()) { + if (r->getStatus() == StatusCANCEL) { + wsptr = &oEvent::formatStatus(StatusCANCEL); + } + else if (r->startTimeAvailable()) { if (pp.type != lRunnerStartZero) wsptr = &r->getStartTimeCompact(); else { int st = r->getStartTime(); - wsptr = &getTimeMSW(st-3600); + wsptr = &getTimeMS(st-oe->getFirstStart()); } } else @@ -1021,7 +1024,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara RunnerStatus ts = r->getTotalStatus(); int rt = r->getTotalRunningTime(); if (ts == StatusOK || (ts == StatusUnknown && rt > 0)) { - wstring vts = formatTimeW(rt) + L" (" + timeStatus + L")"; + wstring vts = formatTime(rt) + L" (" + timeStatus + L")"; swap(vts, timeStatus); } else { @@ -1105,7 +1108,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara int len = pc->getLength(); if (len > 0 && t > 0) { int sperkm = (1000 * t) / len; - wsptr = &formatTimeW(sperkm); + wsptr = &formatTime(sperkm); } } } @@ -1154,7 +1157,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara wsptr = &lang.tl("Struken"); else if (r) { if (r->tempStatus==StatusOK && pc && !pc->getNoTiming()) - wcscpy_s(wbf, formatTimeW(r->tempRT).c_str()); + wcscpy_s(wbf, formatTime(r->tempRT).c_str()); else wcscpy_s(wbf, formatStatus(r->tempStatus).c_str() ); } @@ -1278,14 +1281,14 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara if (r && !invalidClass) { int a = r->getTimeAdjustment(); if (a != 0) - wsptr = &getTimeMSW(a); + wsptr = &getTimeMS(a); } break; case lRunnerRogainingPointOvertime: if (r && !invalidClass) { int over = r->getRogainingOvertime(); if (over > 0) - wsptr = &formatTimeW(over); + wsptr = &formatTime(over); } break; @@ -1434,6 +1437,27 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara wcscpy_s(wbf, s.c_str()); } break; + case lRunnerPaid: + if (r) { + wstring s = formatCurrency(r->getDCI().getInt("Paid")); + wcscpy_s(wbf, s.c_str()); + } + break; + case lRunnerPayMethod: + if (r) { + wsptr = &r->getDCI().formatString(r, "PayMode"); + } + break; + case lRunnerEntryDate: + if (r && r->getDCI().getInt("EntryDate") > 0) { + wsptr = &r->getDCI().getDate("EntryDate"); + } + break; + case lRunnerEntryTime: + if (r) { + wsptr = &formatTime(r->getDCI().getInt("EntryTime")); + } + break; case lTeamFee: if (t) { wstring s = formatCurrency(t->getTeamFee()); @@ -1469,7 +1493,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara wsptr = &t->Runners[legIndex]->getStartTimeCompact(); else { int st = t->Runners[legIndex]->getStartTime(); - wsptr = &getTimeMSW(st-3600); + wsptr = &getTimeMS(st-3600); } } } @@ -1544,7 +1568,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara if (t && !invalidClass) { int over = t->getRogainingOvertime(); if (over > 0) - wsptr = &formatTimeW(over); + wsptr = &formatTime(over); } break; @@ -1562,7 +1586,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara if (t && !invalidClass) { int a = t->getTimeAdjustment(); if (a != 0) - wsptr = &getTimeMSW(a); + wsptr = &getTimeMS(a); } break; @@ -1863,7 +1887,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara res.swap(out[ix]); if (res.find_first_of('%') != res.npos) { wchar_t bf2[256]; - swprintf_s(bf2, res.c_str(), itos(nr).c_str()); + swprintf_s(bf2, res.c_str(), itow(nr).c_str()); res = bf2; } } @@ -2274,12 +2298,11 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form GeneralResult *gResult = 0; if (!li.resultModule.empty()) { wstring src; - gResult = &getGeneralResult(li.resultModule, src); oListInfo::ResultType resType = li.getResultType(); + gResult = &getGeneralResult(li.resultModule, src); gResult->calculateIndividualResults(rlist, resType, li.sortOrder == Custom, li.getParam().getInputNumber()); - if (li.sortOrder == SortByFinishTime || li.sortOrder == SortByFinishTimeReverse - || li.sortOrder == SortByStartTime) + if (li.sortOrder == SortByFinishTime || li.sortOrder == SortByFinishTimeReverse || li.sortOrder == SortByStartTime) gResult->sort(rlist, li.sortOrder); } diff --git a/code/oListInfo.h b/code/oListInfo.h index 5aaf96b..514928b 100644 --- a/code/oListInfo.h +++ b/code/oListInfo.h @@ -112,6 +112,10 @@ enum EPostType lRunnerNationality, lRunnerPhone, lRunnerFee, + lRunnerPaid, + lRunnerPayMethod, + lRunnerEntryDate, + lRunnerEntryTime, lTeamName, lTeamStart, @@ -294,6 +298,8 @@ struct oListParam { GUICALLBACK cb; set selection; + bool lockUpdate; // Temporary prevent animation update + int useControlIdResultTo; int useControlIdResultFrom; int filterMaxPer; @@ -314,6 +320,18 @@ struct oListParam { bool useLargeSize; bool saved; + int bgColor; + int bgColor2; + + int fgColor; + wstring bgImage; + + int nColumns; + bool animate; + int timePerPage; + int margin; + int screenMode;// 0 normal window, 1 = page by page, 2 = scroll + void updateDefaultName(const wstring &pname) const {defaultName = pname;} void setCustomTitle(const wstring &t) {title = t;} void getCustomTitle(wchar_t *t) const; // 256 size buffer required. Get title if set @@ -347,8 +365,6 @@ struct oListParam { return legNumber >= 0 ? legNumber : 1000; } - - private: int legNumber; }; diff --git a/code/oPunch.cpp b/code/oPunch.cpp index d4523a9..2e84237 100644 --- a/code/oPunch.cpp +++ b/code/oPunch.cpp @@ -146,7 +146,7 @@ wstring oPunch::getRunningTime(int startTime) const { int t = getAdjustedTime(); if (startTime>0 && t>0 && t>startTime) - return formatTimeW(t-startTime); + return formatTime(t-startTime); else return makeDash(L"-"); } diff --git a/code/oRunner.cpp b/code/oRunner.cpp index f24ec36..e740300 100644 --- a/code/oRunner.cpp +++ b/code/oRunner.cpp @@ -47,8 +47,8 @@ oRunner::RaceIdFormatter oRunner::raceIdFormatter; -const wstring &oRunner::RaceIdFormatter::formatData(oBase *ob) const { - return itow(dynamic_cast(ob)->getRaceIdentifier()); +const wstring &oRunner::RaceIdFormatter::formatData(const oBase *ob) const { + return itow(dynamic_cast(*ob).getRaceIdentifier()); } wstring oRunner::RaceIdFormatter::setData(oBase *ob, const wstring &input) const { @@ -322,6 +322,7 @@ void oAbstractRunner::addClassDefaultFee(bool resetFees) { if (isVacant()) { di.setInt("Fee", 0); di.setInt("EntryDate", 0); + di.setInt("EntryTime", 0); di.setInt("Paid", 0); if (typeid(*this)==typeid(oRunner)) di.setInt("CardFee", 0); @@ -357,7 +358,10 @@ wstring oRunner::getEntryDate(bool useTeamEntryDate) const { oDataConstInterface dci = getDCI(); int date = dci.getInt("EntryDate"); if (date == 0) { - (const_cast(this)->getDI()).setDate("EntryDate", getLocalDateW()); + auto di = (const_cast(this)->getDI()); + di.setDate("EntryDate", getLocalDate()); + di.setInt("EntryTime", convertAbsoluteTimeHMS(getLocalTimeOnly(), -1)); + } return dci.getDate("EntryDate"); } @@ -607,12 +611,12 @@ int oAbstractRunner::getRunningTime() const { const wstring &oAbstractRunner::getRunningTimeS() const { - return formatTimeW(getRunningTime()); + return formatTime(getRunningTime()); } const wstring &oAbstractRunner::getTotalRunningTimeS() const { - return formatTimeW(getTotalRunningTime()); + return formatTime(getTotalRunningTime()); } int oAbstractRunner::getTotalRunningTime() const { @@ -661,6 +665,8 @@ const wchar_t *formatIOFStatus(RunnerStatus s) { return L"OK"; case StatusDNS: return L"DidNotStart"; + case StatusCANCEL: + return L"Cancelled"; case StatusMP: return L"MisPunch"; case StatusDNF: @@ -1279,7 +1285,7 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, if (*refStatus == StatusMAX && maxTimeStatus == 2) *refStatus = StatusUnknown; - if (OK && (*refStatus==0 || *refStatus==StatusDNS || *refStatus==StatusMP || *refStatus==StatusOK || *refStatus==StatusDNF)) + if (OK && (*refStatus==0 || *refStatus==StatusDNS || *refStatus == StatusCANCEL || *refStatus==StatusMP || *refStatus==StatusOK || *refStatus==StatusDNF)) *refStatus = StatusOK; else *refStatus = RunnerStatus(max(int(StatusMP), int(*refStatus))); @@ -1816,6 +1822,17 @@ bool oRunner::operator <(const oRunner &c) { else if (tStartTime > c.tStartTime) return false; } + else if (oe->CurrentSortOrder == SortByEntryTime) { + auto dci = getDCI(), cdci = c.getDCI(); + int ed = dci.getInt("EntryDate"); + int ced = cdci.getInt("EntryDate"); + if (ed != ced) + return ed > ced; + int et = dci.getInt("EntryTime"); + int cet = cdci.getInt("EntryTime"); + if (et != cet) + return et > cet; + } else if (oe->CurrentSortOrder == ClassPoints) { if (Class != c.Class) return Class->tSortIndex < c.Class->tSortIndex; @@ -1944,6 +1961,10 @@ void oAbstractRunner::setClub(const wstring &clubName) // Vacant clubs have special logic Class->tResultInfo.clear(); } + if (Club && Club->isVacant()) { // Clear entry date/time for vacant + getDI().setInt("EntryDate", 0); + getDI().setInt("EntryTime", 0); + } } } @@ -1957,6 +1978,10 @@ pClub oAbstractRunner::setClubId(int clubId) // Vacant clubs have special logic Class->tResultInfo.clear(); } + if (Club && Club->isVacant()) { // Clear entry date/time for vacant + getDI().setInt("EntryDate", 0); + getDI().setInt("EntryTime", 0); + } } return Club; } @@ -2263,7 +2288,7 @@ bool oAbstractRunner::setStatus(RunnerStatus st, bool updateSource, bool tmpOnly int oAbstractRunner::getPrelRunningTime() const { - if (FinishTime>0 && tStatus!=StatusDNS && tStatus!=StatusDNF && tStatus!=StatusNotCompetiting) + if (FinishTime>0 && tStatus!=StatusDNS && tStatus != StatusCANCEL && tStatus!=StatusDNF && tStatus!=StatusNotCompetiting) return getRunningTime(); else if (tStatus==StatusUnknown) return oe->getComputerTime()-tStartTime; @@ -2273,7 +2298,7 @@ int oAbstractRunner::getPrelRunningTime() const wstring oAbstractRunner::getPrelRunningTimeS() const { int rt=getPrelRunningTime(); - return formatTimeW(rt); + return formatTime(rt); } oDataContainer &oRunner::getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const { @@ -2386,7 +2411,7 @@ pRunner oEvent::getRunnerByCardNo(int cardNo, int time, bool onlyWithNoCard, boo if (t == range.second) { pRunner r = range.first->second; assert(r->getCardNo() == cardNo); - if (ignoreRunnersWithNoStart && r->getStatus() == StatusDNS) + if (ignoreRunnersWithNoStart && (r->getStatus() == StatusDNS || r->getStatus() == StatusCANCEL)) return 0; if (r->getStatus() == StatusNotCompetiting) return 0; @@ -2397,7 +2422,7 @@ pRunner oEvent::getRunnerByCardNo(int cardNo, int time, bool onlyWithNoCard, boo for (hashConstIter it = range.first; it != range.second; ++it) { pRunner r = it->second; assert(r->getCardNo() == cardNo); - if (ignoreRunnersWithNoStart && r->getStatus() == StatusDNS) + if (ignoreRunnersWithNoStart && (r->getStatus() == StatusDNS || r->getStatus() == StatusCANCEL)) continue; if (r->getStatus() == StatusNotCompetiting) continue; @@ -2411,7 +2436,7 @@ pRunner oEvent::getRunnerByCardNo(int cardNo, int time, bool onlyWithNoCard, boo for (it=Runners.begin(); it != Runners.end(); ++it) { if (it->skip()) continue; - if (ignoreRunnersWithNoStart && it->getStatus() == StatusDNS) + if (ignoreRunnersWithNoStart && (it->getStatus() == StatusDNS || it->getStatus() == StatusCANCEL)) continue; if (it->getStatus() == StatusNotCompetiting) continue; @@ -2426,7 +2451,7 @@ pRunner oEvent::getRunnerByCardNo(int cardNo, int time, bool onlyWithNoCard, boo pRunner r = pRunner(&*it); if (r->CardNo != cardNo || r->isRemoved()) continue; - if (ignoreRunnersWithNoStart && r->getStatus() == StatusDNS) + if (ignoreRunnersWithNoStart && (r->getStatus() == StatusDNS || r->getStatus() == StatusCANCEL)) continue; if (r->getStatus() == StatusNotCompetiting) continue; @@ -2487,7 +2512,7 @@ pRunner oEvent::getRunnerByCardNo(int cardNo, int time, bool onlyWithNoCard, boo if (!onlyWithNoCard) { //Then try all runners. for (it=Runners.begin(); it != Runners.end(); ++it){ - if (ignoreRunnersWithNoStart && it->getStatus() == StatusDNS) + if (ignoreRunnersWithNoStart && (it->getStatus() == StatusDNS || it->getStatus() == StatusCANCEL)) continue; if (it->getStatus() == StatusNotCompetiting) continue; @@ -2509,7 +2534,7 @@ void oEvent::getRunnersByCardNo(int cardNo, bool ignoreRunnersWithNoStart, bool for (hashConstIter it = range.first; it != range.second; ++it) { pRunner r = it->second; assert(r->getCardNo() == cardNo); - if (ignoreRunnersWithNoStart && r->getStatus() == StatusDNS) + if (ignoreRunnersWithNoStart && (r->getStatus() == StatusDNS || r->getStatus() == StatusCANCEL)) continue; if (skipDuplicates && r->getRaceNo() != 0) continue; @@ -2523,7 +2548,7 @@ void oEvent::getRunnersByCardNo(int cardNo, bool ignoreRunnersWithNoStart, bool for (oRunnerList::const_iterator it=Runners.begin(); it != Runners.end(); ++it) { if (it->CardNo != cardNo) continue; - if (ignoreRunnersWithNoStart && it->getStatus() == StatusDNS) + if (ignoreRunnersWithNoStart && (it->getStatus() == StatusDNS || it->getStatus() == StatusCANCEL)) continue; if (it->getStatus() == StatusNotCompetiting) continue; @@ -3310,12 +3335,12 @@ int oRunner::getNamedSplit(int controlNumber) const wstring oRunner::getSplitTimeS(int controlNumber, bool normalized) const { - return formatTimeW(getSplitTime(controlNumber, normalized)); + return formatTime(getSplitTime(controlNumber, normalized)); } wstring oRunner::getNamedSplitS(int controlNumber) const { - return formatTimeW(getNamedSplit(controlNumber)); + return formatTime(getNamedSplit(controlNumber)); } int oRunner::getPunchTime(int controlNumber, bool normalized) const @@ -3335,7 +3360,7 @@ int oRunner::getPunchTime(int controlNumber, bool normalized) const wstring oRunner::getPunchTimeS(int controlNumber, bool normalized) const { - return formatTimeW(getPunchTime(controlNumber, normalized)); + return formatTime(getPunchTime(controlNumber, normalized)); } bool oAbstractRunner::isVacant() const @@ -3643,7 +3668,7 @@ void oRunner::setBib(const wstring &bib, int bibNumerical, bool updateStartNo, b void oEvent::analyseDNS(vector &unknown_dns, vector &known_dns, - vector &known, vector &unknown) + vector &known, vector &unknown, bool &hasSetDNS) { autoSynchronizeLists(true); @@ -3654,8 +3679,11 @@ void oEvent::analyseDNS(vector &unknown_dns, vector &known_dns if (!it->isRemoved() && !it->needNoCard()) { if (it->getStatus() == StatusUnknown) stUnknown.push_back(&*it); - else if (it->getStatus() == StatusDNS) + else if (it->getStatus() == StatusDNS) { stDNS.push_back(&*it); + if (it->hasFlag(oAbstractRunner::FlagAutoDNS)) + hasSetDNS = true; + } } } @@ -3827,7 +3855,7 @@ void oRunner::printSplits(gdioutput &gdi) const { wstring statInfo = lang.tl("Status: ") + getStatusS() + lang.tl(", Tid: ") + getRunningTimeS(); if (withSpeed && pc && pc->getLength() > 0) { int kmt = (getRunningTime() * 1000) / pc->getLength(); - statInfo += L" (" + formatTimeW(kmt) + lang.tl(" min/km") + L")"; + statInfo += L" (" + formatTime(kmt) + lang.tl(" min/km") + L")"; } if (pc && withSpeed) { if (pc->legLengths.empty() || *max_element(pc->legLengths.begin(), pc->legLengths.end()) <= 0) @@ -3910,14 +3938,14 @@ void oRunner::printSplits(gdioutput &gdi) const { int st = Card->getSplitTime(getStartTime(), &*it); if (st>0) - gdi.addStringUT(cy, cx + c2, fontSmall|textRight, formatTimeW(st)); + gdi.addStringUT(cy, cx + c2, fontSmall|textRight, formatTime(st)); gdi.addStringUT(cy, cx+c3, fontSmall, it->getTime()); int pt = it->getAdjustedTime(); st = getStartTime(); if (st>0 && pt>0 && pt>st) { - wstring punchTime = formatTimeW(pt-st); + wstring punchTime = formatTime(pt-st); gdi.addStringUT(cy, cx+c4, fontSmall|textRight, punchTime); } @@ -3944,8 +3972,8 @@ void oRunner::printSplits(gdioutput &gdi) const { gdi.addString("", cy, cx, fontSmall, "Mål"); sp = getSplitTime(splitTimes.size(), false); if (sp>0) { - gdi.addStringUT(cy, cx+c2, fontSmall|textRight, formatTimeW(sp)); - punchTime = formatTimeW(getRunningTime()); + gdi.addStringUT(cy, cx+c2, fontSmall|textRight, formatTime(sp)); + punchTime = formatTime(getRunningTime()); } gdi.addStringUT(cy, cx+c3, fontSmall, oe->getAbsTime(it->Time + adjust)); any = true; @@ -3995,7 +4023,7 @@ void oRunner::printSplits(gdioutput &gdi) const { sp = getSplitTime(controlLegIndex, false); if (sp>0) { punchTime = getPunchTimeS(controlLegIndex, false); - gdi.addStringUT(cy, cx+c2, fontSmall|textRight, formatTimeW(sp)); + gdi.addStringUT(cy, cx+c2, fontSmall|textRight, formatTime(sp)); } } else { @@ -4022,7 +4050,7 @@ void oRunner::printSplits(gdioutput &gdi) const { int length = pc->legLengths[controlLegIndex]; if (length > 0) { int tempo=(sp*1000)/length; - gdi.addStringUT(cy, cx+c5, fontSmall|textRight, formatTimeW(tempo)); + gdi.addStringUT(cy, cx+c5, fontSmall|textRight, formatTime(tempo)); } } @@ -4056,7 +4084,7 @@ void oRunner::printSplits(gdioutput &gdi) const { for (int k = pc->useFirstAsStart() ? 1 : 0; k < last; k++) { int missed = getMissedTime(k); if (missed>0) { - misses.push_back(pc->getControlOrdinal(k) + L"/" + formatTimeW(missed)); + misses.push_back(pc->getControlOrdinal(k) + L"/" + formatTime(missed)); } } if (misses.size()==0) { @@ -4728,14 +4756,14 @@ wstring oRunner::getMissedTimeS() const if (tMissedTime[k]>0) t += tMissedTime[k]; - return getTimeMSW(t); + return getTimeMS(t); } wstring oRunner::getMissedTimeS(int ctrlNo) const { int t = getMissedTime(ctrlNo); if (t>0) - return getTimeMSW(t); + return getTimeMS(t); else return L""; } @@ -4981,7 +5009,7 @@ void oAbstractRunner::setInputTime(const wstring &time) { wstring oAbstractRunner::getInputTimeS() const { if (inputTime > 0) - return formatTimeW(inputTime); + return formatTime(inputTime); else return makeDash(L"-"); } @@ -5352,7 +5380,7 @@ const wstring &oAbstractRunner::TempResult::getPrintPlaceS(bool withDot) const { } const wstring &oAbstractRunner::TempResult::getRunningTimeS(int inputTime) const { - return formatTimeW(getRunningTime() + inputTime); + return formatTime(getRunningTime() + inputTime); } const wstring &oAbstractRunner::TempResult::getFinishTimeS(const oEvent *oe) const { @@ -5368,7 +5396,7 @@ const wstring &oAbstractRunner::TempResult::getStartTimeS(const oEvent *oe) cons const wstring &oAbstractRunner::TempResult::getOutputTime(int ix) const { int t = size_t(ix) < outputTimes.size() ? outputTimes[ix] : 0; - return formatTimeW(t); + return formatTime(t); } int oAbstractRunner::TempResult::getOutputNumber(int ix) const { diff --git a/code/oRunner.h b/code/oRunner.h index 5b9d5a0..83df501 100644 --- a/code/oRunner.h +++ b/code/oRunner.h @@ -174,6 +174,7 @@ public: FlagFeeSpecified = 8, FlagUpdateClass = 16, FlagUpdateName = 32, + FlagAutoDNS = 64, }; bool hasFlag(TransferFlags flag) const; @@ -480,7 +481,7 @@ protected: class RaceIdFormatter : public oDataDefiner { public: - const wstring &formatData(oBase *obj) const; + const wstring &formatData(const oBase *obj) const; wstring setData(oBase *obj, const wstring &input) const; int addTableColumn(Table *table, const string &description, int minWidth) const; }; diff --git a/code/oTeam.cpp b/code/oTeam.cpp index 92eed23..33920b6 100644 --- a/code/oTeam.cpp +++ b/code/oTeam.cpp @@ -574,7 +574,7 @@ wstring oTeam::getLegRunningTimeS(int leg, bool multidayTotal) const leg = Runners.size()-1; int rt=getLegRunningTime(leg, multidayTotal); - const wstring &bf = formatTimeW(rt); + const wstring &bf = formatTime(rt); if (rt>0) { if ((unsigned(leg)getStartTime()==Class->getRestartTime(leg)) || getNumShortening(leg)>0) @@ -780,20 +780,21 @@ bool oTeam::isRunnerUsed(int Id) const return false; } -void oTeam::setTeamNoStart(bool dns) +void oTeam::setTeamNoStart(bool dns, RunnerStatus dnsStatus) { if (dns) { - setStatus(StatusDNS, true, false); + assert(dnsStatus == StatusCANCEL || dnsStatus == StatusDNS); + setStatus(dnsStatus, true, false); for(unsigned i=0;igetStatus()==StatusUnknown) { - Runners[i]->setStatus(StatusDNS, true, false); + Runners[i]->setStatus(dnsStatus, true, false); } } } else { setStatus(StatusUnknown, true, false); for(unsigned i=0;igetStatus()==StatusDNS) { + if (Runners[i] && (Runners[i]->getStatus()==StatusDNS || Runners[i]->getStatus() == StatusCANCEL)) { Runners[i]->setStatus(StatusUnknown, true, false); } } @@ -1090,7 +1091,6 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { else {//The else below should only be run by mistake (for an incomplete team) Runners[i]->setStartTime(Class->getRestartTime(i), false, setTmpOnly); Runners[i]->tUseStartPunch=false; - //Runners[i]->setStatus(StatusDNS); } } break; @@ -1546,6 +1546,9 @@ wstring oTeam::getLegStartTimeCompact(int leg) const } void oTeam::setBib(const wstring &bib, int bibnumerical, bool updateStartNo, bool setTmpOnly) { + if (updateStartNo) + updateStartNo = !Class || !Class->lockedForking(); + if (setTmpOnly) { tmpStore.bib = bib; if (updateStartNo) @@ -1745,8 +1748,10 @@ void oEvent::getTeams(int classId, vector &t, bool sort) { wstring oTeam::getEntryDate(bool dummy) const { oDataConstInterface dci = getDCI(); int date = dci.getInt("EntryDate"); - if (date == 0) { - (const_cast(this)->getDI()).setDate("EntryDate", getLocalDateW()); + if (date == 0 && !isVacant()) { + auto di = (const_cast(this)->getDI()); + di.setDate("EntryDate", getLocalDate()); + di.setInt("EntryTime", getLocalAbsTime()); } return dci.getDate("EntryDate"); } @@ -1968,14 +1973,14 @@ bool oTeam::inputData(int id, const wstring &input, case TID_STATUS: { RunnerStatus sIn = RunnerStatus(inputId); - if (sIn != StatusDNS) { - if (getStatus() == StatusDNS && sIn == StatusUnknown) - setTeamNoStart(false); + if (sIn != StatusDNS && sIn != StatusCANCEL) { + if ((getStatus() == StatusDNS || getStatus() == StatusCANCEL) && sIn == StatusUnknown) + setTeamNoStart(false, StatusUnknown); else setStatus(sIn, true, false); } - else if (getStatus() != StatusDNS) - setTeamNoStart(true); + else if (getStatus() != sIn) + setTeamNoStart(true, sIn); apply(true, 0, false); RunnerStatus sOut = getStatus(); diff --git a/code/oTeam.h b/code/oTeam.h index 32e6781..408e9a0 100644 --- a/code/oTeam.h +++ b/code/oTeam.h @@ -139,7 +139,7 @@ public: void prepareRemove(); bool skip() const {return isRemoved();} - void setTeamNoStart(bool dns); + void setTeamNoStart(bool dns, RunnerStatus dnsStatus); // Set DNS or CANCEL // If apply is triggered by a runner, don't go further than that runner. bool apply(bool sync, pRunner source, bool setTmpOnly); diff --git a/code/oTeamEvent.cpp b/code/oTeamEvent.cpp index 1f93ccf..779b47b 100644 --- a/code/oTeamEvent.cpp +++ b/code/oTeamEvent.cpp @@ -592,9 +592,9 @@ void oEvent::setupRelay(oClass &cls, PredefinedTypes type, int nleg, const wstri cls.setLegType(1, LTSum); cls.setStartType(1, STHunting, false); int t = convertAbsoluteTimeHMS(start, ZeroTime)+3600; - cls.setStartData(1, formatTimeHMSW(t)); - cls.setRestartTime(1, formatTimeHMSW(t+1800)); - cls.setRopeTime(1, formatTimeHMSW(t+1800)); + cls.setStartData(1, formatTimeHMS(t)); + cls.setRestartTime(1, formatTimeHMS(t+1800)); + cls.setRopeTime(1, formatTimeHMS(t+1800)); cls.setLegRunner(1, 0); cls.setCoursePool(false); break; @@ -733,7 +733,7 @@ void oTeam::checkClassesWithReferences(oEvent &oe, std::set &clsWithRef) { ++pairedUnpairedPerClass[r[k]->getClassId()].second; } - for (auto it : pairedUnpairedPerClass) { + for (auto &it : pairedUnpairedPerClass) { if (it.second.first > it.second.second) clsWithRef.insert(it.first); } diff --git a/code/onlineinput.cpp b/code/onlineinput.cpp index 2aacc40..8f1e959 100644 --- a/code/onlineinput.cpp +++ b/code/onlineinput.cpp @@ -302,7 +302,7 @@ void OnlineInput::processPunches(oEvent &oe, const xmlList &punches) { int card = punches[k].getObjectInt("card"); int time = punches[k].getObjectInt("time") / 10; - time = oe.getRelativeTime(formatTimeHMSW(time)); + time = oe.getRelativeTime(formatTimeHMS(time)); if (startno.length() > 0) r = oe.getRunnerByBibOrStartNo(startno, false); diff --git a/code/onlineresults.cpp b/code/onlineresults.cpp index 5ca1b1f..97e25ea 100644 --- a/code/onlineresults.cpp +++ b/code/onlineresults.cpp @@ -54,6 +54,12 @@ static int OnlineCB(gdioutput *gdi, int type, void *data) { return ores.processButton(*gdi, bu); } case GUI_LISTBOX:{ + ListBoxInfo lbi = *static_cast(data); + if (lbi.id == "Format") { + if (gdi->hasField("IncludeTotal")) { + gdi->setInputStatus("IncludeTotal", lbi.data == 1); + } + } } } return 0; @@ -113,13 +119,19 @@ void OnlineResults::settings(gdioutput &gdi, oEvent &oe, bool created) { // gdi.dropLine(); // gdi.addInput("Interval", time, 10, 0, "Uppdateringsintervall (sekunder):"); - gdi.addSelection("Format", 200, 200, 0, L"Exportformat:"); + gdi.addSelection("Format", 200, 200, OnlineCB, L"Exportformat:"); gdi.addItem("Format", L"MeOS Online Protocol XML", 1); gdi.addItem("Format", L"IOF XML 2.0.3", 2); gdi.addItem("Format", L"IOF XML 3.0", 3); gdi.selectItemByData("Format", dataType); gdi.addCheckbox("Zip", "Packa stora filer (zip)", 0, zipFile); + if (oe.hasPrevStage()) { + gdi.addCheckbox("IncludeTotal", "Inkludera resultat från tidigare etapper", 0, zipFile); + InfoCompetition &ic = getInfoServer(); + gdi.check("IncludeTotal", ic.includeTotalResults()); + gdi.setInputStatus("IncludeTotal", dataType == 1); + } int cx = gdi.getCX(); gdi.fillRight(); @@ -239,10 +251,13 @@ void OnlineResults::save(oEvent &oe, gdioutput &gdi) { prefix = gdi.getText("Prefix"); exportScript = gdi.getText("ExportScript"); zipFile = gdi.isChecked("Zip"); + bool includeTotal = gdi.hasField("IncludeTotal") && gdi.isChecked("IncludeTotal"); ListBoxInfo lbi; gdi.getSelectedItem("Format", lbi); dataType = lbi.data; + if (dataType == 1) + getInfoServer().includeTotalResults(includeTotal); gdi.getSelection("Classes", classes); if (sendToFile) { @@ -334,7 +349,7 @@ void OnlineResults::process(gdioutput &gdi, oEvent *oe, AutoSyncType ast) { if (!sendToFile && !sendToURL) return; - ProgressWindow pwMain((sendToURL && ast == SyncNone) ? gdi.getHWND() : 0); + ProgressWindow pwMain((sendToURL && ast == SyncNone) ? gdi.getHWNDTarget() : 0); pwMain.init(); wstring t; @@ -342,7 +357,7 @@ void OnlineResults::process(gdioutput &gdi, oEvent *oe, AutoSyncType ast) { InfoCompetition &ic = getInfoServer(); xmlbuffer xmlbuff; if (dataType == 1) { - if (ic.synchronize(*oe, classes, controls)) { + if (ic.synchronize(*oe, false, classes, controls)) { lastSync = tick; // If error, avoid to quick retry ic.getDiffXML(xmlbuff); } @@ -508,7 +523,7 @@ void OnlineResults::formatError(gdioutput &gdi) { gdi.setRestorePoint("ServerError"); gdi.dropLine(); gdi.fillDown(); - gdi.addString("", boldText, "Server response X:#" + getLocalTime()).setColor(colorRed); + gdi.addString("", boldText, L"Server response X:#" + getLocalTime()).setColor(colorRed); for (size_t k = 0; k < errorLines.size(); k++) gdi.addStringUT(0, errorLines[k]); gdi.scrollToBottom(); diff --git a/code/pdfwriter.cpp b/code/pdfwriter.cpp index 3860a5e..744c020 100644 --- a/code/pdfwriter.cpp +++ b/code/pdfwriter.cpp @@ -147,6 +147,7 @@ void pdfwriter::generatePDF(const gdioutput &gdi, const wstring &pageTitleW, const wstring &authorW, const list &tl) { + checkWriteAccess(file); string pageTitle = gdi.narrow(pageTitleW); // XXX WCS string author = gdi.narrow(authorW); diff --git a/code/printer.cpp b/code/printer.cpp index 92e1570..806d952 100644 --- a/code/printer.cpp +++ b/code/printer.cpp @@ -623,6 +623,11 @@ struct PrintItemInfo { const TextInfo *ti = dynamic_cast(obj); return ti && ti->format == pageNewPage; } + + bool isNoPrint() const { + const TextInfo *ti = dynamic_cast(obj); + return ti && gdioutput::skipTextRender(ti->format); + } }; void PageInfo::renderPages(const list &tl, @@ -645,7 +650,7 @@ void PageInfo::renderPages(const list &tl, const TextInfo &text = *it; if (text.format == 10) continue; - if (text.format != pageNewPage && text.format != pagePageInfo) { + if (!text.isFormatInfo()) { if (currentYP > text.yp) { needSort = true; } @@ -740,6 +745,8 @@ void PageInfo::renderPages(const list &tl, if (k + 1 < indexedTL.size() && tlp->yp != indexedTL[k+1].yp) { size_t j = k + 1; + while (j + 1 < indexedTL.size() && indexedTL[j].isNoPrint() && !indexedTL[j].isNewPage()) + j++; // Required new page if (indexedTL[j].isNewPage()) { @@ -819,16 +826,16 @@ wstring PageInfo::pageInfo(const RenderedPage &page) const { wchar_t bf[256]; if (nPagesTotal > 1) { if (!page.info.empty()) - swprintf_s(bf, L"MeOS %s, %s, (%d/%d)", getLocalTimeW().c_str(), - page.info.c_str(), page.nPage, nPagesTotal);//WCS + swprintf_s(bf, L"MeOS %s, %s, (%d/%d)", getLocalTime().c_str(), + page.info.c_str(), page.nPage, nPagesTotal); else - swprintf_s(bf, L"MeOS %s, (%d/%d)", getLocalTimeW().c_str(), page.nPage, nPagesTotal); + swprintf_s(bf, L"MeOS %s, (%d/%d)", getLocalTime().c_str(), page.nPage, nPagesTotal); } else { if (!page.info.empty()) - swprintf_s(bf, L"MeOS %s, %s", getLocalTimeW().c_str(), page.info.c_str()); + swprintf_s(bf, L"MeOS %s, %s", getLocalTime().c_str(), page.info.c_str()); else - swprintf_s(bf, L"MeOS %s", getLocalTimeW().c_str()); + swprintf_s(bf, L"MeOS %s", getLocalTime().c_str()); } return bf; } diff --git a/code/restserver.cpp b/code/restserver.cpp new file mode 100644 index 0000000..11c5bb9 --- /dev/null +++ b/code/restserver.cpp @@ -0,0 +1,261 @@ +/************************************************************************ +MeOS - Orienteering Software +Copyright (C) 2009-2017 Melin Software HB + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Melin Software HB - software@melin.nu - www.melin.nu +Eksoppsvägen 16, SE-75646 UPPSALA, Sweden + +************************************************************************/ + +#include "stdafx.h" +#include "oEvent.h" +#include "xmlparser.h" +#include + +#include "restbed/restbed" +#include "meosexception.h" +#include "restserver.h" +#include "infoserver.h" +#include + +using namespace restbed; + +vector< shared_ptr > RestServer::startedServers; + + +shared_ptr RestServer::construct() { + shared_ptr obj(new RestServer()); + startedServers.push_back(obj); + return obj; +} + +void RestServer::remove(shared_ptr server) { +// xxx std::remove(startedServers.begin(), startedServers.end(), server); +} + +RestServer::RestServer() : hasAnyRequest(false) { +} + +RestServer::~RestServer() { + stop(); +} + +class MeOSResource : public restbed::Resource { + RestServer *server; + +public: + MeOSResource(RestServer *server) : server(server) {} + + ~MeOSResource() { + } + + RestServer &getServer() const { return *server; } +}; + +static void method_handler(const shared_ptr< restbed::Session > session) { + RestServer &server = dynamic_cast(*session->get_resource()).getServer(); + server.handleRequest(session); +} + +void RestServer::handleRequest(const shared_ptr &session) { + const auto request = session->get_request(); + size_t content_length = request->get_header("Content-Length", 0); + + chrono::time_point start, end; + start = chrono::system_clock::now(); + + auto param = request->get_query_parameters(); + auto answer = RestServer::addRequest(param); + { + unique_lock mlock(lock); + if (!waitForCompletion.wait_for(mlock, 10s, [answer] {return answer->isCompleted(); })) { + answer->answer = "Error (MeOS): Internal timeout"; + } + + end = chrono::system_clock::now(); + chrono::duration elapsed_seconds = end - start; + responseTimes.push_back(int(1000 * elapsed_seconds.count())); + } + // lock.unlock(); + + session->fetch(content_length, [request, answer](const shared_ptr< Session > session, const Bytes & body) + { + //fprintf(stdout, "%.*s\n", (int)body.size(), body.data()); + /*while (!answer->state) { + std::this_thread::yield(); + }*/ + session->close(restbed::OK, answer->answer, { { "Content-Length", itos(answer->answer.length()) },{ "Connection", "close" } }); + }); +} + +void RestServer::startThread(int port) { + auto settings = make_shared(); + settings->set_port(port); + auto resource = make_shared(this); + resource->set_path("/meos"); + + resource->set_method_handler("GET", method_handler); + restService->publish(resource); + + restService->start(settings); +} + +void RestServer::startService(int port) { + if (service) + throw meosException("Server started"); + + restService.reset(new restbed::Service()); + service = make_shared(&RestServer::startThread, this, port); +} + +void RestServer::stop() { + if (restService) + restService->stop(); + + if (service && service->joinable()) + service->join(); + + restService.reset(); + service.reset(); +} + +void RestServer::computeRequested(oEvent &ref) { + for (auto &server : startedServers) { + server->compute(ref); + } +} + +void RestServer::compute(oEvent &ref) { + auto rq = getRequest(); + if (!rq) + return; + if (rq->parameters.empty()) { + rq->answer = "" + "MeOS Information Service" + "" + "" + "

MeOS

" + "" + ""; + } + else if (rq->parameters.count("get") > 0) { + string what = rq->parameters.find("get")->second; + getData(ref, what, rq->parameters, rq->answer); + } + else { + rq->answer = "Error (MeOS): Unknown request"; + } + + { + lock_guard lg(lock); + rq->state = true; + } + waitForCompletion.notify_all(); +} + +void RestServer::getData(oEvent &oe, const string &what, const multimap ¶m, string &answer) { + xmlbuffer out; + out.setComplete(true); + + if (what == "competition") { + InfoCompetition cmp(0); + cmp.synchronize(oe); + cmp.serialize(out, false); + } + else if (what == "class") { + vector cls; + oe.getClasses(cls, true); + + for (auto c : cls) { + InfoClass iCls(c->getId()); + set ctrl; + iCls.synchronize(*c, ctrl); + iCls.serialize(out, false); + } + } + else if (what == "competitor") { + vector r; + set selection; + if (param.count("id") > 0) + getSelection(param.find("id")->second, selection); + + oe.getRunners(selection.size() == 1 ? *selection.begin() : 0, -1, r, true); + + for (auto c : r) { + InfoCompetitor iR(c->getId()); + iR.synchronize(false, *c); + iR.serialize(out, false); + } + } + if (out.size() > 0) { + xmlparser mem; + mem.openMemoryOutput(false); + mem.startTag("MOPComplete", "xmlns", "http://www.melin.nu/mop"); + out.commit(mem, 100000); + mem.endTag(); + mem.getMemoryOutput(answer); + } + else { + answer = "Error (MeOS): Unknown command '" + what + "'"; + } +} + +void RestServer::getSelection(const string ¶m, set &sel) { + vector sw; + split(param, ";,", sw); + for (auto &s : sw) { + int id = atoi(s.c_str()); + if (id > 0) + sel.insert(id); + } +} + +shared_ptr RestServer::getRequest() { + shared_ptr res; + if (hasAnyRequest) { + lock_guard lg(lock); + if (!requests.empty()) { + res = requests.front(); + requests.pop_front(); + } + if (requests.empty()) + hasAnyRequest = false; + } + return res; +} + +shared_ptr RestServer::addRequest(multimap ¶m) { + auto rq = make_shared(); + rq->parameters.swap(param); + lock_guard lg(lock); + requests.push_back(rq); + hasAnyRequest = true; + return rq; +} + +void RestServer::getStatistics(Statistics &s) { + lock_guard lg(lock); + s.numRequests = responseTimes.size(); + s.maxResponseTime = 0; + s.averageResponseTime = 0; + for (int t : responseTimes) { + s.maxResponseTime = max(s.maxResponseTime, t); + s.averageResponseTime += t; + } + if (s.numRequests > 0) { + s.averageResponseTime /= s.numRequests; + } +} diff --git a/code/restserver.h b/code/restserver.h new file mode 100644 index 0000000..2a10052 --- /dev/null +++ b/code/restserver.h @@ -0,0 +1,99 @@ +#pragma once + +/************************************************************************ +MeOS - Orienteering Software +Copyright (C) 2009-2017 Melin Software HB + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Melin Software HB - software@melin.nu - www.melin.nu +Eksoppsvägen 16, SE-75646 UPPSALA, Sweden + +************************************************************************/ + +/** Class for providing a MeOS REST service */ + +#include +#include +#include +#include +#include +#include + +namespace restbed { + class Service; + class Session; +}; + +class RestServer { +private: + struct EventRequest { + EventRequest() : state(false) {} + multimap parameters; + string answer; + atomic_bool state; //false - asked, true - answerd + + bool isCompleted() { + return state; + } + }; + + mutex lock; + atomic_bool hasAnyRequest; + shared_ptr service; + shared_ptr restService; + condition_variable waitForCompletion; + + deque> requests; + void getData(oEvent &ref, const string &what, const multimap ¶m, string &answer); + void compute(oEvent &ref); + void startThread(int port); + + static void getSelection(const string ¶m, set &sel); + + void handleRequest(const shared_ptr &session); + friend void method_handler(const shared_ptr< restbed::Session > session); + + shared_ptr addRequest(multimap ¶m); + shared_ptr getRequest(); + + vector responseTimes; + + RestServer(); + static vector< shared_ptr > startedServers; + + RestServer(const RestServer &); + RestServer & operator=(const RestServer &) const; +public: + + ~RestServer(); + + void startService(int port); + void stop(); + + static shared_ptr construct(); + static void remove(shared_ptr server); + + static void computeRequested(oEvent &ref); + + struct Statistics { + int numRequests; + int averageResponseTime; + int maxResponseTime; + }; + + void getStatistics(Statistics &s); +}; + + diff --git a/code/speakermonitor.cpp b/code/speakermonitor.cpp index bbbab6e..11fd840 100644 --- a/code/speakermonitor.cpp +++ b/code/speakermonitor.cpp @@ -380,7 +380,7 @@ void SpeakerMonitor::splitAnalysis(gdioutput &gdi, int xp, int yp, pRunner r) { else first = false; - timeloss += pc->getControlOrdinal(j) + L". " + formatTimeW(delta[j]); + timeloss += pc->getControlOrdinal(j) + L". " + formatTime(delta[j]); } if (timeloss.length() > charlimit || (!timeloss.empty() && !first && j+1 == delta.size())) { gdi.addStringUT(yp, xp, 0, timeloss).setColor(colorDarkRed); @@ -405,7 +405,7 @@ wstring getTimeDesc(int t1, int t2) { else if (tb <= 60) stime = itow(tb) + lang.tl(L" sekunder"); else - stime = formatTimeW(tb); + stime = formatTime(tb); return stime; } @@ -504,7 +504,7 @@ void SpeakerMonitor::getMessage(const oEvent::ResultEvent &res, deltaTime = after - prevAfter; // Positive -> more after. } - wstring timeS = formatTimeW(res.runTime); + wstring timeS = formatTime(res.runTime); wstring detail; const wstring *cname = 0; diff --git a/code/swedish.lng b/code/swedish.lng index e429cef..aaf5424 100644 --- a/code/swedish.lng +++ b/code/swedish.lng @@ -1865,7 +1865,7 @@ Byt till rätt klass (behÃ¥ll eventuell starttid) = Byt till rätt klass (behÃ¥l Byt till vakansplats i rätt klass (om möjligt) = Byt till vakansplats i rätt klass (om möjligt) TillÃ¥t ny klass, behÃ¥ll resultat frÃ¥n annan klass = TillÃ¥t ny klass, behÃ¥ll resultat frÃ¥n annan klass TillÃ¥t ny klass, inget totalresultat = TillÃ¥t ny klass, inget totalresultat -tooltip_explain_status = - = Okänd status\nOK = Godkänt resultat\nEj start = Startade inte\nFelst. = Felstämplat\nUtgÃ¥tt = MÃ¥ltid saknas\nDisk. = Diskvalificerad\nDeltar ej = Springer inte tävlingen +tooltip_explain_status = - = Okänd status\nOK = Godkänt resultat\nEj start = Startade inte\nÃ…terbud = Kommer ej att starta, visas i startlistan.\nFelst. = Felstämplat\nUtgÃ¥tt = MÃ¥ltid saknas\nDisk. = Diskvalificerad\nDeltar ej = Springer inte tävlingen Placering = Placering Resultat frÃ¥n tidigare etapper = Resultat frÃ¥n tidigare etapper Input Results = IngÃ¥ngsresultat @@ -2252,3 +2252,30 @@ Spara fönster- och speakerinställningar pÃ¥ datorn = Spara fönster- och speak ask:loadspeaker = Vill du ladda tidigare sparade klass- och fönsterinställningar? Ã…terskapa = Ã…terskapa Ã…terskapa tidigare sparade fönster- och speakerinställningar = Ã…terskapa tidigare sparade fönster- och speakerinställningar +Inkludera resultat frÃ¥n tidigare etapper = Inkludera resultat frÃ¥n tidigare etapper +Animation = Animation +Bakgrund = Bakgrund +Bakgrundsfärg = Bakgrundsfärg +Fullskärm (rullande) = Fullskärm (rullande) +Fullskärm (sidvis) = Fullskärm (sidvis) +Fönster = Fönster +Fönster (rullande) = Fönster (rullande) +Justera visningsinställningar = Justera visningsinställningar +Marginal = Marginal +Sidor per skärm = Sidor per skärm +Textfärg = Textfärg +Utseende = Utseende +Visningsinställningar för 'X' = Visningsinställningar för 'X' +Visningstid = Visningstid +Visning = Visning +ask:hasVacant = Tävlingen innehÃ¥ller fortfarande vakanser.\n\nVill du ta bort samtliga vakanser innan resultaten exporteras? +warn:missingResult = X deltagare saknar fortfarande resultat, och tas därför inte med.\n\nDu bör använda kvar-i-skogen för att tilldela Ã¥terstÃ¥ende deltagare status . +Ã…terställ till = Ã…terställ till +Ã…terbud[status] = Ã…terbud +LÃ¥s gafflingar = LÃ¥s gafflingar +Markera för att förhindra oavsiktlig ändring av gafflingsnycklar = Markera för att förhindra oavsiktlig ändring av gafflingsnycklar +TillÃ¥t gafflingsändringar = TillÃ¥t gafflingsändringar +ask:updatetimes = Vill du behÃ¥lla nuvarande starttider, om möjligt? Svara nej för att förskjuta tävlingen. +X har en tid (Y) som inte är kompatibel med förändringen = X har en tid (Y) som inte är kompatibel med förändringen +warn:latestarttime = Att använda starttider mer än X timmar efter nolltiden rekommenderas inte eftersom äldre SI brickor endast har en 12-timmarsklocka.\n\nVill du ändÃ¥ använda starttiden? +Anm. tid = Anm. tid diff --git a/code/testmeos.cpp b/code/testmeos.cpp index 8721989..19bb97e 100644 --- a/code/testmeos.cpp +++ b/code/testmeos.cpp @@ -172,7 +172,7 @@ void TestMeOS::runProtected(bool protect) const { gdi_main->isTestMode = false; subWindows.clear(); //oe_main->setProperty("PayModes", pmOrig); - message = ex.what(); + message = gdi_main->widen(ex.what()); if (!protect) throw; } @@ -184,7 +184,7 @@ void TestMeOS::runProtected(bool protect) const { gdi_main->isTestMode = false; subWindows.clear(); //oe_main->setProperty("PayModes", pmOrig); - message = "Unknown Exception"; + message = L"Unknown Exception"; cleanup(); if (!protect) throw; @@ -274,7 +274,7 @@ string TestMeOS::selectString(const char *id, const char *data) const { string TestMeOS::select(const char *id, size_t data) const { string res = gdi_main->dbSelect(id, data); - mainMessageLoop(0, 50); + mainMessageLoop(0, 100); return res; } @@ -323,6 +323,13 @@ void TestMeOS::assertEquals(const string &expected, throw meosAssertionFailure("Expected " + expected + " but got " + value); } +void TestMeOS::assertEquals(const wstring &expected, + const wstring &value) const { + if (expected != value) + throw meosAssertionFailure(L"Expected " + expected + L" but got " + value); +} + + void TestMeOS::assertEquals(int expected, int value) const { assertEquals(itos(expected), itos(value)); } @@ -344,6 +351,13 @@ void TestMeOS::assertEquals(const char *message, assertEquals(string(message), string(expected), value); } +void TestMeOS::assertEquals(const wstring &message, + const wstring &expected, + const wstring &value) const { + if (expected != value) + throw meosAssertionFailure(message + L": Expected " + expected + L" but got " + value); +} + void TestMeOS::checkString(const char *str, int count) const { int c = gdi_main->dbGetStringCount(str, false); assertEquals("String " + string(str) + " not found", itos(count), itos(c)); diff --git a/code/testmeos.h b/code/testmeos.h index 2ef1826..5c7c311 100644 --- a/code/testmeos.h +++ b/code/testmeos.h @@ -41,9 +41,10 @@ enum TestStatus { }; struct meosAssertionFailure { - meosAssertionFailure() {message = "MeOS assertion failure";}; - meosAssertionFailure(const string &err) : message(err) {} - string message; + meosAssertionFailure() {message = L"MeOS assertion failure";}; + meosAssertionFailure(const string &err) : message(err.begin(), err.end()) {} + meosAssertionFailure(const wstring &err) : message(err) {} + wstring message; }; class TestMeOS : public SubCommand { @@ -57,7 +58,7 @@ private: mutable vector tmpFiles; mutable TestStatus status; - mutable string message; + mutable wstring message; int testId; int *testIdMain; // Pointer to main test id @@ -81,6 +82,10 @@ protected: void assertEquals(const char *message, const char *expected, const string &value) const; void assertEquals(const string &message, const string &expected, const string &value) const; + void assertEquals(const wstring &expected, const wstring &value) const; + void assertEquals(const wstring &message, const wstring &expected, const wstring &value) const; + + void assertTrue(const char *message, bool condition) const; int getResultModuleIndex(const char *tag) const; diff --git a/code/toolbar.cpp b/code/toolbar.cpp index 5e9ea64..4a30980 100644 --- a/code/toolbar.cpp +++ b/code/toolbar.cpp @@ -158,7 +158,7 @@ void Toolbar::createToolbar(const string &id, const wstring &title) } wstring t = lang.tl(title); - HWND hParent = gdi.getHWND(); + HWND hParent = gdi.getHWNDTarget(); RECT rc; GetWindowRect(hParent, &rc); if (hwndFloater == 0) { @@ -281,7 +281,7 @@ LRESULT CALLBACK ToolProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) Toolbar *tb = (Toolbar *)GetWindowLongPtr(hWnd, GWL_USERDATA); if (tb) { //DefWindowProc(tb->gdi.getHWND(), message, wParam, lParam); - SendMessage(tb->gdi.getMain(), message, wParam, lParam); + SendMessage(tb->gdi.getHWNDMain(), message, wParam, lParam); } return DefWindowProc(hWnd, message, wParam, lParam); } diff --git a/code/xmlparser.cpp b/code/xmlparser.cpp index 01f31a2..27a4817 100644 --- a/code/xmlparser.cpp +++ b/code/xmlparser.cpp @@ -392,7 +392,7 @@ void xmlparser::openOutputT(const wchar_t *file, bool useCutMode, const string & toString = false; cutMode = useCutMode; foutFile.open(file); - + checkWriteAccess(file); tagStackPointer=0; if (foutFile.bad()) diff --git a/code/xmlparser.h b/code/xmlparser.h index ffda789..ebe6d02 100644 --- a/code/xmlparser.h +++ b/code/xmlparser.h @@ -220,6 +220,18 @@ public: return 0; } + bool got(const char *pname) const { + xmlobject x(getObject(pname)); + if (x) + return true; + else { + xmlattrib xa(getAttrib(pname)); + if (xa) + return true; + } + return false; + } + bool getObjectBool(const char *pname) const; string &getObjectString(const char *pname, string &out) const;