/************************************************************************ 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 #include "resource.h" #include #include #include #include "oEvent.h" #include "xmlparser.h" #include "gdioutput.h" #include "csvparser.h" #include "SportIdent.h" #include "meos_util.h" #include "oListInfo.h" #include "TabClass.h" #include "ClassConfigInfo.h" #include "meosException.h" #include "gdifonts.h" #include "oEventDraw.h" #include "MeOSFeatures.h" extern pEvent gEvent; const char *visualDrawWindow = "visualdraw"; TabClass::TabClass(oEvent *poe):TabBase(poe) { handleCloseWindow.tabClass = this; clearCompetitionData(); } void TabClass::clearCompetitionData() { pSettings.clear(); pSavedDepth = 3600; pFirstRestart = 3600; pTimeScaling = 1.0; pInterval = 120; currentStage = -1; EditChanged = false; ClassId=0; tableMode = false; showForkingGuide = false; storedNStage = L"3"; storedStart = L""; storedPredefined = oEvent::PredefinedTypes(-1); cInfoCache.clear(); hasWarnedDirect = false; hasWarnedStartTime = false; lastSeedMethod = -1; lastSeedPreventClubNb = true; lastSeedReverse = false; lastSeedGroups = L"1"; lastPairSize = 1; lastFirstStart = L""; lastInterval = L"2:00"; lastNumVac = L"0"; lastHandleBibs = false; lastScaleFactor = L"1.0"; lastMaxAfter = L"60:00"; gdioutput *gdi = getExtraWindow(visualDrawWindow, false); if (gdi) { gdi->closeWindow(); } } TabClass::~TabClass(void) { } bool ClassInfoSortStart(ClassInfo &ci1, ClassInfo &ci2) { return ci1.firstStart>ci2.firstStart; } void TabClass::HandleCloseWindow::handle(gdioutput &gdi, BaseInfo &info, GuiEventType type) { if (type == GUI_EVENT) { EventInfo &ei = dynamic_cast(info); if (ei.id == "CloseWindow") { tabClass->closeWindow(gdi); } } } void TabClass::closeWindow(gdioutput &gdi) { } int ClassesCB(gdioutput *gdi, int type, void *data) { TabClass &tc = dynamic_cast(*gdi->getTabs().get(TClassTab)); return tc.classCB(*gdi, type, data); } int MultiCB(gdioutput *gdi, int type, void *data) { TabClass &tc = dynamic_cast(*gdi->getTabs().get(TClassTab)); return tc.multiCB(*gdi, type, data); } int DrawClassesCB(gdioutput *gdi, int type, void *data) { TabClass &tc = dynamic_cast(*gdi->getTabs().get(TClassTab)); if (type == GUI_LISTBOX) { tc.enableLoadSettings(*gdi); } else if (type == GUI_INPUT || type == GUI_INPUTCHANGE) { InputInfo &ii = *(InputInfo *)data; if (ii.id.length() > 1) { int id = atoi(ii.id.substr(1).c_str()); if (id > 0 && ii.changed()) { string key = "C" + itos(id); TextInfo &ti = dynamic_cast(gdi->getBaseInfo(key.c_str())); ti.setColor(colorRed); gdi->enableInput("DrawAdjust"); gdi->refreshFast(); } } } return 0; } int TabClass::multiCB(gdioutput &gdi, int type, void *data) { if (type==GUI_BUTTON) { ButtonInfo bi=*(ButtonInfo *)data; if (bi.id=="ChangeLeg") { gdi.dropLine(); legSetup(gdi); gdi.refresh(); } else if (bi.id == "CommonStart") { gdi.setInputStatus("CommonStartTime", gdi.isChecked(bi.id)); } else if (bi.id == "CoursePool") { 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)) return false; save(gdi, true); EditChanged=true; defineForking(gdi, true); return true; } else if (bi.id == "ApplyForking") { showForkingGuide = false; pClass pc = oe->getClass(ClassId); vector allR; oe->getRunners(ClassId, 0, allR, false); bool doClear = false; for (size_t k = 0; k < allR.size(); k++) { if (allR[k]->getCourseId() != 0) { if (!doClear) { if (gdi.ask(L"Vill du nollställa alla manuellt tilldelade banor?")) doClear = true; else break; } if (doClear) allR[k]->setCourseId(0); } } pair res = pc->autoForking(forkingSetup); gdi.alert("Created X distinct forkings using Y courses.#" + itos(res.first) + "#" + itos(res.second)); loadPage(gdi); EditChanged=true; } else if (bi.id == "AssignCourses") { set selectedCourses, selectedLegs; gdi.getSelection("AllCourses", selectedCourses); gdi.getSelection("AllStages", selectedLegs); for (set::iterator it = selectedLegs.begin(); it != selectedLegs.end(); ++it) { int leg = *it; forkingSetup[leg].clear(); forkingSetup[leg].insert(forkingSetup[leg].begin(), selectedCourses.begin(), selectedCourses.end()); } bool empty = true; for (size_t k = 0; k < forkingSetup.size(); k++) { if (forkingSetup[k].empty()) { gdi.setText("leg"+ itos(k), lang.tl("Leg X: Do not modify.#" + itos(k+1))); } else { empty = false; wstring crs; for (size_t j = 0; j < forkingSetup[k].size(); j++) { if (j>0) crs += L", "; crs += oe->getCourse(forkingSetup[k][j])->getName(); if (j > 3) { crs += L"..."; break; } } gdi.setText("leg"+ itos(k), lang.tl(L"Leg X: Use Y.#" + itow(k+1) + L"#" + crs)); } } gdi.setInputStatus("ApplyForking", !empty); gdi.setSelection("AllCourses", set()); gdi.setSelection("AllStages", set()); gdi.refresh(); } else if (bi.id == "ClearCourses") { gdi.setSelection("AllCourses", set()); gdi.setSelection("AllStages", set()); gdi.disableInput("AssignCourses"); } else if (bi.id == "ShowForking") { if (!checkClassSelected(gdi)) return false; pClass pc=oe->getClass(ClassId); if (!pc) throw std::exception("Klassen finns ej."); vector< vector > forks; set< pair > unfairLegs; vector< vector > legOrder; pc->checkForking(legOrder, forks, unfairLegs); gdioutput *gdi_new = getExtraWindow("fork", true); wstring title = lang.tl(L"Forkings for X#" + pc->getName()); if (!gdi_new) gdi_new = createExtraWindow("fork", title, gdi.scaleLength(1024) ); else gdi_new->setWindowTitle(title); gdi_new->clearPage(false); gdi_new->addString("", fontMediumPlus, "Forkings"); for (size_t k = 0; k < forks.size(); k++) { gdi_new->dropLine(0.7); wstring ver = itow(k+1) + L": "; for (size_t j = 0; j < legOrder[k].size(); j++) { pCourse crs = oe->getCourse(legOrder[k][j]); if (crs) { if (j>0) ver += L", "; ver += crs->getName(); } } gdi_new->addStringUT(1, ver); gdi_new->pushX(); gdi_new->fillRight(); for (size_t j = 0; j < forks[k].size(); j++) { wstring ctrl; if (forks[k][j] > 0) ctrl += itow(forks[k][j]); else { if (j == 0) ctrl += lang.tl("Start"); else if (j + 1 == forks[k].size()) ctrl += lang.tl("Mål"); else ctrl += lang.tl("Växel"); } int next = -100; if (j + 1 < forks[k].size()) { ctrl += L","; next = forks[k][j+1]; } int prev = j>0 ? forks[k][j-1] : -100; bool warn = unfairLegs.count(make_pair(prev, forks[k][j])) != 0;// || //unfairLegs.count(make_pair(forks[k][j], next)) != 0; TextInfo &ti = gdi_new->addStringUT(italicText, ctrl); if (warn) { ti.setColor(colorRed); ti.format = boldText; } gdi.setCX(gdi.getCX() - gdi.scaleLength(4)); } gdi_new->popX(); gdi_new->fillDown(); gdi_new->dropLine(); } if (!unfairLegs.empty()) { gdi_new->dropLine(); gdi_new->addString("", fontMediumPlus, "Unfair control legs"); gdi_new->dropLine(0.5); for (set< pair >::const_iterator p = unfairLegs.begin(); p != unfairLegs.end(); ++p) { wstring f = p->first > 0 ? itow(p->first) : lang.tl("Växel"); wstring s = p->second > 0 ? itow(p->second) : lang.tl("Växel"); gdi_new->addStringUT(0, makeDash(f + L" - " + s)); } } gdi_new->dropLine(); gdi_new->addButton("CloseWindow", "Stäng", ClassesCB); gdi_new->refresh(); } else if (bi.id == "OneCourse") { if (!checkClassSelected(gdi)) return false; pClass pc=oe->getClass(ClassId); if (!pc) throw std::exception("Klassen finns ej."); pc->setNumStages(0); pc->synchronize(); gdi.restore(); gdi.enableInput("MultiCourse", true); gdi.enableInput("Courses"); } else if (bi.id=="SetNStage") { if (!checkClassSelected(gdi)) return false; pClass pc=oe->getClass(ClassId); if (!pc) throw std::exception("Klassen finns ej."); int total, finished, dns; oe->getNumClassRunners(pc->getId(), 0, total, finished, dns); oEvent::PredefinedTypes newType = oEvent::PredefinedTypes(gdi.getSelectedItem("Predefined").first); int nstages = gdi.getTextNo("NStage"); if (finished > 0) { if (gdi.ask(L"warning:has_results") == false) return false; } else if (total>0) { bool ok = false; ClassType ct = pc->getClassType(); if (ct == oClassIndividual) { switch (newType) { case oEvent::PPatrolOptional: case oEvent::PPool: case oEvent::PPoolDrawn: case oEvent::PNoMulti: case oEvent::PForking: ok = true; break; case oEvent::PNoSettings: ok = (nstages == 1); } } if (!ok) { if (gdi.ask(L"warning:has_entries") == false) return false; } } storedPredefined = newType; if (nstages > 0) storedNStage = gdi.getText("NStage"); else { storedNStage = L""; if (newType != oEvent::PNoMulti) nstages = 1; //Fixed by type } if (nstages>0 && nstages<41) { wstring st=gdi.getText("StartTime"); 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 gdi.selectItemByData("Courses", -2); gdi.disableInput("Courses"); oe->setupRelay(*pc, newType, nstages, st); if (gdi.hasField("MAdd")) { gdi.enableInput("MAdd"); gdi.enableInput("MCourses"); gdi.enableInput("MRemove"); } pc->forceShowMultiDialog(true); selectClass(gdi, pc->getId()); } else if (nstages==0){ pc->setNumStages(0); pc->synchronize(); gdi.restore(); gdi.enableInput("MultiCourse", true); gdi.enableInput("Courses"); oe->adjustTeamMultiRunners(pc); } else { gdi.alert("Antalet sträckor måste vara ett heltal mellan 0 och 40."); } } else if (bi.id.substr(0, 7)=="@Course") { int cnr=atoi(bi.id.substr(7).c_str()); selectCourses(gdi, cnr); } else if (bi.id=="MAdd"){ DWORD cid=ClassId; if (!checkClassSelected(gdi)) return false; pClass pc=oe->getClass(cid); if (!pc) return false; if (currentStage>=0){ ListBoxInfo lbi; if (gdi.getSelectedItem("MCourses", lbi)) { int courseid=lbi.data; pc->addStageCourse(currentStage, courseid); pc->fillStageCourses(gdi, currentStage, "StageCourses"); pc->synchronize(); oe->checkOrderIdMultipleCourses(cid); } } EditChanged=true; } else if (bi.id=="MRemove"){ if (!checkClassSelected(gdi)) return false; DWORD cid=ClassId; pClass pc=oe->getClass(cid); if (!pc) return false; if (currentStage>=0){ ListBoxInfo lbi; if (gdi.getSelectedItem("StageCourses", lbi)) { int courseid=lbi.data; pc->removeStageCourse(currentStage, courseid, lbi.index); pc->synchronize(); pc->fillStageCourses(gdi, currentStage, "StageCourses"); } } } EditChanged=true; } else if (type==GUI_LISTBOX) { ListBoxInfo bi=*(ListBoxInfo *)data; if (bi.id.substr(0, 7)=="LegType") { LegTypes lt = LegTypes(bi.data); int i=atoi(bi.id.substr(7).c_str()); pClass pc=oe->getClass(ClassId); if (!pc) return false; if (lt == pc->getLegType(i)) return 0; pc->setLegType(i, lt); char legno[10]; sprintf_s(legno, "%d", i); updateStartData(gdi, pc, i, true, false); gdi.setInputStatus(string("Restart")+legno, !pc->restartIgnored(i), true); gdi.setInputStatus(string("RestartRope")+legno, !pc->restartIgnored(i), true); EditChanged=true; } else if (bi.id == "AllStages") { set t; gdi.getSelection(bi.id, t); gdi.setInputStatus("AssignCourses", !t.empty()); } else if (bi.id.substr(0, 9)=="StartType") { StartTypes st=StartTypes(bi.data); int i=atoi(bi.id.substr(9).c_str()); pClass pc=oe->getClass(ClassId); if (!pc) return false; pc->setStartType(i, st, true); char legno[10]; sprintf_s(legno, "%d", i); updateStartData(gdi, pc, i, true, false); gdi.setInputStatus(string("Restart")+legno, !pc->restartIgnored(i), true); gdi.setInputStatus(string("RestartRope")+legno, !pc->restartIgnored(i), true); EditChanged=true; } else if (bi.id == "Predefined") { bool nleg; bool start; oe->setupRelayInfo(oEvent::PredefinedTypes(bi.data), nleg, start); gdi.setInputStatus("NStage", nleg); gdi.setInputStatus("StartTime", start); wstring nl = gdi.getText("NStage"); if (!nleg && _wtoi(nl.c_str()) != 0) { storedNStage = nl; gdi.setText("NStage", makeDash(L"-")); } else if (nleg && _wtoi(nl.c_str()) == 0) { gdi.setText("NStage", storedNStage); } wstring st = gdi.getText("StartTime"); if (!start && _wtoi(nl.c_str()) != 0) { storedStart = st; gdi.setText("StartTime", makeDash(L"-")); } else if (start && _wtoi(nl.c_str()) == 0) { gdi.setText("StartTime", storedStart); } } else if (bi.id=="Courses") { EditChanged=true; } } else if (type==GUI_INPUTCHANGE){ InputInfo ii=*(InputInfo *)data; EditChanged=true; if (ii.id=="NStage") gdi.enableInput("SetNStage"); //else if (ii.id=="") } return 0; } int TabClass::classCB(gdioutput &gdi, int type, void *data) { if (type==GUI_BUTTON) { ButtonInfo bi=*(ButtonInfo *)data; if (bi.id=="Cancel") { showForkingGuide = false; loadPage(gdi); return 0; } else if (bi.id == "UseAdvanced") { bool checked = gdi.isChecked("UseAdvanced"); oe->setProperty("AdvancedClassSettings", checked); save(gdi, true); PostMessage(gdi.getHWNDTarget(), WM_USER + 2, TClassTab, 0); } else if (bi.id=="SwitchMode") { if (!tableMode) save(gdi, true); tableMode=!tableMode; loadPage(gdi); } else if (bi.id=="Restart") { save(gdi, 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); oe->fillClasses(gdi, "RestartClasses", oEvent::extraNone, oEvent::filterOnlyMulti); gdi.pushX(); gdi.fillRight(); oe->updateComputerTime(); int t=oe->getComputerTime()-(oe->getComputerTime()%60)+60; gdi.addInput("Rope", oe->getAbsTime(t), 6, 0, L"Repdragningstid"); gdi.addInput("Restart", oe->getAbsTime(t+600), 6, 0, L"Omstartstid"); gdi.dropLine(0.9); gdi.addButton("DoRestart","OK", ClassesCB); gdi.addButton("Cancel","Stäng", ClassesCB); gdi.fillDown(); gdi.dropLine(3); gdi.popX(); } else if (bi.id=="DoRestart") { set cls; gdi.getSelection("RestartClasses", cls); gdi.fillDown(); set::iterator it; wstring ropeS=gdi.getText("Rope"); int rope = oe->getRelativeTime(ropeS); wstring restartS=gdi.getText("Restart"); int restart = oe->getRelativeTime(restartS); if (rope<=0) { gdi.alert("Ogiltig repdragningstid."); return 0; } if (restart<=0) { gdi.alert("Ogiltig omstartstid."); return 0; } if (restartgetAbsTime(rope) + L"#" + oe->getAbsTime(restart)); for (it=cls.begin(); it!=cls.end(); ++it) { pClass pc=oe->getClass(*it); if (pc) { gdi.addStringUT(0, pc->getName()); int ns=pc->getNumStages(); for (int k=0;ksetRopeTime(k, ropeS); pc->setRestartTime(k, restartS); } } } gdi.scrollToBottom(); gdi.refresh(); } else if (bi.id=="SaveDrawSettings") { readClassSettings(gdi); for(size_t k=0; ksynchronize(false); ci.pc->setDrawFirstStart(drawInfo.firstStart + drawInfo.baseInterval * ci.firstStart); ci.pc->setDrawInterval(ci.interval * drawInfo.baseInterval); ci.pc->setDrawVacant(ci.nVacant); ci.pc->setDrawNumReserved(ci.nExtra); ci.pc->synchronize(true); } } } else if (bi.id=="DoDrawAll") { readClassSettings(gdi); int method = gdi.getSelectedItem("Method").first; bool soft = method == DMSOFT; int pairSize = gdi.getSelectedItem("PairSize").first; bool drawCoursebased = drawInfo.coursesTogether; int maxST = 0; map > specs; for(size_t k=0; ksynchronize(false); 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); ci.pc->synchronize(true); } ClassDrawSpecification cds(ci.classId, 0, drawInfo.firstStart + drawInfo.baseInterval * ci.firstStart, drawInfo.baseInterval * ci.interval, ci.nVacant); if (drawCoursebased) { pCourse pCrs = oe->getClass(ci.classId)->getCourse(); int id = pCrs ? pCrs->getId() : 101010101 + ci.classId; specs[id].push_back(cds); } else 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); } oe->addAutoBib(); clearPage(gdi, false); gdi.addButton("Cancel", "Återgå", ClassesCB); oListParam par; oListInfo info; par.listCode = EStdStartList; for (size_t k=0; kgenerateListInfo(par, gdi.getLineHeight(), info); oe->generateList(gdi, false, info, true); gdi.refresh(); } else if (bi.id == "RemoveVacant") { if (gdi.ask(L"Vill du radera alla vakanser från tävlingen?")) { oe->removeVacanies(0); gdi.disableInput(bi.id.c_str()); } } else if (bi.id == "SelectAll") { set lst; oe->getAllClasses(lst); gdi.setSelection("Classes", lst); enableLoadSettings(gdi); } else if (bi.id == "SelectUndrawn") { set lst; oe->getNotDrawnClasses(lst, false); gdi.setSelection("Classes", lst); enableLoadSettings(gdi); } else if (bi.id == "SelectStart") { int id = bi.getExtraInt(); vector blocks; vector starts; oe->getStartBlocks(blocks, starts); if (size_t(id) < starts.size()) { wstring start = starts[id]; set lst; vector cls; oe->getClasses(cls, true); for (size_t k = 0; k < cls.size(); k++) { if (cls[k]->getStart() == start) lst.insert(cls[k]->getId()); } gdi.setSelection("Classes", lst); } enableLoadSettings(gdi); } else if (bi.id == "QuickSettings") { save(gdi, false); prepareForDrawing(gdi); } else if (bi.id == "DrawMode") { save(gdi, false); ClassId = 0; EditChanged=false; clearPage(gdi, true); gdi.addString("", boldLarge, "Lotta flera klasser"); gdi.dropLine(); gdi.pushX(); gdi.fillRight(); gdi.addInput("FirstStart", oe->getAbsTime(3600), 10, 0, L"Första (ordinarie) start:"); gdi.addInput("MinInterval", L"2:00", 10, 0, L"Minsta startintervall:"); gdi.fillDown(); gdi.addInput("Vacances", L"5%", 10, 0, L"Andel vakanser:"); gdi.popX(); gdi.addSelection("Method", 200, 200, 0, L"Metod:"); gdi.addItem("Method", lang.tl("Lottning"), DMRandom); gdi.addItem("Method", lang.tl("SOFT-lottning"), DMSOFT); gdi.selectItemByData("Method", getDefaultMethod(false)); gdi.fillDown(); gdi.addCheckbox("LateBefore", "Efteranmälda före ordinarie"); gdi.dropLine(); gdi.popX(); gdi.fillRight(); gdi.addButton("AutomaticDraw", "Automatisk lottning", ClassesCB).setDefault(); gdi.addButton("DrawAll", "Manuell lottning", ClassesCB).setExtra(1); gdi.addButton("Simultaneous", "Gemensam start", ClassesCB); const bool multiDay = oe->hasPrevStage(); if (multiDay) gdi.addButton("Pursuit", "Hantera jaktstart", ClassesCB); gdi.addButton("Cancel", "Återgå", ClassesCB).setCancel(); gdi.dropLine(3); gdi.newColumn(); gdi.addString("", 10, "help_autodraw"); } else if (bi.id == "Pursuit") { pursuitDialog(gdi); } else if (bi.id == "SelectAllNoneP") { bool select = bi.getExtraInt() != 0; for (int k = 0; k < oe->getNumClasses(); k++) { gdi.check("PLUse" + itos(k), select); gdi.setInputStatus("First" + itos(k), select); } } else if (bi.id == "DoPursuit" || bi.id=="CancelPursuit" || bi.id == "SavePursuit") { bool cancel = bi.id=="CancelPursuit"; int maxAfter = convertAbsoluteTimeMS(gdi.getText("MaxAfter")); int deltaRestart = convertAbsoluteTimeMS(gdi.getText("TimeRestart")); int interval = convertAbsoluteTimeMS(gdi.getText("Interval")); double scale = _wtof(gdi.getText("ScaleFactor").c_str()); bool reverse = bi.getExtraInt() == 2; int pairSize = gdi.getSelectedItem("PairSize").first; pSavedDepth = maxAfter; pFirstRestart = deltaRestart; pTimeScaling = scale; pInterval = interval; oListParam par; for (int k = 0; k < oe->getNumClasses(); k++) { if (!gdi.hasField("PLUse" + itos(k))) continue; BaseInfo *biu = gdi.setText("PLUse" + itos(k), L"", false); if (biu) { int id = biu->getExtraInt(); bool checked = gdi.isChecked("PLUse" + itos(k)); int first = oe->getRelativeTime(gdi.getText("First" + itos(k))); map::iterator st = pSettings.find(id); if (st != pSettings.end()) { st->second.firstTime = first; st->second.maxTime = maxAfter; st->second.use = checked; } if (checked) { pClass pc = oe->getClass(id); if (pc) pc->setDrawFirstStart(first); } if (!cancel && checked) { oe->drawPersuitList(id, first, first + deltaRestart, maxAfter, interval, pairSize, reverse, scale); par.selection.insert(id); } } } if (bi.id == "SavePursuit") { return 0; } if (cancel) { loadPage(gdi); return 0; } gdi.restore("Pursuit", false); gdi.dropLine(); gdi.fillDown(); oListInfo info; par.listCode = EStdStartList; oe->generateListInfo(par, gdi.getLineHeight(), info); oe->generateList(gdi, false, info, true); gdi.dropLine(); gdi.addButton("Cancel", "Återgå", ClassesCB); gdi.refresh(); } else if (bi.id.substr(0,5) == "PLUse") { int k = atoi(bi.id.substr(5).c_str()); gdi.setInputStatus("First" + itos(k), gdi.isChecked(bi.id)); } 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; //bool pairwise = gdi.isChecked("Pairwise"); int pairSize = 1; if (gdi.hasField("PairSize")) { pairSize = gdi.getSelectedItem("PairSize").first; } int method = gdi.getSelectedItem("Method").first; bool useSoft = method == DMSOFT; clearPage(gdi, true); oe->automaticDrawAll(gdi, firstStart, minInterval, vacances, lateBefore, useSoft, pairSize); oe->addAutoBib(); gdi.scrollToBottom(); gdi.addButton("Cancel", "Återgå", ClassesCB); } else if (bi.id == "SelectMisses") { set lst; oe->getNotDrawnClasses(lst, true); gdi.setSelection("Classes", lst); enableLoadSettings(gdi); } else if (bi.id == "SelectNone") { gdi.setSelection("Classes", set()); enableLoadSettings(gdi); } else if (bi.id == "Simultaneous") { wstring firstStart; firstStart = gdi.getText("FirstStart"); clearPage(gdi, false); gdi.addString("", boldLarge, "Gemensam start"); gdi.dropLine(); int by = 0; int bx = gdi.getCX(); showClassSelection(gdi, bx, by, 0); gdi.pushX(); gdi.addInput("FirstStart", firstStart, 10, 0, L"Starttid:"); gdi.dropLine(4); gdi.popX(); gdi.fillRight(); gdi.addButton("AssignStart", "Tilldela", ClassesCB).isEdit(true); gdi.addButton("Cancel", "Avbryt", ClassesCB).setCancel(); gdi.addButton("EraseStartAll", "Radera starttider...", ClassesCB).isEdit(true).setExtra(1); gdi.refresh(); } else if (bi.id == "AssignStart") { set classes; gdi.getSelection("Classes", classes); if (classes.empty()) { gdi.alert("Ingen klass vald."); return 0; } wstring time = gdi.getText("FirstStart"); if (warnDrawStartTime(gdi, time)) return 0; for (set::iterator it = classes.begin(); it!=classes.end();++it) { simultaneous(*it, time); } bi.id = "Simultaneous"; classCB(gdi, type, &bi); } else if (bi.id == "DrawAll") { int origin = bi.getExtraInt(); wstring firstStart = oe->getAbsTime(3600); wstring minInterval = L"2:00"; wstring vacances = L"5%"; int maxNumControl = 1; int pairSize = 1; //bool pairwise = false; int by = 0; int bx = gdi.getCX(); if (origin!=13) { if (origin!=1) { save(gdi, true); ClassId = 0; EditChanged=false; } else { firstStart = gdi.getText("FirstStart"); minInterval = gdi.getText("MinInterval"); vacances = gdi.getText("Vacances"); //pairwise = gdi.isChecked("Pairwise"); if (gdi.hasField("PairSize")) { pairSize = gdi.getSelectedItem("PairSize").first; } } clearPage(gdi, false); gdi.addString("", boldLarge, "Lotta flera klasser"); gdi.dropLine(0.5); showClassSelection(gdi, bx, by, DrawClassesCB); vector cls; oe->getClasses(cls, false); set clsId; for (size_t k = 0; k < cls.size(); k++) { if (cls[k]->hasFreeStart()) continue; if (cls[k]->getStartType(0) != STDrawn) continue; clsId.insert(cls[k]->getId()); } gdi.setSelection("Classes", clsId); gdi.addString("", 1, "Grundinställningar"); gdi.pushX(); gdi.fillRight(); gdi.addInput("FirstStart", firstStart, 10, 0, L"Första start:"); gdi.addInput("nFields", L"10", 10, 0, L"Max parallellt startande:"); gdi.popX(); gdi.dropLine(3); gdi.addSelection("MaxCommonControl", 150, 100, 0, L"Max antal gemensamma kontroller:"); vector< pair > items; items.push_back(make_pair(lang.tl("Inga"), 1)); items.push_back(make_pair(lang.tl("Första kontrollen"), 2)); for (int k = 2; k<10; k++) items.push_back(make_pair(lang.tl("X kontroller#" + itos(k)), k+1)); items.push_back(make_pair(lang.tl("Hela banan"), 1000)); gdi.addItem("MaxCommonControl", items); gdi.selectItemByData("MaxCommonControl", maxNumControl); gdi.popX(); gdi.dropLine(4); gdi.addCheckbox("AllowNeighbours", "Tillåt samma bana inom basintervall", 0, true); gdi.addCheckbox("CoursesTogether", "Lotta klasser med samma bana gemensamt", 0, false); gdi.popX(); gdi.dropLine(2); gdi.addString("", 1, "Startintervall"); gdi.dropLine(1.4); gdi.popX(); gdi.fillRight(); gdi.addInput("BaseInterval", L"1:00", 10, 0, L"Basintervall (min):"); gdi.addInput("MinInterval", minInterval, 10, 0, L"Minsta intervall i klass:"); gdi.addInput("MaxInterval", minInterval, 10, 0, L"Största intervall i klass:"); gdi.popX(); gdi.dropLine(4); gdi.addString("", 1, "Vakanser och efteranmälda"); gdi.dropLine(1.4); gdi.popX(); gdi.addInput("Vacances", vacances, 10, 0, L"Andel vakanser:"); gdi.addInput("VacancesMin", L"1", 10, 0, L"Min. vakanser (per klass):"); gdi.addInput("VacancesMax", L"10", 10, 0, L"Max. vakanser (per klass):"); gdi.popX(); gdi.dropLine(3); gdi.addInput("Extra", L"0%", 10, 0, L"Förväntad andel efteranmälda:"); gdi.dropLine(4); gdi.fillDown(); gdi.popX(); gdi.setRestorePoint("Setup"); } else { gdi.restore("Setup"); by = gdi.getHeight(); gdi.enableEditControls(true); } gdi.fillRight(); gdi.pushX(); RECT rcPrepare; rcPrepare.left = gdi.getCX(); rcPrepare.top = gdi.getCY(); gdi.setCX(gdi.getCX() + gdi.getLineHeight()); gdi.dropLine(); gdi.addString("", fontMediumPlus, "Förbered lottning"); gdi.dropLine(2.5); gdi.popX(); gdi.setCX(gdi.getCX() + gdi.getLineHeight()); gdi.addButton("PrepareDrawAll", "Fördela starttider...", ClassesCB).isEdit(true).setDefault(); gdi.addButton("EraseStartAll", "Radera starttider...", ClassesCB).isEdit(true).setExtra(0); gdi.addButton("LoadSettings", "Hämta inställningar från föregående lottning", ClassesCB).isEdit(true); enableLoadSettings(gdi); gdi.dropLine(3); rcPrepare.bottom = gdi.getCY(); rcPrepare.right = gdi.getWidth(); gdi.addRectangle(rcPrepare, colorLightGreen); gdi.dropLine(); gdi.popX(); gdi.addString("", 1, "Efteranmälningar"); gdi.dropLine(1.5); gdi.popX(); gdi.addButton("DrawAllBefore", "Efteranmälda (före ordinarie)", ClassesCB).isEdit(true); gdi.addButton("DrawAllAfter", "Efteranmälda (efter ordinarie)", ClassesCB).isEdit(true); gdi.dropLine(4); gdi.popX(); gdi.addButton("Cancel", "Avbryt", ClassesCB).setCancel(); gdi.addButton("HelpDraw", "Hjälp...", ClassesCB, ""); gdi.dropLine(3); by = max(by, gdi.getCY()); gdi.setCX(bx); gdi.setCY(by); gdi.fillDown(); gdi.dropLine(); gdi.setRestorePoint("ReadyToDistribute"); gdi.refresh(); } else if (bi.id == "HelpDraw") { gdioutput *gdi_new = getExtraWindow("help", true); if (!gdi_new) gdi_new = createExtraWindow("help", makeDash(L"MeOS - " + lang.tl(L"Hjälp")), gdi.scaleLength(640)); gdi_new->clearPage(true); gdi_new->addString("", boldLarge, "Lotta flera klasser"); gdi_new->addString("", 10, "help_draw"); gdi_new->dropLine(); gdi_new->addButton("CloseWindow", "Stäng", ClassesCB); } else if (bi.id == "CloseWindow") { gdi.closeWindow(); } else if (bi.id=="PrepareDrawAll") { set classes; gdi.getSelection("Classes", classes); if (classes.empty()) { throw meosException("Ingen klass vald."); } gdi.restore("ReadyToDistribute"); drawInfo.classes.clear(); for (set::iterator it = classes.begin(); it!=classes.end();++it) { map::iterator res = cInfoCache.find(*it); if ( res != cInfoCache.end() ) { res->second.hasFixedTime = false; drawInfo.classes[*it] = res->second; } else drawInfo.classes[*it] = ClassInfo(oe->getClass(*it)); } readDrawInfo(gdi, drawInfo); if (drawInfo.baseInterval <= 0 || drawInfo.baseInterval == NOTIME) throw meosException("Ogiltigt basintervall."); if (drawInfo.minClassInterval <= 0 || drawInfo.minClassInterval == NOTIME) throw meosException("Ogiltigt minimalt intervall."); if (drawInfo.minClassInterval > drawInfo.maxClassInterval || drawInfo.maxClassInterval == NOTIME) throw meosException("Ogiltigt maximalt intervall. "); if (drawInfo.minClassInterval < drawInfo.baseInterval) { throw meosException("Startintervallet får inte vara kortare än basintervallet."); } if (drawInfo.minClassInterval % drawInfo.baseInterval != 0 || drawInfo.maxClassInterval % drawInfo.baseInterval != 0) { throw meosException("Ett startintervall måste vara en multipel av basintervallet."); } gdi.enableEditControls(false); if (drawInfo.firstStart<=0) { drawInfo.baseInterval = 0; gdi.addString("", 0, "Raderar starttider..."); } oe->optimizeStartOrder(gdi, drawInfo, cInfo); showClassSettings(gdi); } else if (bi.id == "LoadSettings") { set classes; gdi.getSelection("Classes", classes); if (classes.empty()) { throw meosException("Ingen klass vald."); } gdi.restore("ReadyToDistribute"); oe->loadDrawSettings(classes, drawInfo, cInfo); /* drawInfo.firstStart = 3600 * 22; drawInfo.minClassInterval = 3600; drawInfo.maxClassInterval = 1; drawInfo.minVacancy = 10; set reducedStart; for (set::iterator it = classes.begin(); it != classes.end(); ++it) { pClass pc = oe->getClass(*it); if (pc) { int fs = pc->getDrawFirstStart(); int iv = pc->getDrawInterval(); if (iv > 0 && fs > 0) { drawInfo.firstStart = min(drawInfo.firstStart, fs); drawInfo.minClassInterval = min(drawInfo.minClassInterval, iv); drawInfo.maxClassInterval = max(drawInfo.maxClassInterval, iv); drawInfo.minVacancy = min(drawInfo.minVacancy, pc->getDrawVacant()); drawInfo.maxVacancy = max(drawInfo.maxVacancy, pc->getDrawVacant()); reducedStart.insert(fs%iv); } } } drawInfo.baseInterval = drawInfo.minClassInterval; int lastStart = -1; for (set::iterator it = reducedStart.begin(); it != reducedStart.end(); ++it) { if (lastStart == -1) lastStart = *it; else { drawInfo.baseInterval = min(drawInfo.baseInterval, *it-lastStart); lastStart = *it; } } cInfo.clear(); cInfo.resize(classes.size()); int i = 0; for (set::iterator it = classes.begin(); it != classes.end(); ++it) { pClass pc = oe->getClass(*it); if (pc) { int fs = pc->getDrawFirstStart(); int iv = pc->getDrawInterval(); cInfo[i].pc = pc; cInfo[i].classId = *it; cInfo[i].courseId = pc->getCourseId(); cInfo[i].firstStart = fs; cInfo[i].unique = pc->getCourseId(); if (cInfo[i].unique == 0) cInfo[i].unique = pc->getId() * 10000; cInfo[i].firstStart = (fs - drawInfo.firstStart) / drawInfo.baseInterval; cInfo[i].interval = iv / drawInfo.baseInterval; cInfo[i].nVacant = pc->getDrawVacant(); i++; } } */ writeDrawInfo(gdi, drawInfo); gdi.enableEditControls(false); showClassSettings(gdi); } else if (bi.id == "VisualizeDraw") { readClassSettings(gdi); gdioutput *gdi_new = getExtraWindow(visualDrawWindow, true); if (!gdi_new) gdi_new = createExtraWindow(visualDrawWindow, makeDash(L"MeOS - " + lang.tl(L"Visualisera startfältet")), gdi.scaleLength(1000)); gdi_new->clearPage(false); gdi_new->addString("", boldLarge, "Visualisera startfältet"); gdi_new->dropLine(); gdi_new->addString("", 0, "För muspekaren över en markering för att få mer information."); gdi_new->dropLine(); visualizeField(*gdi_new); gdi_new->dropLine(); gdi_new->addButton("CloseWindow", "Stäng", ClassesCB); gdi_new->registerEvent("CloseWindow", 0).setHandler(&handleCloseWindow); gdi_new->refresh(); } else if (bi.id == "EraseStartAll") { set classes; gdi.getSelection("Classes", classes); if (classes.empty()) { gdi.alert("Ingen klass vald."); return 0; } if (classes.size() == 1) { pClass pc = oe->getClass(*classes.begin()); if (!pc || !gdi.ask(L"Vill du verkligen radera alla starttider i X?#" + pc->getName())) return 0; } else { if (!gdi.ask(L"Vill du verkligen radera starttider i X klasser?#" + itow(classes.size())) ) return 0; } for (set::const_iterator it = classes.begin(); it != classes.end(); ++it) { vector spec; spec.push_back(ClassDrawSpecification(*it, 0, 0, 0, 0)); oe->drawList(spec, false, 1, oEvent::drawAll); } if (bi.getExtraInt() == 1) bi.id = "Simultaneous"; else bi.id = "DrawAll"; bi.setExtra(1); classCB(gdi, type, &bi); // Reload draw dialog } else if (bi.id == "DrawAdjust") { readClassSettings(gdi); gdi.restore("ReadyToDistribute"); oe->optimizeStartOrder(gdi, drawInfo, cInfo); showClassSettings(gdi); } else if (bi.id == "DrawAllAdjust") { readClassSettings(gdi); bi.id = "DrawAll"; return classCB(gdi, type, &bi); } else if (bi.id == "DrawAllBefore" || bi.id == "DrawAllAfter") { oe->drawRemaining(true, bi.id == "DrawAllAfter"); oe->addAutoBib(); loadPage(gdi); } else if (bi.id=="DoDraw" || bi.id=="DoDrawAfter" || bi.id=="DoDrawBefore"){ if (!checkClassSelected(gdi)) return false; DWORD cid=ClassId; DrawMethod method = DrawMethod(gdi.getSelectedItem("Method").first); int interval = 0; if (gdi.hasField("Interval")) interval = convertAbsoluteTimeMS(gdi.getText("Interval")); int vacanses = 0; if (gdi.hasField("Vacanses")) vacanses = gdi.getTextNo("Vacanses"); int leg = 0; if (gdi.hasField("Leg")) { leg = gdi.getSelectedItem("Leg").first; } wstring bib; bool doBibs = false; if (gdi.hasField("Bib")) { bib = gdi.getText("Bib"); doBibs = gdi.isChecked("HandleBibs"); } wstring time = gdi.getText("FirstStart"); int t=oe->getRelativeTime(time); 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")) // pairwise = gdi.isChecked("Pairwise"); int pairSize = 1; if (gdi.hasField("PairSize")) { pairSize = gdi.getSelectedItem("PairSize").first; } int maxTime = 0, restartTime = 0; double scaleFactor = 1.0; if (gdi.hasField("TimeRestart")) restartTime = oe->getRelativeTime(gdi.getText("TimeRestart")); if (gdi.hasField("MaxAfter")) maxTime = convertAbsoluteTimeMS(gdi.getText("MaxAfter")); if (gdi.hasField("ScaleFactor")) scaleFactor = _wtof(gdi.getText("ScaleFactor").c_str()); if (method == DMRandom || method == DMSOFT) { vector spec; spec.push_back(ClassDrawSpecification(cid, leg, t, interval, vacanses)); oe->drawList(spec, method == DMSOFT, pairSize, dtype); } else if (method == DMClumped) oe->drawListClumped(cid, t, interval, vacanses); else if (method == DMPursuit || method == DMReversePursuit) { oe->drawPersuitList(cid, t, restartTime, maxTime, interval, pairSize, method == DMReversePursuit, scaleFactor); } else if (method == DMSimultaneous) { simultaneous(cid, time); } else if (method == DMSeeded) { ListBoxInfo seedMethod; gdi.getSelectedItem("SeedMethod", seedMethod); wstring seedGroups = gdi.getText("SeedGroups"); vector out; split(seedGroups, L" ,;", out); vector sg; bool invalid = false; for (size_t k = 0; k < out.size(); k++) { if (trim(out[k]).empty()) continue; int val = _wtoi(trim(out[k]).c_str()); if (val <= 0) invalid = true; sg.push_back(val); } if (invalid || sg.empty()) throw meosException(L"Ogiltig storlek på seedningsgrupper X.#" + seedGroups); bool noClubNb = gdi.isChecked("PreventClubNb"); bool reverse = gdi.isChecked("ReverseSeedning"); pClass pc=oe->getClass(ClassId); if (!pc) throw meosException("Class not found"); pc->drawSeeded(ClassSeedMethod(seedMethod.data), leg, t, interval, sg, noClubNb, reverse, pairSize); } else throw std::exception("Not implemented"); if (doBibs) oe->addBib(cid, leg, bib); // Clear input gdi.restore("", false); gdi.addButton("Cancel", "Återgå", ClassesCB).setCancel(); gdi.dropLine(); oListParam par; par.selection.insert(cid); oListInfo info; par.listCode = EStdStartList; par.setLegNumberCoded(leg); oe->generateListInfo(par, gdi.getLineHeight(), info); oe->generateList(gdi, false, info, true); gdi.refresh(); gdi.setData("ClassPageLoaded", 1); return 0; } else if (bi.id=="HandleBibs") { gdi.setInputStatus("Bib", gdi.isChecked("HandleBibs")); } else if (bi.id == "DoDeleteStart") { pClass pc=oe->getClass(ClassId); if (!pc) throw meosException("Class not found"); if (!gdi.ask(L"Vill du verkligen radera alla starttider i X?#" + pc->getName())) return 0; int leg = 0; if (gdi.hasField("Leg")) { leg = gdi.getSelectedItem("Leg").first; } vector spec; spec.push_back(ClassDrawSpecification(ClassId, leg, 0, 0, 0)); oe->drawList(spec, false, 1, oEvent::drawAll); loadPage(gdi); } else if (bi.id=="Draw") { save(gdi, true); if (!checkClassSelected(gdi)) return false; DWORD cid=ClassId; if (oe->classHasResults(cid)) { if (!gdi.ask(L"warning:drawresult")) return 0; } pClass pc=oe->getClass(cid); if (!pc) throw std::exception("Class not found"); if (EditChanged) gdi.sendCtrlMessage("Save"); clearPage(gdi, false); gdi.addString("", boldLarge, L"Lotta klassen X#"+pc->getName()); gdi.dropLine(); gdi.pushX(); gdi.setRestorePoint(); gdi.fillDown(); bool multiDay = oe->hasPrevStage(); if (multiDay) { gdi.addCheckbox("HandleMultiDay", "Använd funktioner för fleretappsklass", ClassesCB, true); } gdi.addSelection("Method", 200, 200, ClassesCB, L"Metod:"); gdi.dropLine(1.5); gdi.popX(); gdi.setRestorePoint("MultiDayDraw"); lastDrawMethod = NOMethod; drawDialog(gdi, getDefaultMethod(true), *pc); } else if (bi.id == "HandleMultiDay") { ListBoxInfo lbi; gdi.getSelectedItem("Method", lbi); pClass pc=oe->getClass(ClassId); if (!pc) throw std::exception("Class not found"); if (gdi.isChecked(bi.id) && (lastDrawMethod == DMReversePursuit || lastDrawMethod == DMPursuit)) { drawDialog(gdi, getDefaultMethod(false), *pc); } else setMultiDayClass(gdi, gdi.isChecked(bi.id), lastDrawMethod); } else if (bi.id=="Bibs") { save(gdi, true); if (!checkClassSelected(gdi)) return false; DWORD cid=ClassId; pClass pc=oe->getClass(cid); if (!pc) throw std::exception("Class not found"); clearPage(gdi, false); gdi.addString("", boldLarge, L"Nummerlappar i X#" + pc->getName()); gdi.dropLine(); gdi.setRestorePoint("bib"); gdi.addString("", 10, "help:bibs"); gdi.dropLine(); vector< pair > bibOptions; vector< pair > bibTeamOptions; bibOptions.push_back(make_pair(lang.tl("Manuell"), AutoBibManual)); bibOptions.push_back(make_pair(lang.tl("Löpande"), AutoBibConsecutive)); bibOptions.push_back(make_pair(lang.tl("Ingen"), AutoBibNone)); bibOptions.push_back(make_pair(lang.tl("Automatisk"), AutoBibExplicit)); gdi.fillRight(); gdi.pushX(); gdi.addSelection("BibSettings", 150, 100, ClassesCB, L"Metod:"); gdi.addItem("BibSettings", bibOptions); AutoBibType bt = pc->getAutoBibType(); gdi.selectItemByData("BibSettings", bt); wstring bib = pc->getDCI().getString("Bib"); if (pc->getNumDistinctRunners() > 1) { bibTeamOptions.push_back(make_pair(lang.tl("Oberoende"), BibFree)); bibTeamOptions.push_back(make_pair(lang.tl("Samma"), BibSame)); bibTeamOptions.push_back(make_pair(lang.tl("Ökande"), BibAdd)); bibTeamOptions.push_back(make_pair(lang.tl("Sträcka"), BibLeg)); gdi.addSelection("BibTeam", 80, 100, 0, L"Lagmedlem:", L"Ange relation mellan lagets och deltagarnas nummerlappar."); gdi.addItem("BibTeam", bibTeamOptions); gdi.selectItemByData("BibTeam", pc->getBibMode()); } gdi.dropLine(1.1); gdi.addInput("Bib", L"", 10, 0, L""); gdi.dropLine(3); gdi.fillRight(); gdi.popX(); gdi.addString("", 0, "Antal reserverade nummerlappsnummer mellan klasser:"); gdi.dropLine(-0.1); gdi.addInput("BibGap", itow(oe->getBibClassGap()), 5); gdi.dropLine(3); gdi.popX(); gdi.fillRight(); gdi.addButton("DoBibs", "Tilldela", ClassesCB).setDefault(); gdi.setInputStatus("Bib", bt == AutoBibExplicit || bt == AutoBibManual); gdi.setInputStatus("BibGap", bt != AutoBibManual); if (bt != AutoBibManual) gdi.setTextTranslate("DoBibs", L"OK"); if (bt == AutoBibExplicit) gdi.setText("Bib", bib); gdi.fillDown(); gdi.addButton("Cancel", "Avbryt", ClassesCB).setCancel(); gdi.popX(); EditChanged=false; gdi.refresh(); } else if (bi.id=="DoBibs") { if (!checkClassSelected(gdi)) return false; DWORD cid=ClassId; pClass pc = oe->getClass(cid); AutoBibType bt = AutoBibType(gdi.getSelectedItem("BibSettings").first); pair teamBib = gdi.getSelectedItem("TeamBib"); if (teamBib.second) { pc->setBibMode(BibMode(teamBib.first)); } pc->getDI().setString("Bib", getBibCode(bt, gdi.getText("Bib"))); pc->synchronize(); if (bt == AutoBibManual) { oe->addBib(cid, 0, gdi.getText("Bib")); } else { oe->setBibClassGap(gdi.getTextNo("BibGap")); oe->addAutoBib(); } gdi.restore("bib", false); gdi.dropLine(); gdi.addButton("Cancel", "Återgå", ClassesCB).setDefault(); oListParam par; par.selection.insert(cid); oListInfo info; par.listCode = EStdStartList; par.setLegNumberCoded(0); oe->generateListInfo(par, gdi.getLineHeight(), info); oe->generateList(gdi, false, info, true); gdi.refresh(); return 0; } else if (bi.id=="Split") { save(gdi, true); if (!checkClassSelected(gdi)) return false; pClass pc=oe->getClass(ClassId); if (!pc) throw std::exception("Class not found"); clearPage(gdi, true); gdi.addString("", boldLarge, L"Dela klass: X#" + pc->getName()); gdi.dropLine(); int tot, fin, dns; oe->getNumClassRunners(pc->getId(), 0, tot, fin, dns); gdi.addString("", fontMediumPlus, "Antal deltagare: X#" + itos(tot)); gdi.dropLine(1.2); gdi.pushX(); gdi.fillRight(); gdi.addSelection("Type", 200, 100, ClassesCB, L"Typ av delning:"); gdi.selectItemByData("Type", 1); vector< pair > mt; oClass::getSplitMethods(mt); gdi.addItem("Type", mt); gdi.selectFirstItem("Type"); gdi.addSelection("SplitInput", 100, 150, ClassesCB, L"Antal klasser:").setExtra(tot); vector< pair > sp; for (int k = 2; k < 10; k++) sp.push_back(make_pair(itow(k), k)); gdi.addItem("SplitInput", sp); gdi.selectFirstItem("SplitInput"); gdi.dropLine(3); gdi.popX(); updateSplitDistribution(gdi, 2, tot); } else if (bi.id=="DoSplit") { if (!checkClassSelected(gdi)) return false; pClass pc=oe->getClass(ClassId); if (!pc) throw std::exception("Class not found"); ListBoxInfo lbi; gdi.getSelectedItem("Type", lbi); int number = gdi.getSelectedItem("SplitInput").first; vector parts(number); for (int k = 0; k < number; k++) { string id = "CLS" + itos(k); parts[k] = gdi.getTextNo(id, false); } vector outClass; pc->splitClass(ClassSplitMethod(lbi.data), parts, outClass); clearPage(gdi, true); gdi.addButton("Cancel", "Återgå", ClassesCB); oListParam par; par.selection.insert(outClass.begin(), outClass.end()); oListInfo info; par.listCode = EStdStartList; 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)) return false; pClass pc=oe->getClass(ClassId); if (!pc) throw std::exception("Class not found"); vector< pair > rawClass, cls; oe->fillClasses(rawClass, oEvent::extraNone, oEvent::filterNone); int def = -1; bool next = false; for (size_t k = 0; k < rawClass.size(); k++) { if (rawClass[k].second == ClassId) next = true; else { cls.push_back(rawClass[k]); if (next) { next = false; def = rawClass[k].second; } } } if (cls.empty()) throw std::exception("En klass kan inte slås ihop med sig själv."); clearPage(gdi, true); gdi.addString("", boldLarge, L"Slå ihop klass: X (denna klass behålls)#" + pc->getName()); gdi.dropLine(); gdi.addString("", 10, "help:12138"); gdi.dropLine(2); gdi.pushX(); gdi.fillRight(); gdi.addSelection("Class", 150, 300, 0, L"Klass att slå ihop:"); gdi.addItem("Class", cls); if (def != -1) gdi.selectItemByData("Class", def); else gdi.selectFirstItem("Class"); gdi.dropLine(); gdi.addButton("DoMergeAsk", "Slå ihop", ClassesCB).setDefault(); gdi.addButton("Cancel", "Avbryt", ClassesCB).setCancel(); gdi.dropLine(3); gdi.popX(); } else if (bi.id=="DoMergeAsk") { if (!checkClassSelected(gdi)) return false; pClass pc=oe->getClass(ClassId); if (!pc) throw std::exception("Class not found"); pClass mergeClass = oe->getClass(gdi.getSelectedItem("Class").first); if (!mergeClass) throw std::exception("Ingen klass vald."); if (mergeClass->getId() == ClassId) throw std::exception("En klass kan inte slås ihop med sig själv."); if (gdi.ask(L"Vill du flytta löpare från X till Y och ta bort Z?#" + mergeClass->getName() + L"#" + pc->getName() + L"#" + mergeClass->getName())) { bi.id = "DoMerge"; return classCB(gdi, type, &bi); } return false; } else if (bi.id=="DoMerge") { if (!checkClassSelected(gdi)) return false; pClass pc=oe->getClass(ClassId); if (!pc) throw std::exception("Class not found"); ListBoxInfo lbi; gdi.getSelectedItem("Class", lbi); if (signed(lbi.data)<=0) throw std::exception("Ingen klass vald."); if (lbi.data==ClassId) throw std::exception("En klass kan inte slås ihop med sig själv."); pc->mergeClass(lbi.data); clearPage(gdi, true); gdi.addButton("Cancel", "Återgå", ClassesCB); oListParam par; par.selection.insert(ClassId); oListInfo info; par.listCode = EStdStartList; oe->generateListInfo(par, gdi.getLineHeight(), info); oe->generateList(gdi, false, info, true); gdi.refresh(); } else if (bi.id=="MultiCourse") { save(gdi, false); multiCourse(gdi, 0); gdi.refresh(); } else if (bi.id=="Save") save(gdi, false); else if (bi.id=="Add") { wstring name = gdi.getText("Name"); pClass c = oe->getClass(ClassId); if (!name.empty() && c && c->getName() != name) { if (gdi.ask(L"Vill du lägga till klassen 'X'?#" + name)) { c = oe->addClass(name); ClassId = c->getId(); save(gdi, false); return true; } } save(gdi, true); pClass pc = oe->addClass(oe->getAutoClassName(), 0); if (pc) { oe->fillClasses(gdi, "Classes", oEvent::extraDrawn, oEvent::filterNone); selectClass(gdi, pc->getId()); gdi.setInputFocus("Name", true); } } else if (bi.id=="Remove") { EditChanged=false; if (!checkClassSelected(gdi)) return false; DWORD cid=ClassId; if (oe->isClassUsed(cid)) gdi.alert("Klassen används och kan inte tas bort."); else oe->removeClass(cid); oe->fillClasses(gdi, "Classes", oEvent::extraDrawn, oEvent::filterNone); ClassId = 0; selectClass(gdi, 0); } } else if (type==GUI_LISTBOX) { ListBoxInfo bi=*(ListBoxInfo *)data; if (bi.id=="Classes") { if (gdi.isInputChanged("")) save(gdi, true); selectClass(gdi, bi.data); } else if (bi.id == "SplitInput") { int num = bi.data; updateSplitDistribution(gdi, num, bi.getExtraInt()); } else if (bi.id=="Courses") EditChanged=true; else if (bi.id == "BibSettings") { AutoBibType bt = (AutoBibType)bi.data; gdi.setInputStatus("Bib", bt == AutoBibExplicit || bt == AutoBibManual); gdi.setInputStatus("BibGap", bt != AutoBibManual); if (bt != AutoBibManual) gdi.setTextTranslate("DoBibs", L"OK"); else gdi.setTextTranslate("DoBibs", L"Tilldela"); } else if (bi.id=="Type") { if (bi.data==1) { gdi.setTextTranslate("TypeDesc", L"Antal klasser:", true); gdi.setText("SplitInput", L"2"); } else { gdi.setTextTranslate("TypeDesc", L"Löpare per klass:", true); gdi.setText("SplitInput", L"100"); } } else if (bi.id == "Method") { pClass pc = oe->getClass(ClassId); if (!pc) throw std::exception("Nullpointer exception"); drawDialog(gdi, DrawMethod(bi.data), *pc); } } else if (type==GUI_INPUTCHANGE) { //InputInfo ii=*(InputInfo *)data; } else if (type==GUI_CLEAR) { if (ClassId>0) save(gdi, true); if (EditChanged) { if (gdi.ask(L"Spara ändringar?")) gdi.sendCtrlMessage("Save"); } return true; } return 0; } void TabClass::readClassSettings(gdioutput &gdi) { for (size_t k=0;kgetRelativeTime(start) - drawInfo.firstStart; if (drawInfo.firstStart == 0 && startPos == -1) startPos = 0; else if (startPos<0 || (startPos % drawInfo.baseInterval)!=0) throw std::exception("Ogiltig tid"); startPos /= drawInfo.baseInterval; int intervalPos = convertAbsoluteTimeMS(intervall); if (intervalPos<0 || intervalPos == NOTIME || (intervalPos % drawInfo.baseInterval)!=0) throw std::exception("Ogiltigt startintervall"); intervalPos /= drawInfo.baseInterval; if (ci.nVacant != vacant) { ci.nVacantSpecified = true; ci.nVacant = vacant; } if (ci.nExtra != reserved) { ci.nExtraSpecified = true; ci.nExtra = reserved; } // If times has been changed, mark this class to be kept fixed if (ci.firstStart != startPos || ci.interval!=intervalPos) ci.hasFixedTime = true; ci.firstStart = startPos; ci.interval = intervalPos; drawInfo.classes[ci.classId] = ci; cInfoCache[ci.classId] = ci; cInfoCache[ci.classId].hasFixedTime = false; } } void TabClass::visualizeField(gdioutput &gdi) { ClassInfo::sSortOrder = 3; sort(cInfo.begin(), cInfo.end()); ClassInfo::sSortOrder = 0; vector field; vector index(cInfo.size(), -1); for (size_t k = 0;k < cInfo.size(); k++) { const ClassInfo &ci = cInfo[k]; int laststart = ci.firstStart + (ci.nRunners-1) * ci.interval; for (size_t j = 0; j < field.size(); j++) { if (field[j] < ci.firstStart) { index[k] = j; field[j] = laststart; break; } } if (index[k] == -1) { index[k] = field.size(); field.push_back(laststart); } /* string first=oe->getAbsTime(ci.firstStart*drawInfo.baseInterval+drawInfo.firstStart); string last=oe->getAbsTime((laststart)*drawInfo.baseInterval+drawInfo.firstStart); pClass pc=oe->getClass(ci.classId);*/ } map groupNumber; map groups; int freeNumber = 1; for (size_t k = 0;k < cInfo.size(); k++) { const ClassInfo &ci = cInfo[k]; if (!groupNumber.count(ci.unique)) groupNumber[ci.unique] = freeNumber++; pClass pc = oe->getClass(ci.classId); if (pc) { if (groups[ci.unique].empty()) groups[ci.unique] = pc->getName(); else if (groups[ci.unique].size() < 64) groups[ci.unique] += L", " + pc->getName(); else groups[ci.unique] += L"..."; } } int marg = gdi.scaleLength(20); int xp = gdi.getCX() + marg; int yp = gdi.getCY() + marg; int h = gdi.scaleLength(12); int w = gdi.scaleLength(6); int maxx = xp, maxy = yp; RECT rc; for (size_t k = 0;k < cInfo.size(); k++) { const ClassInfo &ci = cInfo[k]; rc.top = yp + index[k] * h; rc.bottom = rc.top + h - 1; int g = ci.unique; GDICOLOR color = GDICOLOR(RGB(((g * 30)&0xFF), ((g * 50)&0xFF), ((g * 70)&0xFF))); for (int j = 0; jgetClass(ci.classId); if (pc) { wstring course = pc->getCourse() ? L", " + pc->getCourse()->getName() : L""; wstring tip = L"X (Y deltagare, grupp Z, W)#" + pc->getName() + course + L"#" + itow(ci.nRunners) + L"#" + itow(groupNumber[ci.unique]) + L"#" + groups[ci.unique]; rc.left = xp + ci.firstStart * w; int laststart = ci.firstStart + (ci.nRunners-1) * ci.interval; rc.right = xp + (laststart + 1) * w; gdi.addToolTip("", tip, 0, &rc); maxx = max(maxx, rc.right); maxy = max(maxy, rc.bottom); } } rc.left = xp - marg; rc.top = yp - marg; rc.bottom = maxy + marg; rc.right = maxx + marg; gdi.addRectangle(rc, colorLightYellow, true, true); } void TabClass::showClassSettings(gdioutput &gdi) { ClassInfo::sSortOrder = 2; sort(cInfo.begin(), cInfo.end()); ClassInfo::sSortOrder=0; int laststart=0; for (size_t k=0;kgetAbsTime(ci.firstStart*drawInfo.baseInterval+drawInfo.firstStart); wstring last=oe->getAbsTime((cstart)*drawInfo.baseInterval+drawInfo.firstStart); pClass pc=oe->getClass(ci.classId); swprintf_s(bf1, L"%s, %d", pc ? pc->getName().c_str() : L"-", ci.nRunners); swprintf_s(bf2, L"%d-[%d]-%d (%s-%s)", ci.firstStart, ci.interval, cstart, first.c_str(), last.c_str()); gdi.fillRight(); int id = ci.classId; 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), 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); if (k%5 == 4) gdi.dropLine(1); gdi.dropLine(1.6); gdi.fillDown(); gdi.popX(); } gdi.dropLine(); gdi.pushX(); gdi.fillRight(); gdi.addButton("VisualizeDraw", "Visualisera startfältet...", ClassesCB); gdi.addButton("SaveDrawSettings", "Spara starttider", ClassesCB, "Spara inmatade tider i tävlingen utan att tilldela starttider."); gdi.addButton("DrawAllAdjust", "Ändra inställningar", ClassesCB, "Ändra grundläggande inställningar och gör en ny fördelning").setExtra(13); if (!cInfo.empty()) { gdi.addButton("DrawAdjust", "Uppdatera fördelning", ClassesCB, "Uppdatera fördelningen av starttider med hänsyn till manuella ändringar ovan"); gdi.disableInput("DrawAdjust"); } gdi.popX(); gdi.dropLine(3); gdi.fillRight(); if (!cInfo.empty()) { gdi.pushX(); RECT rc; rc.left = gdi.getCX(); rc.top = gdi.getCY(); rc.bottom = rc.top + gdi.getButtonHeight() + gdi.scaleLength(22) + gdi.getLineHeight(); gdi.setCX(rc.left + gdi.scaleLength(10)); gdi.setCY(rc.top + gdi.scaleLength(10)); gdi.addSelection("Method", 200, 200, 0, L"Metod:"); gdi.addItem("Method", lang.tl("Lottning"), DMRandom); gdi.addItem("Method", lang.tl("SOFT-lottning"), DMSOFT); gdi.selectItemByData("Method", getDefaultMethod(false)); gdi.addSelection("PairSize", 150, 200, 0, L"Tillämpa parstart:"); gdi.addItem("PairSize", getPairOptions()); gdi.selectItemByData("PairSize", 1); gdi.dropLine(0.9); gdi.addButton("DoDrawAll", "Utför lottning", ClassesCB); rc.right = gdi.getCX() + gdi.scaleLength(5); gdi.addRectangle(rc, colorLightGreen); gdi.setCX(rc.right + gdi.scaleLength(10)); } gdi.addButton("Cancel", "Avbryt", ClassesCB); gdi.fillDown(); gdi.dropLine(2); gdi.popX(); gdi.fillDown(); gdi.popX(); gdi.scrollToBottom(); gdi.updateScrollbars(); gdi.refresh(); } void TabClass::selectClass(gdioutput &gdi, int cid) { oe->fillCourses(gdi, "Courses", true); gdi.addItem("Courses", lang.tl("Ingen bana"), -2); if (cid==0) { gdi.restore("", true); gdi.disableInput("MultiCourse", true); gdi.enableInput("Courses"); gdi.enableEditControls(false); gdi.setText("Name", L""); gdi.selectItemByData("Courses", -2); gdi.check("AllowQuickEntry", true); if (gdi.hasField("FreeStart")) gdi.check("FreeStart", false); if (gdi.hasField("IgnoreStart")) gdi.check("IgnoreStart", false); if (gdi.hasField("DirectResult")) gdi.check("DirectResult", false); gdi.check("NoTiming", false); ClassId=cid; EditChanged=false; gdi.disableInput("Remove"); gdi.disableInput("Save"); return; } pClass pc = oe->getClass(cid); if (!pc) { selectClass(gdi, 0); return; } gdi.enableEditControls(true); gdi.enableInput("Remove"); gdi.enableInput("Save"); pc->synchronize(); gdi.setText("Name", pc->getName()); gdi.setText("ClassType", pc->getType()); gdi.setText("StartName", pc->getStart()); if (pc->getBlock()>0) gdi.selectItemByData("StartBlock", pc->getBlock()); else gdi.selectItemByData("StartBlock", -1); if (gdi.hasField("Status")) { vector< pair > out; size_t selected = 0; pc->getDCI().fillInput("Status", out, selected); gdi.addItem("Status", out); gdi.selectItemByData("Status", selected); } gdi.check("AllowQuickEntry", pc->getAllowQuickEntry()); gdi.check("NoTiming", pc->getNoTiming()); if (gdi.hasField("FreeStart")) gdi.check("FreeStart", pc->hasFreeStart()); if (gdi.hasField("IgnoreStart")) gdi.check("IgnoreStart", pc->ignoreStartPunch()); if (gdi.hasField("DirectResult")) gdi.check("DirectResult", pc->hasDirectResult()); ClassId=cid; if (pc->hasTrueMultiCourse()) { gdi.restore("", false); multiCourse(gdi, pc->getNumStages()); gdi.refresh(); gdi.addItem("Courses", lang.tl("Flera banor"), -3); gdi.selectItemByData("Courses", -3); gdi.disableInput("Courses"); gdi.check("CoursePool", pc->hasCoursePool()); 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_label"; gdi.setTextTranslate(strId, getCourseLabel(pc->hasCoursePool()), true); } if (gdi.hasData("SimpleMulti")) { bool hasStart = pc->getStartType(0) == STTime; gdi.setInputStatus("CommonStartTime", hasStart); gdi.check("CommonStart", hasStart); if (hasStart) gdi.setText("CommonStartTime", pc->getStartDataS(0)); else gdi.setText("CommonStartTime", makeDash(L"-")); } else { updateFairForking(gdi, pc); int nstage=pc->getNumStages(); gdi.setText("NStage", nstage); for (int k=0;kgetLegType(k)); gdi.selectItemByData((string("StartType")+legno).c_str(), pc->getStartType(k)); updateStartData(gdi, pc, k, false, true); gdi.setInputStatus(string("Restart")+legno, !pc->restartIgnored(k), true); gdi.setInputStatus(string("RestartRope")+legno, !pc->restartIgnored(k), true); if (gdi.hasField(string("Restart")+legno)) gdi.setText(string("Restart")+legno, pc->getRestartTimeS(k)); if (gdi.hasField(string("RestartRope")+legno)) gdi.setText(string("RestartRope")+legno, pc->getRopeTimeS(k)); if (gdi.hasField(string("MultiR")+legno)) gdi.selectItemByData((string("MultiR")+legno).c_str(), pc->getLegRunner(k)); } } } else { gdi.restore("", true); gdi.enableInput("MultiCourse", true); gdi.enableInput("Courses"); pCourse pcourse = pc->getCourse(); gdi.selectItemByData("Courses", pcourse ? pcourse->getId():-2); } gdi.selectItemByData("Classes", cid); ClassId=cid; EditChanged=false; } void TabClass::legSetup(gdioutput &gdi) { gdi.restore("RelaySetup"); gdi.pushX(); gdi.fillDown(); gdi.addString("", 10, "help:relaysetup"); gdi.dropLine(); gdi.addSelection("Predefined", 150, 200, MultiCB, L"Fördefinierade tävlingsformer:").ignore(true); oe->fillPredefinedCmp(gdi, "Predefined"); if (storedPredefined == oEvent::PredefinedTypes(-1)) { bool hasPatrol = oe->getMeOSFeatures().hasFeature(MeOSFeatures::Patrol); bool hasRelay = oe->getMeOSFeatures().hasFeature(MeOSFeatures::Relay); if (hasRelay) storedPredefined = oEvent::PRelay; else if (hasPatrol) storedPredefined = oEvent::PPatrol; else storedPredefined = oEvent::PNoSettings; } gdi.selectItemByData("Predefined", storedPredefined); gdi.fillRight(); gdi.addInput("NStage", storedNStage, 4, MultiCB, L"Antal sträckor:").ignore(true); gdi.addInput("StartTime", storedStart, 6, MultiCB, L"Starttid (HH:MM:SS):"); gdi.popX(); bool nleg; bool start; oe->setupRelayInfo(storedPredefined, nleg, start); gdi.setInputStatus("NStage", nleg); gdi.setInputStatus("StartTime", start); gdi.fillRight(); gdi.dropLine(3); gdi.addButton("SetNStage", "Verkställ", MultiCB); gdi.fillDown(); gdi.addButton("Cancel", "Avbryt", ClassesCB); gdi.popX(); } void TabClass::multiCourse(gdioutput &gdi, int nLeg) { currentStage=-1; bool simpleView = nLeg==1; bool showGuide = (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Relay) || oe->getMeOSFeatures().hasFeature(MeOSFeatures::Patrol)) && nLeg==0; if (nLeg == 0 && !showGuide) { pClass pc = oe->getClass(ClassId); if (pc) { pc->setNumStages(1); pc->setStartType(0, STDrawn, false); pc->forceShowMultiDialog(true); selectClass(gdi, ClassId); return; } } gdi.disableInput("MultiCourse", true); gdi.setRestorePoint(); gdi.fillDown(); gdi.newColumn(); int cx=gdi.getCX(), cy=gdi.getCY(); gdi.setCX(cx+10); gdi.setCY(cy+10); if (simpleView) { gdi.addString("", fontMediumPlus, "Gafflade banor"); } else { gdi.addString("", 2, "Flera banor / stafett / patrull / banpool"); gdi.addString("", 0, "Låt klassen ha mer än en bana eller sträcka"); gdi.dropLine(); } gdi.setRestorePoint("RelaySetup"); if (showGuide) { legSetup(gdi); RECT rc; rc.left = cx; rc.right = gdi.getWidth()+10; rc.bottom = gdi.getCY()+10; rc.top = cy; gdi.addRectangle(rc, colorLightGreen, true, false).set3D(true).setColor2(colorLightCyan); } else if (simpleView) { gdi.fillRight(); gdi.pushX(); gdi.setData("SimpleMulti", 1); gdi.dropLine(); gdi.addCheckbox("CommonStart", "Gemensam start", MultiCB, false); //gdi.dropLine(-1); gdi.addInput("CommonStartTime", L"", 10, 0, L""); gdi.fillDown(); gdi.popX(); gdi.dropLine(2); gdi.addCheckbox("CoursePool", "Använd banpool", MultiCB, false, "Knyt löparna till banor från en pool vid målgång."); gdi.addButton("OneCourse", "Endast en bana", MultiCB, "Använd endast en bana i klassen"); gdi.setRestorePoint("Courses"); selectCourses(gdi, 0); RECT rc; rc.left = cx; rc.right = gdi.getWidth()+10; rc.bottom = gdi.getCY()+10; rc.top = cy; gdi.addRectangle(rc, colorLightBlue, true, false).set3D(true); } else { gdi.pushX(); gdi.fillRight(); gdi.addButton("ChangeLeg", "Ändra klassinställningar...", MultiCB, "Starta en guide som hjälper dig göra klassinställningar"); gdi.fillDown(); gdi.popX(); gdi.dropLine(2); gdi.dropLine(0.5); int headYPos=gdi.getCY(); gdi.dropLine(1.2); vector< pair > legs; legs.reserve(nLeg); for (int j=0;jgetMeOSFeatures().hasFeature(MeOSFeatures::MultipleRaces); bool hasRelay = oe->getMeOSFeatures().hasFeature(MeOSFeatures::Relay); for (int k=0;k0) pc=oe->getClass(cid); else { pc=oe->addClass(name); create=true; } if (!pc) throw std::exception("Class not found."); ClassId=pc->getId(); pc->setName(name); if (gdi.hasField("StartName")) pc->setStart(gdi.getText("StartName")); if (gdi.hasField("ClassType")) pc->setType(gdi.getText("ClassType")); if (gdi.hasField("StartBlock")) pc->setBlock(gdi.getTextNo("StartBlock")); if (gdi.hasField("Status")) { pc->getDI().setEnum("Status", gdi.getSelectedItem("Status").first); } if (gdi.hasField("CoursePool")) pc->setCoursePool(gdi.isChecked("CoursePool")); 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")); if (gdi.hasField("FreeStart")) pc->setFreeStart(gdi.isChecked("FreeStart")); if (gdi.hasField("IgnoreStart")) pc->setIgnoreStartPunch(gdi.isChecked("IgnoreStart")); if (gdi.hasField("DirectResult")) { bool withDirect = gdi.isChecked("DirectResult"); if (withDirect && !pc->hasDirectResult() && !hasWarnedDirect) { if (gdi.ask(L"warning:direct_result")) hasWarnedDirect = true; else withDirect = false; } pc->setDirectResult(withDirect); } int crs = gdi.getSelectedItem("Courses").first; if (crs==0) { //Skapa ny bana... pCourse pcourse=oe->addCourse(L"Bana " + name); pc->setCourse(pcourse); pc->synchronize(); return; } else if (crs==-2) pc->setCourse(0); else if (crs > 0) pc->setCourse(oe->getCourse(crs)); if (pc->hasMultiCourse()) { if (gdi.hasData("SimpleMulti")) { bool sim = gdi.isChecked("CommonStart"); if (sim) { pc->setStartType(0, STTime, true); if (!warnDrawStartTime(gdi, gdi.getText("CommonStartTime"))) pc->setStartData(0, gdi.getText("CommonStartTime")); } else { pc->setStartType(0, STDrawn, true); } } else { int nstage=pc->getNumStages(); bool needAdjust = false; for (int k=0;ksetLegType(k, LegTypes(gdi.getSelectedItem(string("LegType")+legno).first)); pc->setStartType(k, StartTypes(gdi.getSelectedItem(string("StartType")+legno).first), true); if (pc->getStartType(k) == STChange) { int val = gdi.getSelectedItem(string("StartData")+legno).first; if (val <= -10) pc->setStartData(k, val + 10); else pc->setStartData(k, 0); } else { pc->setStartData(k, gdi.getText(string("StartData")+legno)); } string key; key = string("Restart")+legno; if (gdi.hasField(key)) pc->setRestartTime(k, gdi.getText(key)); key = string("RestartRope")+legno; if (gdi.hasField(key)) pc->setRopeTime(k, gdi.getText(key)); key = string("MultiR")+legno; if (gdi.hasField(key)) { int mr = gdi.getSelectedItem(key).first; if (pc->getLegRunner(k) != mr) needAdjust = true; pc->setLegRunner(k, mr); } } if (needAdjust) oe->adjustTeamMultiRunners(pc); } } pc->addClassDefaultFee(false); pc->updateChangedCoursePool(); pc->synchronize(); oe->reCalculateLeaderTimes(pc->getId()); set cls; cls.insert(pc->getId()); oe->reEvaluateAll(cls, true); oe->fillClasses(gdi, "Classes", oEvent::extraDrawn, oEvent::filterNone); EditChanged=false; if (!skipReload) { ClassId = 0; selectClass(gdi, pc->getId()); } if (checkValid) { // Check/warn that starts blocks are set up correctly vector b; vector s; oe->getStartBlocks(b, s); oe->sanityCheck(gdi, false, pc->getId()); } } struct ButtonData { ButtonData(const char *idIn, const char *labelIn, bool glob) : id(idIn), label(labelIn), global(glob) {} string id; string label; bool global; }; bool TabClass::loadPage(gdioutput &gdi) { if (!gdi.hasData("ClassPageLoaded")) hasWarnedStartTime = false; oe->checkDB(); oe->checkNecessaryFeatures(); gdi.selectTab(tabId); clearPage(gdi, false); int xp=gdi.getCX(); const int button_w=gdi.scaleLength(90); string switchMode; switchMode=tableMode ? "Formulärläge" : "Tabelläge"; gdi.addButton(2, 2, button_w, "SwitchMode", switchMode, ClassesCB, "Välj vy", false, false).fixedCorner(); if (tableMode) { Table *tbl=oe->getClassTB(); gdi.addTable(tbl, xp, 30); return true; } if (showForkingGuide) { try { defineForking(gdi, false); } catch(...) { showForkingGuide = false; throw; } return true; } ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); bool showAdvanced = oe->getPropertyInt("AdvancedClassSettings", 0) != 0; gdi.addString("", boldLarge, "Klasser"); gdi.fillDown(); gdi.addListBox("Classes", 200, showAdvanced ? 512 : 420, ClassesCB, L"").isEdit(false).ignore(true); gdi.setTabStops("Classes", 185); oe->fillClasses(gdi, "Classes", oEvent::extraDrawn, oEvent::filterNone); gdi.newColumn(); gdi.dropLine(2); gdi.fillRight(); gdi.pushX(); gdi.addInput("Name", L"", 14, ClassesCB, L"Klassnamn:"); bool sameLineNameCourse = true; if (showAdvanced) { gdi.addCombo("ClassType", 100, 300, 0, L"Typ:"); oe->fillClassTypes(gdi, "ClassType"); sameLineNameCourse = false; } if (showMulti(false) || !sameLineNameCourse) { gdi.dropLine(3); gdi.popX(); } gdi.addSelection("Courses", 120, 400, ClassesCB, L"Bana:"); oe->fillCourses(gdi, "Courses", true); gdi.addItem("Courses", lang.tl("Ingen bana"), -2); if (showMulti(false)) { gdi.dropLine(0.9); if (showMulti(true)) { gdi.addButton("MultiCourse", "Flera banor/stafett...", ClassesCB); } else { gdi.addButton("MultiCourse", "Gafflade banor...", ClassesCB); } gdi.disableInput("MultiCourse"); } gdi.popX(); if (showAdvanced) { gdi.dropLine(3); gdi.addCombo("StartName", 120, 300, 0, L"Startnamn:"); oe->fillStarts(gdi, "StartName"); gdi.addSelection("StartBlock", 80, 300, 0, L"Startblock:"); for (int k=1;k<=100;k++) { gdi.addItem("StartBlock", itow(k), k); } gdi.popX(); gdi.dropLine(3); gdi.addSelection("Status", 200, 300, 0, L"Status:"); } gdi.popX(); gdi.dropLine(3.5); gdi.addCheckbox("AllowQuickEntry", "Tillåt direktanmälan", 0); gdi.addCheckbox("NoTiming", "Utan tidtagning", 0); if (showAdvanced) { gdi.dropLine(2); gdi.popX(); gdi.addCheckbox("FreeStart", "Fri starttid", 0, false, "Klassen lottas inte, startstämpling"); gdi.addCheckbox("IgnoreStart", "Ignorera startstämpling", 0, false, "Uppdatera inte starttiden vid startstämpling"); gdi.dropLine(2); gdi.popX(); gdi.addCheckbox("DirectResult", "Resultat vid målstämpling", 0, false, "help:DirectResult"); } gdi.dropLine(2); gdi.popX(); 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 (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 (showAdvanced && oe->getMeOSFeatures().hasFeature(MeOSFeatures::Vacancy)) { vector rr; oe->getRunners(0, 0, rr, false); bool hasVac = false; for (size_t k = 0; k < rr.size(); k++) { if (rr[k]->isVacant()) { hasVac = true; break; } } if (hasVac) 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); 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(0.5); gdi.fillRight(); gdi.addButton("Save", "Spara", ClassesCB).setDefault(); gdi.disableInput("Save"); gdi.addButton("Remove", "Radera", ClassesCB); gdi.disableInput("Remove"); gdi.addButton("Add", "Ny klass", ClassesCB); gdi.popX(); gdi.fillDown(); gdi.dropLine(3); gdi.addCheckbox("UseAdvanced", "Visa avancerade funktioner", ClassesCB, showAdvanced).isEdit(false); gdi.setOnClearCb(ClassesCB); gdi.setRestorePoint(); gdi.setCX(xp); gdi.setCY(gdi.getHeight()); gdi.addString("", 10, "help:26963"); selectClass(gdi, ClassId); EditChanged=false; gdi.refresh(); return true; } bool TabClass::showMulti(bool singleOnly) const { const MeOSFeatures &mf = oe->getMeOSFeatures(); if (!singleOnly) return mf.hasFeature(MeOSFeatures::Relay) || mf.hasFeature(MeOSFeatures::Patrol) || mf.hasFeature(MeOSFeatures::ForkedIndividual); else return mf.hasFeature(MeOSFeatures::Relay) || mf.hasFeature(MeOSFeatures::Patrol) || mf.hasFeature(MeOSFeatures::MultipleRaces); } static int classSettingsCB(gdioutput *gdi, int type, void *data) { TabClass &tc = dynamic_cast(*gdi->getTabs().get(TClassTab)); static wstring lastStart = L"Start 1"; if (type==GUI_INPUT) { InputInfo ii=*(InputInfo *)data; if (ii.id.substr(0,4) == "Strt") { lastStart = ii.text; } } else if (type == GUI_FOCUS) { InputInfo ii=*(InputInfo *)data; if (ii.id.substr(0,4) == "Strt") { if (ii.text.empty()) { gdi->setText(ii.id, lastStart); gdi->setInputFocus(ii.id, true); } } } else if (type == GUI_BUTTON) { ButtonInfo bi = *(ButtonInfo*)data; if (bi.id == "SaveCS") { tc.saveClassSettingsTable(*gdi); } } else if (type == GUI_CLEAR) { tc.saveClassSettingsTable(*gdi); return 1; } return 0; } void TabClass::saveClassSettingsTable(gdioutput &gdi) { set modifiedFee; bool modifiedBib = false; saveClassSettingsTable(gdi, modifiedFee, modifiedBib); oe->synchronize(true); if (gdi.hasField("BibGap")) { int gap = gdi.getTextNo("BibGap"); if (oe->getBibClassGap() != gap) { oe->setBibClassGap(gap); modifiedBib = true; } } if (!modifiedFee.empty() && oe->getNumRunners() > 0) { bool updateFee = gdi.ask(L"ask:changedclassfee"); if (updateFee) oe->applyEventFees(false, true, false, modifiedFee); } if (modifiedBib && gdi.ask(L"Vill du uppdatera alla nummerlappar?")) { oe->addAutoBib(); } oe->synchronize(true); gdi.sendCtrlMessage("Cancel"); } void TabClass::prepareForDrawing(gdioutput &gdi) { clearPage(gdi, false); gdi.addString("", 2, "Klassinställningar"); int baseLine = gdi.getCY(); gdi.addString("", 10, "help:59395"); gdi.pushX(); int by = gdi.getCY(); gdi.setCX(gdi.getWidth()); gdi.setCY(baseLine); gdi.addString("", 10, "help:59395_more"); gdi.setCY(max(gdi.getCY(), by)); gdi.popX(); gdi.dropLine(); if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Bib)) { gdi.fillRight(); gdi.addString("", 0, "Antal reserverade nummerlappsnummer mellan klasser:"); gdi.dropLine(-0.1); gdi.addInput("BibGap", itow(oe->getBibClassGap()), 5); gdi.popX(); gdi.dropLine(1.5); gdi.fillDown(); } getClassSettingsTable(gdi, classSettingsCB); gdi.dropLine(); gdi.fillRight(); gdi.addButton("SaveCS", "Spara", classSettingsCB); gdi.addButton("Cancel", "Avbryt", ClassesCB); gdi.refresh(); } void TabClass::drawDialog(gdioutput &gdi, DrawMethod method, const oClass &pc) { oe->setProperty("DefaultDrawMethod", method); if (lastDrawMethod == method) return; if (lastDrawMethod == DMPursuit && method == DMReversePursuit) return; if (lastDrawMethod == DMReversePursuit && method == DMPursuit) return; if (lastDrawMethod == DMRandom && method == DMSOFT) return; if (lastDrawMethod == DMSOFT && method == DMRandom) return; int firstStart = 3600, interval = 120, vac = _wtoi(lastNumVac.c_str()); int pairSize = lastPairSize; if (gdi.hasField("FirstStart")) firstStart = oe->getRelativeTime(gdi.getText("FirstStart")); else if (!lastFirstStart.empty()) firstStart = oe->getRelativeTime(lastFirstStart); if (gdi.hasField("Interval")) interval = convertAbsoluteTimeMS(gdi.getText("Interval")); else if (!lastInterval.empty()) interval = convertAbsoluteTimeMS(lastInterval); if (gdi.hasField("PairSize")) { pairSize = gdi.getSelectedItem("PairSize").first; } gdi.restore("MultiDayDraw", false); const bool multiDay = oe->hasPrevStage(); if (method == DMSeeded) { gdi.addString("", 10, "help:seeding_info"); gdi.dropLine(1); gdi.pushX(); gdi.fillRight(); ListBoxInfo &seedmethod = gdi.addSelection("SeedMethod", 120, 100, 0, L"Seedningskälla:"); vector< pair > methods; oClass::getSeedingMethods(methods); gdi.addItem("SeedMethod", methods); if (lastSeedMethod == -1) gdi.selectFirstItem("SeedMethod"); else gdi.selectItemByData("SeedMethod", lastSeedMethod); seedmethod.setSynchData(&lastSeedMethod); gdi.addInput("SeedGroups", lastSeedGroups, 32, 0, L"Seedningsgrupper:", L"Ange en gruppstorlek (som repeteras) eller flera kommaseparerade gruppstorlekar"). setSynchData(&lastSeedGroups); gdi.fillDown(); gdi.popX(); gdi.dropLine(3); gdi.addCheckbox("PreventClubNb", "Hindra att deltagare från samma klubb startar på angränsande tider", 0, lastSeedPreventClubNb).setSynchData(&lastSeedPreventClubNb); gdi.addCheckbox("ReverseSeedning", "Låt de bästa start först", 0, lastSeedReverse). setSynchData(&lastSeedReverse); } else { gdi.popX(); gdi.addString("", 10, "help:41641"); gdi.dropLine(1); } if (method == DMRandom || method == DMSOFT || method == DMPursuit || method == DMReversePursuit || method == DMSeeded) { gdi.addSelection("PairSize", 150, 200, 0, L"Tillämpa parstart:").setSynchData(&lastPairSize); gdi.addItem("PairSize", getPairOptions()); gdi.selectItemByData("PairSize", pairSize); } gdi.fillRight(); gdi.addInput("FirstStart", oe->getAbsTime(firstStart), 10, 0, L"Första start:").setSynchData(&lastFirstStart); if (method == DMPursuit || method == DMReversePursuit) { gdi.addInput("MaxAfter", lastMaxAfter, 10, 0, L"Maxtid efter:", L"Maximal tid efter ledaren för att delta i jaktstart").setSynchData(&lastMaxAfter); gdi.addInput("TimeRestart", oe->getAbsTime(firstStart + 3600), 8, 0, L"Första omstartstid:"); gdi.addInput("ScaleFactor", lastScaleFactor, 8, 0, L"Tidsskalning:").setSynchData(&lastScaleFactor); } if (method != DMSimultaneous) 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); if ((method == DMRandom || method == DMSOFT || method == DMSeeded) && pc.getNumStages() > 1 && pc.getClassType() != oClassPatrol) { gdi.addSelection("Leg", 90, 100, 0, L"Sträcka:", L"Sträcka att lotta"); for (unsigned k = 0; k 10) defaultMethod = getDefaultMethod(hasMulti); gdi.selectItemByData("Method", defaultMethod); if (gdi.hasField("Vacanses")) { gdi.setInputStatus("Vacanses", !hasMulti); gdi.setInputStatus("HandleBibs", !hasMulti); if (hasMulti) { gdi.check("HandleBibs", false); gdi.setInputStatus("Bib", false); } } if (gdi.hasField("DoDrawBefore")) { gdi.setInputStatus("DoDrawBefore", !hasMulti); gdi.setInputStatus("DoDrawAfter", !hasMulti); } } void TabClass::pursuitDialog(gdioutput &gdi) { clearPage(gdi, false); gdi.addString("", boldLarge, "Jaktstart"); gdi.dropLine(); vector cls; oe->getClasses(cls, true); gdi.setRestorePoint("Pursuit"); gdi.pushX(); gdi.fillRight(); 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:"); gdi.dropLine(4); gdi.popX(); gdi.fillDown(); //xxx //gdi.addCheckbox("Pairwise", "Tillämpa parstart", 0, false); gdi.addSelection("PairSize", 150, 200, 0, L"Tillämpa parstart:"); gdi.addItem("PairSize", getPairOptions()); gdi.selectItemByData("PairSize", 1); int cx = gdi.getCX(); int cy = gdi.getCY(); const int len5 = gdi.scaleLength(5); const int len40 = gdi.scaleLength(30); const int len200 = gdi.scaleLength(200); gdi.addString("", cy, cx, 1, "Välj klasser"); gdi.addString("", cy, cx + len200 + len40, 1, "Första starttid"); cy += gdi.getLineHeight()*2; for (size_t k = 0; k::iterator st = pSettings.find(cls[k]->getId()); if (st == pSettings.end()) { pSettings.insert(make_pair(cls[k]->getId(), PursuitSettings(*cls[k]))); st = pSettings.find(cls[k]->getId()); } PursuitSettings &ps = st->second; int fs = cls[k]->getDrawFirstStart(); if (fs > 0) ps.firstTime = fs; ButtonInfo &bi = gdi.addCheckbox(cx, cy + len5, "PLUse" + itos(k), "", ClassesCB, ps.use); bi.setExtra(cls[k]->getId()); gdi.addStringUT(cy, cx + len40, 0, cls[k]->getName(), len200); gdi.addInput(cx + len200 + len40, cy, "First" + itos(k), oe->getAbsTime(ps.firstTime), 8); if (!ps.use) gdi.disableInput(("First" + itos(k)).c_str()); cy += int(gdi.getLineHeight()*1.8); } gdi.dropLine(); gdi.fillRight(); gdi.addButton("SelectAllNoneP", "Välj alla", ClassesCB).setExtra(1); gdi.addButton("SelectAllNoneP", "Välj ingen", ClassesCB).setExtra(0); gdi.popX(); gdi.dropLine(3); RECT rc; rc.left = gdi.getCX(); rc.top = gdi.getCY(); rc.bottom = rc.top + gdi.getButtonHeight() + gdi.scaleLength(17); gdi.setCX(rc.left + gdi.scaleLength(10)); gdi.setCY(rc.top + gdi.scaleLength(10)); gdi.addButton("DoPursuit", "Jaktstart", ClassesCB).setDefault().setExtra(1); gdi.addButton("DoPursuit", "Omvänd jaktstart", ClassesCB).setExtra(2); rc.right = gdi.getCX() + gdi.scaleLength(5); gdi.addRectangle(rc, colorLightGreen); gdi.setCX(rc.right + gdi.scaleLength(10)); gdi.addButton("SavePursuit", "Spara starttider", ClassesCB, "Spara inmatade tider i tävlingen utan att tilldela starttider."); gdi.addButton("CancelPursuit", "Återgå", ClassesCB).setCancel(); gdi.refresh(); } void TabClass::showClassSelection(gdioutput &gdi, int &bx, int &by, GUICALLBACK classesCB) const { gdi.pushY(); int cx = gdi.getCX(); int width = gdi.scaleLength(230); gdi.addListBox("Classes", 200, 400, classesCB, L"Klasser:", L"", true); gdi.setTabStops("Classes", 185); gdi.fillRight(); gdi.pushX(); gdi.addButton("SelectAll", "Välj allt", ClassesCB, "Välj alla klasser").isEdit(true); gdi.addButton("SelectMisses", "Saknad starttid", ClassesCB, "Välj klasser där någon löpare saknar starttid").isEdit(true); gdi.dropLine(2.3); gdi.popX(); gdi.addButton("SelectUndrawn", "Ej lottade", ClassesCB, "Välj klasser där alla löpare saknar starttid").isEdit(true); gdi.fillDown(); gdi.addButton("SelectNone", "Välj inget", ClassesCB, "Avmarkera allt").isEdit(true); gdi.popX(); vector blocks; vector starts; oe->getStartBlocks(blocks, starts); map sstart; for (size_t k = 0; k < starts.size(); k++) { sstart.insert(make_pair(starts[k], k)); } if (sstart.size() > 1) { gdi.fillRight(); int cnt = 0; for (map::reverse_iterator it = sstart.rbegin(); it != sstart.rend(); ++it) { if ((cnt & 1)==0 && cnt>0) { gdi.dropLine(2); gdi.popX(); } wstring name = it->first; if (name.empty()) name = lang.tl(L"övriga"); gdi.addButton("SelectStart", L"Välj X#" + name, ClassesCB, L"").isEdit(true).setExtra(it->second); cnt++; } gdi.dropLine(2.5); gdi.popX(); gdi.fillDown(); } oe->fillClasses(gdi, "Classes", oEvent::extraDrawn, oEvent::filterNone); by = gdi.getCY()+gdi.getLineHeight(); bx = gdi.getCX(); //gdi.newColumn(); gdi.setCX(cx+width); gdi.popY(); } void TabClass::enableLoadSettings(gdioutput &gdi) { if (!gdi.hasField("LoadSettings")) return; set sel; gdi.getSelection("Classes", sel); bool ok = !sel.empty(); gdi.setInputStatus("PrepareDrawAll", ok); gdi.setInputStatus("EraseStartAll", ok); ok = false; for (set::iterator it = sel.begin(); it != sel.end(); ++it) { pClass pc = oe->getClass(*it); if (pc) { if (pc->getDrawFirstStart() > 0 && pc->getDrawInterval() > 0) { ok = true; break; } } } gdi.setInputStatus("LoadSettings", ok); } void TabClass::simultaneous(int classId, const wstring &time) { pClass pc = oe->getClass(classId); if (!pc) throw exception(); pc->getNumStages(); if (pc->getNumStages() == 0) { pCourse crs = pc->getCourse(); pc->setNumStages(1); if (crs) pc->addStageCourse(0, crs->getId()); } pc->setStartType(0, STTime, false); pc->setStartData(0, time); pc->synchronize(true); pc->forceShowMultiDialog(false); oe->reCalculateLeaderTimes(pc->getId()); set cls; cls.insert(pc->getId()); oe->reEvaluateAll(cls, true); } const wchar_t *TabClass::getCourseLabel(bool pool) { if (pool) return L"Banpool:"; else return L"Sträckans banor:"; } void TabClass::selectCourses(gdioutput &gdi, int legNo) { gdi.restore("Courses", false); gdi.setRestorePoint("Courses"); wchar_t bf[128]; pClass pc=oe->getClass(ClassId); if (!pc) { gdi.refresh(); return; } currentStage = legNo; gdi.dropLine(); gdi.pushX(); gdi.fillRight(); bool simpleView = pc->getNumStages() == 1; if (!simpleView) { swprintf_s(bf, lang.tl("Banor för %s, sträcka %d").c_str(), pc->getName().c_str(), legNo+1); gdi.addStringUT(1, bf); ButtonInfo &bi1 = gdi.addButton("@Course" + itos(legNo-1), "<< Föregående", MultiCB); if (legNo<=0) gdi.disableInput(bi1.id.c_str()); ButtonInfo &bi2 = gdi.addButton("@Course" + itos(legNo+1), "Nästa >>", MultiCB); if (unsigned(legNo + 1) >= pc->getNumStages()) gdi.disableInput(bi2.id.c_str()); gdi.popX(); gdi.dropLine(2.5); } gdi.fillRight(); int x1=gdi.getCX(); gdi.addListBox("StageCourses", 180, 200, MultiCB, getCourseLabel(pc->hasCoursePool())).ignore(true); pc->fillStageCourses(gdi, currentStage, "StageCourses"); int x2=gdi.getCX(); gdi.fillDown(); gdi.addListBox("MCourses", 180, 200, MultiCB, L"Banor:").ignore(true); oe->fillCourses(gdi, "MCourses", true); gdi.setCX(x1); gdi.fillRight(); gdi.addButton("MRemove", "Ta bort markerad >>", MultiCB); gdi.setCX(x2); gdi.fillDown(); gdi.addButton("MAdd", "<< Lägg till", MultiCB); gdi.setCX(x1); gdi.refresh(); if (pc->getNumStages() > 1) gdi.scrollTo(gdi.getCX(), gdi.getCY()); } void TabClass::updateFairForking(gdioutput &gdi, pClass pc) const { vector< vector > forks; vector< vector > forksC; set< pair > unfairLegs; if (pc->checkForking(forksC, forks, unfairLegs)) { BaseInfo *bi = gdi.setText("FairForking", gdi.getText("FairForking"), false); TextInfo &text = dynamic_cast(*bi); text.setColor(colorGreen); gdi.setText("FairForking", lang.tl("The forking is fair."), true); } else { BaseInfo *bi = gdi.setText("FairForking", gdi.getText("FairForking"), false); TextInfo &text = dynamic_cast(*bi); text.setColor(colorRed); gdi.setText("FairForking", lang.tl("The forking is not fair."), true); } } void TabClass::defineForking(gdioutput &gdi, bool clearSettings) { pClass pc = oe->getClass(ClassId); if (clearSettings) { forkingSetup.clear(); forkingSetup.resize(pc->getNumStages()); } else if (forkingSetup.size() != pc->getNumStages()) throw meosException("Internal error"); showForkingGuide = true; gdi.clearPage(false); int tx = gdi.getCX(); int ty = gdi.getCY(); gdi.dropLine(2); gdi.pushY(); gdi.addListBox("AllCourses", 180, 300, 0, L"Banor:", L"", true); oe->fillCourses(gdi, "AllCourses", true); int bxp = gdi.getCX(); int byp = gdi.getCY(); gdi.fillDown(); gdi.newColumn(); gdi.popY(); gdi.addListBox("AllStages", 180, 300, MultiCB, L"Legs:", L"", true); int ns = pc->getNumStages(); gdi.newColumn(); gdi.fillDown(); gdi.popY(); gdi.addButton("AssignCourses", "Assign selected courses to selected legs", MultiCB); gdi.disableInput("AssignCourses"); gdi.dropLine(); gdi.addString("", boldText, "Forking setup"); gdi.dropLine(0.5); for (int k = 0; k < ns; k++) { LegTypes lt = pc->getLegType(k); if (lt != LTIgnore) { gdi.addString("leg"+ itos(k), 0, "Leg X: Do not modify.#" + itos(k+1)); gdi.addItem("AllStages", lang.tl("Leg X#" + itos(k+1)), k); } } gdi.dropLine(); gdi.fillRight(); gdi.addButton("ApplyForking", "Calculate and apply forking", MultiCB); gdi.addButton("Cancel", "Avbryt", ClassesCB).setCancel(); gdi.disableInput("ApplyForking"); gdi.setCX(bxp); gdi.setCY(byp); gdi.fillDown(); gdi.addButton("ClearCourses", "Clear selections", MultiCB); gdi.addString("", 10, "help:assignforking"); gdi.addString("", ty, tx, boldLarge, L"Assign courses and apply forking to X#" + pc->getName()); if (!clearSettings) gdi.sendCtrlMessage("AssignCourses"); gdi.refresh(); } void TabClass::getClassSettingsTable(gdioutput &gdi, GUICALLBACK cb) { vector cls; oe->getClasses(cls, true); int yp = gdi.getCY(); int a = gdi.scaleLength(160); int b = gdi.scaleLength(250); int c = gdi.scaleLength(300); int d = gdi.scaleLength(350); int e = gdi.scaleLength(510); int et = gdi.scaleLength(605); int f = gdi.scaleLength(510); int g = gdi.scaleLength(535); int ek1 = 0, ekextra = 0; bool useEco = oe->getMeOSFeatures().hasFeature(MeOSFeatures::Economy); gdi.setOnClearCb(cb); if (useEco) { ek1 = gdi.scaleLength(70); ekextra = gdi.scaleLength(35); d += 4 * ek1 + ekextra; e += 4 * ek1 + ekextra; et += 4 * ek1 + ekextra; f += 4 * ek1 + ekextra; g += 4 * ek1 + ekextra; gdi.addString("", yp, c+ek1, 1, "Avgift"); gdi.addString("", yp, c+2*ek1, 1, "Sen avgift"); gdi.addString("", yp, c+3*ek1, 1, "Red. avgift"); gdi.addString("", yp, c+4*ek1, 1, "Sen red. avgift"); } gdi.addString("", yp, gdi.getCX(), 1, "Klass"); gdi.addString("", yp, a, 1, "Start"); gdi.addString("", yp, b, 1, "Block"); gdi.addString("", yp, c, 1, "Index"); gdi.addString("", yp, d, 1, "Bana"); const bool useBibs = oe->getMeOSFeatures().hasFeature(MeOSFeatures::Bib); const bool useTeam = oe->hasTeam(); vector< pair > bibOptions; vector< pair > bibTeamOptions; if (useBibs) { gdi.addString("", yp, e, 1, "Nummerlapp"); bibOptions.push_back(make_pair(lang.tl("Manuell"), 0)); bibOptions.push_back(make_pair(lang.tl("Löpande"), 1)); bibOptions.push_back(make_pair(lang.tl("Ingen"), 2)); int bibW = gdi.scaleLength(100); if (useTeam) { gdi.addString("", yp, et, 1, "Lagmedlem"); bibTeamOptions.push_back(make_pair(lang.tl("Oberoende"), BibFree)); bibTeamOptions.push_back(make_pair(lang.tl("Samma"), BibSame)); bibTeamOptions.push_back(make_pair(lang.tl("Ökande"), BibAdd)); bibTeamOptions.push_back(make_pair(lang.tl("Sträcka"), BibLeg)); bibW += gdi.scaleLength(85); } f += bibW; g += bibW; } gdi.addString("", yp, f, 1, "Direktanmälan"); vector< pair > arg; oe->fillCourses(arg, true); for (size_t k = 0; k < cls.size(); k++) { pClass it = cls[k]; int cyp = gdi.getCY(); string id = itos(it->getId()); gdi.addStringUT(0, it->getName(), 0); gdi.addInput(a, cyp, "Strt"+id, it->getStart(), 7, cb); wstring blk = it->getBlock()>0 ? itow(it->getBlock()) : L""; gdi.addInput(b, cyp, "Blck"+id, blk, 4); gdi.addInput(c, cyp, "Sort"+id, itow(it->getDCI().getInt("SortIndex")), 4); if (useEco) { gdi.addInput(c + ek1, cyp, "Fee"+id, oe->formatCurrency(it->getDCI().getInt("ClassFee")), 5); gdi.addInput(c + 2*ek1, cyp, "LateFee"+id, oe->formatCurrency(it->getDCI().getInt("HighClassFee")), 5); gdi.addInput(c + 3*ek1, cyp, "RedFee"+id, oe->formatCurrency(it->getDCI().getInt("ClassFeeRed")), 5); gdi.addInput(c + 4*ek1, cyp, "RedLateFee"+id, oe->formatCurrency(it->getDCI().getInt("HighClassFeeRed")), 5); } string crs = "Cors"+id; gdi.addSelection(d, cyp, crs, 150, 400); if (it->hasTrueMultiCourse()) { gdi.addItem(crs, lang.tl("Flera banor"), -5); gdi.selectItemByData(crs.c_str(), -5); gdi.disableInput(crs.c_str()); } else { gdi.addItem(crs, arg); gdi.selectItemByData(crs.c_str(), it->getCourseId()); } if (useBibs) { gdi.addCombo(e, cyp, "Bib" + id, 90, 100, 0, L"", L"Ange löpande numrering eller första nummer i klassen."); gdi.addItem("Bib" + id, bibOptions); wstring bib = it->getDCI().getString("Bib"); AutoBibType bt = it->getAutoBibType(); if (bt != AutoBibExplicit) gdi.selectItemByData("Bib"+ id, bt); else gdi.setText("Bib"+ id, bib); if (useTeam && it->getNumDistinctRunners() > 1) { gdi.addSelection(et, cyp, "BibTeam" + id, 80, 100, 0, L"", L"Ange relation mellan lagets och deltagarnas nummerlappar."); gdi.addItem("BibTeam" + id, bibTeamOptions); gdi.selectItemByData("BibTeam" + id, it->getBibMode()); } } gdi.addCheckbox(g, cyp, "Dirc"+id, " ", 0, it->getAllowQuickEntry()); gdi.dropLine(-0.3); } } void TabClass::saveClassSettingsTable(gdioutput &gdi, set &classModifiedFee, bool &modifiedBib) { vector cls; oe->getClasses(cls, true); classModifiedFee.clear(); modifiedBib = false; for (size_t k = 0; k < cls.size(); k++) { pClass it = cls[k]; string id = itos(it->getId()); wstring start = gdi.getText("Strt"+id); int block = gdi.getTextNo("Blck"+id); int sort = gdi.getTextNo("Sort"+id); if (gdi.hasField("Fee" + id)) { int fee = oe->interpretCurrency(gdi.getText("Fee"+id)); int latefee = oe->interpretCurrency(gdi.getText("LateFee"+id)); int feered = oe->interpretCurrency(gdi.getText("RedFee"+id)); int latefeered = oe->interpretCurrency(gdi.getText("RedLateFee"+id)); int oFee = it->getDCI().getInt("ClassFee"); int oLateFee = it->getDCI().getInt("HighClassFee"); int oFeeRed = it->getDCI().getInt("ClassFeeRed"); int oLateFeeRed = it->getDCI().getInt("HighClassFeeRed"); if (oFee != fee || oLateFee != latefee || oFeeRed != feered || oLateFeeRed != latefeered) classModifiedFee.insert(it->getId()); it->getDI().setInt("ClassFee", fee); it->getDI().setInt("HighClassFee", latefee); it->getDI().setInt("ClassFeeRed", feered); it->getDI().setInt("HighClassFeeRed", latefeered); } if (gdi.hasField("Bib" + id)) { ListBoxInfo lbi; bool mod = false; if (gdi.getSelectedItem("Bib" + id, lbi)) { mod = it->getDI().setString("Bib", getBibCode(AutoBibType(lbi.data), L"1")); } else { const wstring &v = gdi.getText("Bib" + id); mod = it->getDI().setString("Bib", v); } modifiedBib |= mod; if (gdi.hasField("BibTeam" + id)) { ListBoxInfo lbi_bib; if (gdi.getSelectedItem("BibTeam" + id, lbi_bib)) { if (it->getBibMode() != lbi_bib.data) modifiedBib = true; it->setBibMode(BibMode(lbi_bib.data)); } } } int courseId = 0; ListBoxInfo lbi; if (gdi.getSelectedItem("Cors"+id, lbi)) courseId = lbi.data; bool direct = gdi.isChecked("Dirc"+id); it->setStart(start); it->setBlock(block); if (courseId != -5) it->setCourse(oe->getCourse(courseId)); it->getDI().setInt("SortIndex", sort); it->setAllowQuickEntry(direct); } } wstring TabClass::getBibCode(AutoBibType bt, const wstring &key) { if (bt == AutoBibManual) return L""; else if (bt == AutoBibConsecutive) return L"*"; else if (bt == AutoBibNone) return L"-"; else return key; } void TabClass::updateStartData(gdioutput &gdi, pClass pc, int leg, bool updateDependent, bool forceWrite) { string sdKey = "StartData"+itos(leg); BaseInfo &sdataBase = gdi.getBaseInfo(sdKey.c_str()); StartTypes st = pc->getStartType(leg); if (st == STChange) { if (typeid(sdataBase) != typeid(ListBoxInfo)) { InputInfo sdII = dynamic_cast(sdataBase); gdi.removeControl(sdKey); gdi.addSelection(sdII.getX(), sdII.getY(), sdKey, sdII.getWidth(), 200, MultiCB); setParallelOptions(sdKey, gdi, pc, leg); } else if (forceWrite) { setParallelOptions(sdKey, gdi, pc, leg); } } else { if (typeid(sdataBase) != typeid(InputInfo)) { ListBoxInfo sdLBI = dynamic_cast(sdataBase); gdi.removeControl(sdKey); string val = "-"; gdi.addInput(sdLBI.getX(), sdLBI.getY(), sdKey, pc->getStartDataS(leg), 8, MultiCB); } else if (forceWrite) { gdi.setText(sdKey, pc->getStartDataS(leg), true); } gdi.setInputStatus(sdKey, !pc->startdataIgnored(leg)); } if (updateDependent) { for (size_t j = 0; j < pc->getNumStages(); j++) { if (j != leg && pc->getStartType(leg) == STChange) { setParallelOptions("StartData"+itos(j), gdi, pc, j); } } } } void TabClass::setParallelOptions(const string &sdKey, gdioutput &gdi, pClass pc, int legno) { int baseLeg = legno; while (baseLeg > 0 && pc->isParallel(baseLeg)) baseLeg--; baseLeg--; int sd = pc->getStartData(legno); vector< pair > opt; int defKey = 0; opt.push_back(make_pair(lang.tl("Ordnat"), 0)); for (int k = 0; k <= baseLeg; k++) { if (!pc->isOptional(k)) { opt.push_back(make_pair(lang.tl("Str. X#" + itos(k+1)), (k-legno) - 10)); if (sd == k-legno) defKey = sd - 10; } } if (defKey == 0) { pc->setStartData(legno, 0); } gdi.addItem(sdKey, opt); gdi.selectItemByData(sdKey, defKey); } void TabClass::updateSplitDistribution(gdioutput &gdi, int num, int tot) const { gdi.restore("SplitDistr", false); gdi.setRestorePoint("SplitDistr"); vector distr; for (int k = 0; k < num; k++) { int frac = tot / (num-k); distr.push_back(frac); tot -= frac; } sort(distr.rbegin(), distr.rend()); gdi.dropLine(); gdi.fillDown(); gdi.pushX(); for (size_t k = 0; k < distr.size(); k++) { int yp = gdi.getCY(); int xp = gdi.getCX(); gdi.addString("", yp, xp, 0, "Klass X:#" + itos(k+1)); gdi.addInput(xp + gdi.scaleLength(100), yp, "CLS" + itos(k), itow(distr[k]), 4); gdi.popX(); } gdi.dropLine(1.5); gdi.fillRight(); gdi.popX(); gdi.addButton("DoSplit", "Dela", ClassesCB).setDefault(); gdi.addButton("Cancel", "Avbryt", ClassesCB).setCancel(); gdi.popX(); gdi.refresh(); } DrawMethod TabClass::getDefaultMethod(bool allowPursuit) const { int dm = oe->getPropertyInt("DefaultDrawMethod", DMSOFT); if (!allowPursuit) { if (dm == DMRandom) return DMRandom; else return DMSOFT; } else { switch (dm) { case DMRandom: return DMRandom; case DMPursuit: return DMPursuit; case DMReversePursuit: return DMReversePursuit; case DMSeeded: return DMSeeded; default: return DMSOFT; } } } vector< pair > TabClass::getPairOptions() { vector< pair > res; res.push_back(make_pair(lang.tl("Ingen parstart"), 1)); res.push_back(make_pair(lang.tl("Parvis (två och två)"), 2)); for (int j = 3; j <= 10; j++) { res.push_back(make_pair(lang.tl("X och Y[N by N]#" + itos(j) + "#" + itos(j)), j)); } return res; } void TabClass::readDrawInfo(gdioutput &gdi, DrawInfo &drawInfoOut) { drawInfoOut.maxCommonControl = gdi.getSelectedItem("MaxCommonControl").first; drawInfoOut.maxVacancy=gdi.getTextNo("VacancesMax"); drawInfoOut.minVacancy=gdi.getTextNo("VacancesMin"); drawInfoOut.vacancyFactor = 0.01*_wtof(gdi.getText("Vacances").c_str()); drawInfoOut.extraFactor = 0.01*_wtof(gdi.getText("Extra").c_str()); drawInfoOut.baseInterval=convertAbsoluteTimeMS(gdi.getText("BaseInterval")); drawInfoOut.allowNeighbourSameCourse = gdi.isChecked("AllowNeighbours"); drawInfoOut.coursesTogether = gdi.isChecked("CoursesTogether"); drawInfoOut.minClassInterval = convertAbsoluteTimeMS(gdi.getText("MinInterval")); drawInfoOut.maxClassInterval = convertAbsoluteTimeMS(gdi.getText("MaxInterval")); drawInfoOut.nFields = gdi.getTextNo("nFields"); drawInfoOut.firstStart = oe->getRelativeTime(gdi.getText("FirstStart")); } void TabClass::writeDrawInfo(gdioutput &gdi, const DrawInfo &drawInfoIn) { gdi.selectItemByData("MaxCommonControl", drawInfoIn.maxCommonControl); gdi.setText("VacancesMax", drawInfoIn.maxVacancy); gdi.setText("VacancesMin", drawInfoIn.minVacancy); gdi.setText("Vacances", itow(int(drawInfoIn.vacancyFactor *100.0)) + L"%"); gdi.setText("Extra", itow(int(drawInfoIn.extraFactor * 100.0) ) + L"%"); gdi.setText("BaseInterval", formatTime(drawInfoIn.baseInterval)); gdi.check("AllowNeighbours", drawInfoIn.allowNeighbourSameCourse); gdi.check("CoursesTogether", drawInfoIn.coursesTogether); 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); }